summaryrefslogtreecommitdiffstats
path: root/src/VBox/HostDrivers
diff options
context:
space:
mode:
Diffstat (limited to 'src/VBox/HostDrivers')
-rw-r--r--src/VBox/HostDrivers/.scm-settings73
-rw-r--r--src/VBox/HostDrivers/Makefile.kmk80
-rw-r--r--src/VBox/HostDrivers/Support/Certificates/AppleRoot-2bd06947947609fef46b8d2e40a6f7474d7f085e.tafbin0 -> 422 bytes
-rw-r--r--src/VBox/HostDrivers/Support/Certificates/AppleRoot-G2-c499136c1803c27bc0a3a00d7f72807a1c77268d.tafbin0 -> 683 bytes
-rw-r--r--src/VBox/HostDrivers/Support/Certificates/Makefile.kup0
-rw-r--r--src/VBox/HostDrivers/Support/Certificates/NtRoot-CrossSign-DigiCertAssuredIDRootCA-589567a6c1944d68f11ff3d86576092b.tafbin0 -> 425 bytes
-rw-r--r--src/VBox/HostDrivers/Support/Certificates/NtRoot-CrossSign-DigiCertHighAssuranceEVRootCA-f4a38dbe86386c554d25f1ce2557a4fe.tafbin0 -> 432 bytes
-rw-r--r--src/VBox/HostDrivers/Support/Certificates/NtRoot-MicrosoftCodeVerificationRoot-729404101f3e0ca347837fca175a8438.tafbin0 -> 708 bytes
-rw-r--r--src/VBox/HostDrivers/Support/Certificates/SpcRoot-MicrosoftAuthenticodeTmRootAuthority-01.tafbin0 -> 400 bytes
-rw-r--r--src/VBox/HostDrivers/Support/Certificates/SpcRoot-MicrosoftDevelopmentRootCertificateAuthority2014-078f0a9d03df119e434e4fec1bf0235a.tafbin0 -> 730 bytes
-rw-r--r--src/VBox/HostDrivers/Support/Certificates/SpcRoot-MicrosoftDigitalMediaAuthority2005-6eff330eb6e7569740680870104baaba.tafbin0 -> 629 bytes
-rw-r--r--src/VBox/HostDrivers/Support/Certificates/SpcRoot-MicrosoftRootAuthority-00c1008b3c3c8811d13ef663ecdf40.tafbin0 -> 432 bytes
-rw-r--r--src/VBox/HostDrivers/Support/Certificates/SpcRoot-MicrosoftRootCertificateAuthority-79ad16a14aa0a5ad4c7358f407132e65.tafbin0 -> 675 bytes
-rw-r--r--src/VBox/HostDrivers/Support/Certificates/SpcRoot-MicrosoftRootCertificateAuthority2010-28cc3a25bfba44ac449a9b586b4339aa.tafbin0 -> 718 bytes
-rw-r--r--src/VBox/HostDrivers/Support/Certificates/SpcRoot-MicrosoftRootCertificateAuthority2011-3f8bc8b5fc9fb29643b569d66c42e144.tafbin0 -> 718 bytes
-rw-r--r--src/VBox/HostDrivers/Support/Certificates/SpcRoot-MicrosoftTestingRootCertificateAuthority2010-8a334aa8052dd244a647306a76b8178fa215f344.tafbin0 -> 726 bytes
-rw-r--r--src/VBox/HostDrivers/Support/Certificates/Timestamp-CopyrightC1997MicrosoftCorp-01.tafbin0 -> 332 bytes
-rw-r--r--src/VBox/HostDrivers/Support/Certificates/Timestamp-VBoxLegacyWinCA.crtbin0 -> 1593 bytes
-rw-r--r--src/VBox/HostDrivers/Support/Certificates/Timestamp-VBoxLegacyWinCA.tafbin0 -> 731 bytes
-rw-r--r--src/VBox/HostDrivers/Support/Certificates/Timestamp-VBoxLegacyWinSha1CA.crtbin0 -> 1603 bytes
-rw-r--r--src/VBox/HostDrivers/Support/Certificates/Timestamp-VBoxLegacyWinSha1CA.tafbin0 -> 736 bytes
-rw-r--r--src/VBox/HostDrivers/Support/Certificates/Trusted-OracleCorporationVirtualBox-05308b76ac2e15b29720fb4395f65f38.cerbin0 -> 1339 bytes
-rw-r--r--src/VBox/HostDrivers/Support/Certificates/Trusted-OracleCorporationVirtualBox-05308b76ac2e15b29720fb4395f65f38.tafbin0 -> 456 bytes
-rw-r--r--src/VBox/HostDrivers/Support/Makefile.kmk938
-rw-r--r--src/VBox/HostDrivers/Support/SUPDrv-dtrace.cpp1201
-rw-r--r--src/VBox/HostDrivers/Support/SUPDrv.cpp7204
-rw-r--r--src/VBox/HostDrivers/Support/SUPDrv.d51
-rw-r--r--src/VBox/HostDrivers/Support/SUPDrvGip.cpp5120
-rw-r--r--src/VBox/HostDrivers/Support/SUPDrvIDC.h288
-rw-r--r--src/VBox/HostDrivers/Support/SUPDrvIOC.h1725
-rw-r--r--src/VBox/HostDrivers/Support/SUPDrvInternal.h1296
-rw-r--r--src/VBox/HostDrivers/Support/SUPDrvSem.cpp451
-rw-r--r--src/VBox/HostDrivers/Support/SUPDrvTracer.cpp2494
-rw-r--r--src/VBox/HostDrivers/Support/SUPDrvTracerA.asm65
-rw-r--r--src/VBox/HostDrivers/Support/SUPLib.cpp2438
-rw-r--r--src/VBox/HostDrivers/Support/SUPLibAll.cpp429
-rw-r--r--src/VBox/HostDrivers/Support/SUPLibInternal.h524
-rw-r--r--src/VBox/HostDrivers/Support/SUPLibLdr.cpp1153
-rw-r--r--src/VBox/HostDrivers/Support/SUPLibSem.cpp366
-rw-r--r--src/VBox/HostDrivers/Support/SUPLibTracerA.asm232
-rw-r--r--src/VBox/HostDrivers/Support/SUPR0-asm-files.sed66
-rw-r--r--src/VBox/HostDrivers/Support/SUPR0-asm.sed62
-rw-r--r--src/VBox/HostDrivers/Support/SUPR0-def-lx.sed91
-rw-r--r--src/VBox/HostDrivers/Support/SUPR0-def-pe.sed92
-rw-r--r--src/VBox/HostDrivers/Support/SUPR0IdcClient.c222
-rw-r--r--src/VBox/HostDrivers/Support/SUPR0IdcClientComponent.c101
-rw-r--r--src/VBox/HostDrivers/Support/SUPR0IdcClientInternal.h80
-rw-r--r--src/VBox/HostDrivers/Support/SUPR0IdcClientStubs.c153
-rw-r--r--src/VBox/HostDrivers/Support/SUPR3HardenedIPRT.cpp152
-rw-r--r--src/VBox/HostDrivers/Support/SUPR3HardenedMain.cpp2687
-rw-r--r--src/VBox/HostDrivers/Support/SUPR3HardenedMainTemplate.cpp44
-rw-r--r--src/VBox/HostDrivers/Support/SUPR3HardenedMainTemplateTestcase.cpp44
-rw-r--r--src/VBox/HostDrivers/Support/SUPR3HardenedNoCrt.cpp189
-rw-r--r--src/VBox/HostDrivers/Support/SUPR3HardenedVerify.cpp2149
-rw-r--r--src/VBox/HostDrivers/Support/SUPSvc.cpp456
-rw-r--r--src/VBox/HostDrivers/Support/SUPSvcGlobal.cpp79
-rw-r--r--src/VBox/HostDrivers/Support/SUPSvcGrant.cpp1012
-rw-r--r--src/VBox/HostDrivers/Support/SUPSvcInternal.h104
-rw-r--r--src/VBox/HostDrivers/Support/bldSUPSignedDummy.cpp42
-rw-r--r--src/VBox/HostDrivers/Support/darwin/Info.plist46
-rw-r--r--src/VBox/HostDrivers/Support/darwin/Makefile.kup0
-rw-r--r--src/VBox/HostDrivers/Support/darwin/SUPDrv-darwin.cpp2410
-rw-r--r--src/VBox/HostDrivers/Support/darwin/SUPLib-darwin.cpp333
-rw-r--r--src/VBox/HostDrivers/Support/darwin/SUPR0IdcClient-darwin.c66
-rw-r--r--src/VBox/HostDrivers/Support/darwin/SUPR3HardenedEntitlements.plist37
-rw-r--r--src/VBox/HostDrivers/Support/darwin/SUPR3HardenedEntitlementsVM.plist35
-rw-r--r--src/VBox/HostDrivers/Support/darwin/SUPR3HardenedMain-darwin.cpp287
-rwxr-xr-xsrc/VBox/HostDrivers/Support/darwin/load.sh164
-rw-r--r--src/VBox/HostDrivers/Support/darwin/sys/dtrace_glue.h49
-rw-r--r--src/VBox/HostDrivers/Support/freebsd/Makefile207
-rw-r--r--src/VBox/HostDrivers/Support/freebsd/Makefile.kup0
-rw-r--r--src/VBox/HostDrivers/Support/freebsd/SUPDrv-freebsd.c679
-rw-r--r--src/VBox/HostDrivers/Support/freebsd/SUPDrv-freebsd.def2
-rw-r--r--src/VBox/HostDrivers/Support/freebsd/SUPLib-freebsd.cpp197
-rw-r--r--src/VBox/HostDrivers/Support/freebsd/SUPR0IdcClient-freebsd.c66
-rwxr-xr-xsrc/VBox/HostDrivers/Support/freebsd/files_vboxdrv240
-rw-r--r--src/VBox/HostDrivers/Support/linux/LnxPerfHack.cpp446
-rw-r--r--src/VBox/HostDrivers/Support/linux/Makefile208
-rw-r--r--src/VBox/HostDrivers/Support/linux/Makefile-vbox_vboxddr0.gmk43
-rw-r--r--src/VBox/HostDrivers/Support/linux/Makefile-vbox_vmmr0.gmk44
-rw-r--r--src/VBox/HostDrivers/Support/linux/Makefile-wrapper.gmk166
-rw-r--r--src/VBox/HostDrivers/Support/linux/Makefile.kup0
-rw-r--r--src/VBox/HostDrivers/Support/linux/SUPDrv-linux.c1796
-rw-r--r--src/VBox/HostDrivers/Support/linux/SUPDrv-linux.mod.c97
-rw-r--r--src/VBox/HostDrivers/Support/linux/SUPLib-linux.cpp388
-rw-r--r--src/VBox/HostDrivers/Support/linux/SUPR0IdcClient-linux.c66
-rw-r--r--src/VBox/HostDrivers/Support/linux/SUPWrapperMod-linux.c211
-rw-r--r--src/VBox/HostDrivers/Support/linux/VBoxR0-wrapped.lds49
-rw-r--r--src/VBox/HostDrivers/Support/linux/combined-agnostic1.c99
-rw-r--r--src/VBox/HostDrivers/Support/linux/combined-agnostic2.c116
-rw-r--r--src/VBox/HostDrivers/Support/linux/combined-os-specific.c68
-rwxr-xr-xsrc/VBox/HostDrivers/Support/linux/files_vboxdrv242
-rw-r--r--src/VBox/HostDrivers/Support/os2/Makefile.kup0
-rw-r--r--src/VBox/HostDrivers/Support/os2/SUPDrv-os2.cpp570
-rw-r--r--src/VBox/HostDrivers/Support/os2/SUPDrv-os2.def55
-rw-r--r--src/VBox/HostDrivers/Support/os2/SUPDrvA-os2.asm973
-rw-r--r--src/VBox/HostDrivers/Support/os2/SUPLib-os2.cpp204
-rw-r--r--src/VBox/HostDrivers/Support/os2/SUPR0IdcClient-os2.c61
-rw-r--r--src/VBox/HostDrivers/Support/posix/SUPR3HardenedMain-posix.cpp708
-rw-r--r--src/VBox/HostDrivers/Support/posix/SUPR3HardenedMainA-posix.asm170
-rw-r--r--src/VBox/HostDrivers/Support/solaris/Makefile.kup0
-rw-r--r--src/VBox/HostDrivers/Support/solaris/SUPDrv-solaris.c1347
-rw-r--r--src/VBox/HostDrivers/Support/solaris/SUPLib-solaris.cpp245
-rw-r--r--src/VBox/HostDrivers/Support/solaris/SUPR0IdcClient-solaris.c66
-rwxr-xr-xsrc/VBox/HostDrivers/Support/solaris/load.sh128
-rw-r--r--src/VBox/HostDrivers/Support/solaris/vboxdrv.conf43
-rw-r--r--src/VBox/HostDrivers/Support/testcase/Makefile.kmk158
-rw-r--r--src/VBox/HostDrivers/Support/testcase/SUPInstall.cpp69
-rw-r--r--src/VBox/HostDrivers/Support/testcase/SUPLoggerCtl.cpp196
-rw-r--r--src/VBox/HostDrivers/Support/testcase/SUPUninstall.cpp63
-rw-r--r--src/VBox/HostDrivers/Support/testcase/tstContiguous.cpp118
-rw-r--r--src/VBox/HostDrivers/Support/testcase/tstGIP-2.cpp356
-rw-r--r--src/VBox/HostDrivers/Support/testcase/tstGetPagingMode.cpp104
-rw-r--r--src/VBox/HostDrivers/Support/testcase/tstInit.cpp61
-rw-r--r--src/VBox/HostDrivers/Support/testcase/tstInt.cpp240
-rw-r--r--src/VBox/HostDrivers/Support/testcase/tstLow.cpp164
-rw-r--r--src/VBox/HostDrivers/Support/testcase/tstNtQueryStuff.cpp440
-rw-r--r--src/VBox/HostDrivers/Support/testcase/tstPage.cpp101
-rw-r--r--src/VBox/HostDrivers/Support/testcase/tstPin.cpp223
-rw-r--r--src/VBox/HostDrivers/Support/testcase/tstSupLoadModule.cpp128
-rw-r--r--src/VBox/HostDrivers/Support/testcase/tstSupSem-Zombie.cpp232
-rw-r--r--src/VBox/HostDrivers/Support/testcase/tstSupSem.cpp649
-rw-r--r--src/VBox/HostDrivers/Support/testcase/tstSupTscDelta.cpp235
-rw-r--r--src/VBox/HostDrivers/Support/testcase/tstSupVerify.cpp162
-rw-r--r--src/VBox/HostDrivers/Support/win/Makefile.kup0
-rw-r--r--src/VBox/HostDrivers/Support/win/SUPDrv-win.cpp5719
-rw-r--r--src/VBox/HostDrivers/Support/win/SUPDrvA-win.asm119
-rw-r--r--src/VBox/HostDrivers/Support/win/SUPHardenedVerify-win.h303
-rw-r--r--src/VBox/HostDrivers/Support/win/SUPHardenedVerifyImage-win.cpp3090
-rw-r--r--src/VBox/HostDrivers/Support/win/SUPHardenedVerifyProcess-win.cpp2681
-rw-r--r--src/VBox/HostDrivers/Support/win/SUPLib-win.cpp1002
-rw-r--r--src/VBox/HostDrivers/Support/win/SUPR0IdcClient-win.c167
-rw-r--r--src/VBox/HostDrivers/Support/win/SUPR3HardenedMain-win.cpp7031
-rw-r--r--src/VBox/HostDrivers/Support/win/SUPR3HardenedMainA-win.asm404
-rw-r--r--src/VBox/HostDrivers/Support/win/SUPR3HardenedMainImports-win.cpp873
-rw-r--r--src/VBox/HostDrivers/Support/win/SUPR3HardenedNoCrt-win.cpp501
-rw-r--r--src/VBox/HostDrivers/Support/win/SUPSvc-win.cpp908
-rw-r--r--src/VBox/HostDrivers/Support/win/VBoxDrv.rc71
-rw-r--r--src/VBox/HostDrivers/Support/win/VBoxSup.inf100
-rw-r--r--src/VBox/HostDrivers/Support/win/VBoxSupLib-win.cpp108
-rw-r--r--src/VBox/HostDrivers/Support/win/VBoxSupLib.rc70
-rw-r--r--src/VBox/HostDrivers/Support/win/import-template-kernel32.h21
-rw-r--r--src/VBox/HostDrivers/Support/win/import-template-ntdll.h99
-rw-r--r--src/VBox/HostDrivers/VBoxNetAdp/Makefile.kmk228
-rw-r--r--src/VBox/HostDrivers/VBoxNetAdp/VBoxNetAdp.c238
-rw-r--r--src/VBox/HostDrivers/VBoxNetAdp/VBoxNetAdpInternal.h202
-rw-r--r--src/VBox/HostDrivers/VBoxNetAdp/darwin/Info.plist26
-rw-r--r--src/VBox/HostDrivers/VBoxNetAdp/darwin/Makefile.kup0
-rw-r--r--src/VBox/HostDrivers/VBoxNetAdp/darwin/VBoxNetAdp-darwin.cpp529
-rwxr-xr-xsrc/VBox/HostDrivers/VBoxNetAdp/darwin/loadnetadp.sh133
-rw-r--r--src/VBox/HostDrivers/VBoxNetAdp/freebsd/Makefile54
-rw-r--r--src/VBox/HostDrivers/VBoxNetAdp/freebsd/VBoxNetAdp-freebsd.c339
-rwxr-xr-xsrc/VBox/HostDrivers/VBoxNetAdp/freebsd/files_vboxnetadp94
-rw-r--r--src/VBox/HostDrivers/VBoxNetAdp/linux/Makefile78
-rw-r--r--src/VBox/HostDrivers/VBoxNetAdp/linux/Makefile.kup0
-rw-r--r--src/VBox/HostDrivers/VBoxNetAdp/linux/VBoxNetAdp-linux.c583
-rwxr-xr-xsrc/VBox/HostDrivers/VBoxNetAdp/linux/files_vboxnetadp112
-rw-r--r--src/VBox/HostDrivers/VBoxNetAdp/solaris/VBoxNetAdp-solaris.c581
-rw-r--r--src/VBox/HostDrivers/VBoxNetAdp/solaris/hostname.vboxnet01
-rw-r--r--src/VBox/HostDrivers/VBoxNetAdp/solaris/vboxnet.conf43
-rw-r--r--src/VBox/HostDrivers/VBoxNetAdp/win/Makefile.kup0
-rw-r--r--src/VBox/HostDrivers/VBoxNetAdp/win/VBoxNetAdp-win.cpp1888
-rw-r--r--src/VBox/HostDrivers/VBoxNetAdp/win/VBoxNetAdp-win.h71
-rw-r--r--src/VBox/HostDrivers/VBoxNetAdp/win/VBoxNetAdp-win.rc72
-rw-r--r--src/VBox/HostDrivers/VBoxNetAdp/win/VBoxNetAdp6.inf101
-rw-r--r--src/VBox/HostDrivers/VBoxNetFlt/Makefile.kmk556
-rw-r--r--src/VBox/HostDrivers/VBoxNetFlt/VBoxNetFlt.c1591
-rw-r--r--src/VBox/HostDrivers/VBoxNetFlt/VBoxNetFlt.rc69
-rw-r--r--src/VBox/HostDrivers/VBoxNetFlt/VBoxNetFltInternal.h499
-rw-r--r--src/VBox/HostDrivers/VBoxNetFlt/darwin/Info.plist38
-rw-r--r--src/VBox/HostDrivers/VBoxNetFlt/darwin/Makefile.kup0
-rw-r--r--src/VBox/HostDrivers/VBoxNetFlt/darwin/VBoxNetFlt-darwin.cpp1754
-rwxr-xr-xsrc/VBox/HostDrivers/VBoxNetFlt/darwin/loadnetflt.sh133
-rw-r--r--src/VBox/HostDrivers/VBoxNetFlt/freebsd/Makefile57
-rw-r--r--src/VBox/HostDrivers/VBoxNetFlt/freebsd/VBoxNetFlt-freebsd.c817
-rwxr-xr-xsrc/VBox/HostDrivers/VBoxNetFlt/freebsd/files_vboxnetflt99
-rw-r--r--src/VBox/HostDrivers/VBoxNetFlt/linux/Makefile81
-rw-r--r--src/VBox/HostDrivers/VBoxNetFlt/linux/Makefile.kup0
-rw-r--r--src/VBox/HostDrivers/VBoxNetFlt/linux/VBoxNetFlt-linux.c2609
-rwxr-xr-xsrc/VBox/HostDrivers/VBoxNetFlt/linux/files_vboxnetflt113
-rw-r--r--src/VBox/HostDrivers/VBoxNetFlt/solaris/VBoxNetFlt-solaris.c4042
-rw-r--r--src/VBox/HostDrivers/VBoxNetFlt/solaris/VBoxNetFltBow-solaris.c1702
-rw-r--r--src/VBox/HostDrivers/VBoxNetFlt/solaris/vboxbow.conf43
-rw-r--r--src/VBox/HostDrivers/VBoxNetFlt/solaris/vboxflt.conf56
-rw-r--r--src/VBox/HostDrivers/VBoxNetFlt/win/Makefile.kup0
-rw-r--r--src/VBox/HostDrivers/VBoxNetFlt/win/cfg/Makefile.kup0
-rw-r--r--src/VBox/HostDrivers/VBoxNetFlt/win/cfg/VBoxNetCfg.cpp3770
-rw-r--r--src/VBox/HostDrivers/VBoxNetFlt/win/drv/Makefile.kup0
-rw-r--r--src/VBox/HostDrivers/VBoxNetFlt/win/drv/VBoxNetAdp.inf95
-rw-r--r--src/VBox/HostDrivers/VBoxNetFlt/win/drv/VBoxNetFlt-win.rc77
-rw-r--r--src/VBox/HostDrivers/VBoxNetFlt/win/drv/VBoxNetFlt.inf117
-rw-r--r--src/VBox/HostDrivers/VBoxNetFlt/win/drv/VBoxNetFltCmn-win.h533
-rw-r--r--src/VBox/HostDrivers/VBoxNetFlt/win/drv/VBoxNetFltM-win.cpp1570
-rw-r--r--src/VBox/HostDrivers/VBoxNetFlt/win/drv/VBoxNetFltM-win.h67
-rw-r--r--src/VBox/HostDrivers/VBoxNetFlt/win/drv/VBoxNetFltM.inf82
-rw-r--r--src/VBox/HostDrivers/VBoxNetFlt/win/drv/VBoxNetFltP-win.cpp1595
-rw-r--r--src/VBox/HostDrivers/VBoxNetFlt/win/drv/VBoxNetFltP-win.h52
-rw-r--r--src/VBox/HostDrivers/VBoxNetFlt/win/drv/VBoxNetFltRt-win.cpp3650
-rw-r--r--src/VBox/HostDrivers/VBoxNetFlt/win/drv/VBoxNetFltRt-win.h972
-rw-r--r--src/VBox/HostDrivers/VBoxNetFlt/win/drv/VBoxNetLwf-win.cpp2736
-rw-r--r--src/VBox/HostDrivers/VBoxNetFlt/win/drv/VBoxNetLwf-win.h55
-rw-r--r--src/VBox/HostDrivers/VBoxNetFlt/win/drv/VBoxNetLwf.inf120
-rw-r--r--src/VBox/HostDrivers/VBoxNetFlt/win/nobj/Makefile.kup0
-rw-r--r--src/VBox/HostDrivers/VBoxNetFlt/win/nobj/VBoxNetFltNobj.cpp747
-rw-r--r--src/VBox/HostDrivers/VBoxNetFlt/win/nobj/VBoxNetFltNobj.def42
-rw-r--r--src/VBox/HostDrivers/VBoxNetFlt/win/nobj/VBoxNetFltNobj.h96
-rw-r--r--src/VBox/HostDrivers/VBoxNetFlt/win/nobj/VBoxNetFltNobj.rc78
-rw-r--r--src/VBox/HostDrivers/VBoxNetFlt/win/nobj/VBoxNetFltNobj.rgs13
-rw-r--r--src/VBox/HostDrivers/VBoxNetFlt/win/nobj/VBoxNetFltNobjRc.h46
-rw-r--r--src/VBox/HostDrivers/VBoxNetFlt/win/nobj/VBoxNetFltNobjT.idl55
-rw-r--r--src/VBox/HostDrivers/VBoxNetFlt/win/tools/Makefile.kup0
-rw-r--r--src/VBox/HostDrivers/VBoxNetFlt/win/tools/VBoxNetAdpInstall.cpp341
-rw-r--r--src/VBox/HostDrivers/VBoxNetFlt/win/tools/VBoxNetAdpUninstall.cpp107
-rw-r--r--src/VBox/HostDrivers/VBoxNetFlt/win/tools/VBoxNetFltInstall.cpp201
-rw-r--r--src/VBox/HostDrivers/VBoxNetFlt/win/tools/VBoxNetFltUninstall.cpp133
-rw-r--r--src/VBox/HostDrivers/VBoxNetFlt/win/tools/VBoxNetLwfInstall.cpp186
-rw-r--r--src/VBox/HostDrivers/VBoxNetFlt/win/tools/VBoxNetLwfUninstall.cpp130
-rw-r--r--src/VBox/HostDrivers/VBoxPci/Makefile.kmk93
-rw-r--r--src/VBox/HostDrivers/VBoxPci/VBoxPci.c800
-rw-r--r--src/VBox/HostDrivers/VBoxPci/VBoxPciInternal.h213
-rw-r--r--src/VBox/HostDrivers/VBoxPci/linux/Makefile81
-rw-r--r--src/VBox/HostDrivers/VBoxPci/linux/Makefile.kup0
-rw-r--r--src/VBox/HostDrivers/VBoxPci/linux/VBoxPci-linux.c1186
-rwxr-xr-xsrc/VBox/HostDrivers/VBoxPci/linux/files_vboxpci111
-rw-r--r--src/VBox/HostDrivers/VBoxUSB/Makefile.kmk85
-rw-r--r--src/VBox/HostDrivers/VBoxUSB/USBFilter.cpp1860
-rw-r--r--src/VBox/HostDrivers/VBoxUSB/USBLib.cpp71
-rw-r--r--src/VBox/HostDrivers/VBoxUSB/VBoxUSBFilterMgr.cpp571
-rw-r--r--src/VBox/HostDrivers/VBoxUSB/VBoxUSBFilterMgr.h68
-rw-r--r--src/VBox/HostDrivers/VBoxUSB/os2/Makefile.kup0
-rw-r--r--src/VBox/HostDrivers/VBoxUSB/os2/usbcalls.c1278
-rw-r--r--src/VBox/HostDrivers/VBoxUSB/os2/usbcalls.h242
-rw-r--r--src/VBox/HostDrivers/VBoxUSB/solaris/Makefile.kmk67
-rw-r--r--src/VBox/HostDrivers/VBoxUSB/solaris/USBLib-solaris.cpp279
-rw-r--r--src/VBox/HostDrivers/VBoxUSB/solaris/VBoxUSB-solaris.c4054
-rw-r--r--src/VBox/HostDrivers/VBoxUSB/solaris/VBoxUSBMon-solaris.c1064
-rw-r--r--src/VBox/HostDrivers/VBoxUSB/solaris/include/usbai_private.h161
-rw-r--r--src/VBox/HostDrivers/VBoxUSB/solaris/vboxusb.conf37
-rw-r--r--src/VBox/HostDrivers/VBoxUSB/solaris/vboxusbmon.conf37
-rw-r--r--src/VBox/HostDrivers/VBoxUSB/testcase/Makefile.kup0
-rw-r--r--src/VBox/HostDrivers/VBoxUSB/testcase/tstUSBFilter.cpp391
-rw-r--r--src/VBox/HostDrivers/VBoxUSB/win/Install/Makefile.kup0
-rw-r--r--src/VBox/HostDrivers/VBoxUSB/win/Install/USBInstall.cpp256
-rw-r--r--src/VBox/HostDrivers/VBoxUSB/win/Install/USBUninstall.cpp215
-rw-r--r--src/VBox/HostDrivers/VBoxUSB/win/Makefile.kmk204
-rw-r--r--src/VBox/HostDrivers/VBoxUSB/win/cmn/Makefile.kup0
-rw-r--r--src/VBox/HostDrivers/VBoxUSB/win/cmn/VBoxDrvTool.cpp247
-rw-r--r--src/VBox/HostDrivers/VBoxUSB/win/cmn/VBoxDrvTool.h113
-rw-r--r--src/VBox/HostDrivers/VBoxUSB/win/cmn/VBoxUsbIdc.h95
-rw-r--r--src/VBox/HostDrivers/VBoxUSB/win/cmn/VBoxUsbTool.cpp428
-rw-r--r--src/VBox/HostDrivers/VBoxUSB/win/cmn/VBoxUsbTool.h89
-rw-r--r--src/VBox/HostDrivers/VBoxUSB/win/dev/Makefile.kup0
-rw-r--r--src/VBox/HostDrivers/VBoxUSB/win/dev/VBoxUSB.inf88
-rw-r--r--src/VBox/HostDrivers/VBoxUSB/win/dev/VBoxUsbCmn.h103
-rw-r--r--src/VBox/HostDrivers/VBoxUSB/win/dev/VBoxUsbDev.cpp357
-rw-r--r--src/VBox/HostDrivers/VBoxUSB/win/dev/VBoxUsbDev.h218
-rw-r--r--src/VBox/HostDrivers/VBoxUSB/win/dev/VBoxUsbDev.rc70
-rw-r--r--src/VBox/HostDrivers/VBoxUSB/win/dev/VBoxUsbPnP.cpp274
-rw-r--r--src/VBox/HostDrivers/VBoxUSB/win/dev/VBoxUsbPnP.h46
-rw-r--r--src/VBox/HostDrivers/VBoxUSB/win/dev/VBoxUsbPwr.cpp427
-rw-r--r--src/VBox/HostDrivers/VBoxUSB/win/dev/VBoxUsbPwr.h51
-rw-r--r--src/VBox/HostDrivers/VBoxUSB/win/dev/VBoxUsbRt.cpp1593
-rw-r--r--src/VBox/HostDrivers/VBoxUSB/win/dev/VBoxUsbRt.h97
-rw-r--r--src/VBox/HostDrivers/VBoxUSB/win/lib/Makefile.kup0
-rw-r--r--src/VBox/HostDrivers/VBoxUSB/win/lib/VBoxUsbLib-win.cpp2221
-rw-r--r--src/VBox/HostDrivers/VBoxUSB/win/mon/Makefile.kup0
-rw-r--r--src/VBox/HostDrivers/VBoxUSB/win/mon/VBoxUSBMon.inf98
-rw-r--r--src/VBox/HostDrivers/VBoxUSB/win/mon/VBoxUsbFlt.cpp1763
-rw-r--r--src/VBox/HostDrivers/VBoxUSB/win/mon/VBoxUsbFlt.h75
-rw-r--r--src/VBox/HostDrivers/VBoxUSB/win/mon/VBoxUsbHook.cpp218
-rw-r--r--src/VBox/HostDrivers/VBoxUSB/win/mon/VBoxUsbHook.h96
-rw-r--r--src/VBox/HostDrivers/VBoxUSB/win/mon/VBoxUsbMon.cpp1568
-rw-r--r--src/VBox/HostDrivers/VBoxUSB/win/mon/VBoxUsbMon.h77
-rw-r--r--src/VBox/HostDrivers/VBoxUSB/win/mon/VBoxUsbMon.rc70
-rw-r--r--src/VBox/HostDrivers/VBoxUSB/win/testcase/Makefile.kup0
-rw-r--r--src/VBox/HostDrivers/VBoxUSB/win/testcase/USBTest.cpp384
-rw-r--r--src/VBox/HostDrivers/VBoxUSB/win/usbd/Makefile.kup0
-rw-r--r--src/VBox/HostDrivers/VBoxUSB/win/usbd/usbd.def74
-rw-r--r--src/VBox/HostDrivers/adpctl/Makefile.kmk44
-rw-r--r--src/VBox/HostDrivers/adpctl/VBoxNetAdpCtl.cpp1178
-rw-r--r--src/VBox/HostDrivers/darwin/Makefile.kmk49
-rw-r--r--src/VBox/HostDrivers/darwin/VBoxNetSend.h132
-rwxr-xr-xsrc/VBox/HostDrivers/darwin/loadall.sh48
-rw-r--r--src/VBox/HostDrivers/freebsd/Makefile94
-rw-r--r--src/VBox/HostDrivers/freebsd/Makefile.kmk45
-rw-r--r--src/VBox/HostDrivers/linux/Makefile248
-rw-r--r--src/VBox/HostDrivers/linux/Makefile.kmk50
-rwxr-xr-xsrc/VBox/HostDrivers/linux/build_in_tmp104
-rwxr-xr-xsrc/VBox/HostDrivers/linux/export_modules.sh258
-rwxr-xr-xsrc/VBox/HostDrivers/linux/load.sh65
-rwxr-xr-xsrc/VBox/HostDrivers/linux/loadall.sh85
-rw-r--r--src/VBox/HostDrivers/win/Makefile.kmk66
-rw-r--r--src/VBox/HostDrivers/win/VBoxDbgLog.h150
-rw-r--r--src/VBox/HostDrivers/win/cfg/Makefile.kup0
-rw-r--r--src/VBox/HostDrivers/win/cfg/VBoxDrvCfg.cpp1006
-rw-r--r--src/VBox/HostDrivers/win/load.cmd117
-rw-r--r--src/VBox/HostDrivers/win/loadall.cmd142
297 files changed, 139889 insertions, 0 deletions
diff --git a/src/VBox/HostDrivers/.scm-settings b/src/VBox/HostDrivers/.scm-settings
new file mode 100644
index 00000000..34c50941
--- /dev/null
+++ b/src/VBox/HostDrivers/.scm-settings
@@ -0,0 +1,73 @@
+# $Id: .scm-settings $
+## @file
+# Source code massager settings for the host installers.
+#
+
+#
+# Copyright (C) 2010-2023 Oracle and/or its affiliates.
+#
+# This file is part of VirtualBox base platform packages, as
+# available from https://www.virtualbox.org.
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation, in version 3 of the
+# License.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, see <https://www.gnu.org/licenses>.
+#
+# The contents of this file may alternatively be used under the terms
+# of the Common Development and Distribution License Version 1.0
+# (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+# in the VirtualBox distribution, in which case the provisions of the
+# CDDL are applicable instead of those of the GPL.
+#
+# You may elect to license modified versions of this file under the
+# terms and conditions of either the GPL or the CDDL or both.
+#
+# SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+#
+
+# Driver code is dual licensed.
+--license-ose-dual
+
+/adpctl/*: --license-ose-gpl
+
+# External stuff.
+/Support/freebsd/SUPDrv-freebsd.c: --external-copyright
+/Support/freebsd/SUPDrv-freebsd.def: --no-update-copyright-year --no-update-license
+/Support/os2/SUPDrv-os2.cpp: --external-copyright
+/Support/os2/SUPDrvA-os2.asm: --external-copyright
+/VBoxNetAdp/freebsd/VBoxNetAdp-freebsd.c: --external-copyright
+/VBoxNetFlt/freebsd/VBoxNetFlt-freebsd.c: --external-copyright
+--filter-out-files /VBoxUSB/solaris/include/usbai_private.h
+
+# No license header (public domain).
+/VBoxUSB/os2/usbcalls.c: --external-copyright
+/VBoxUSB/os2/usbcalls.h: --external-copyright --no-convert-tabs
+
+# Trivial dual C and assembly include files which cannot have comments.
+/Support/win/import-template-kernel32.h: --no-update-copyright-year --no-update-license --no-fix-header-guards
+/Support/win/import-template-ntdll.h: --no-update-copyright-year --no-update-license --no-fix-header-guards
+
+# Ignore binary files
+--filter-out-files /Support/Certificates/*.taf
+--filter-out-files /Support/Certificates/*.cer
+
+# Misc
+/linux/build_in_tmp: --treat-as .sh
+/*/solaris/*.conf: --treat-as .sh
+--filter-out-files /VBoxNetAdp/solaris/hostname.vboxnet0
+--filter-out-files /VBoxNetFlt/win/nobj/VBoxNetFltNobj.rgs
+/VBoxNetFlt/win/nobj/VBoxNetFltNobjT.idl: --treat-as .h --no-fix-header-guards
+/Support/solaris/mod.sh: --no-convert-tabs
+/Support/linux/SUPDrv-linux.mod.c: --no-convert-tabs
+
+/*.h: --guard-relative-to-dir .
+
diff --git a/src/VBox/HostDrivers/Makefile.kmk b/src/VBox/HostDrivers/Makefile.kmk
new file mode 100644
index 00000000..df62eb0d
--- /dev/null
+++ b/src/VBox/HostDrivers/Makefile.kmk
@@ -0,0 +1,80 @@
+# $Id: Makefile.kmk $
+## @file
+# Top-level makefile for the VBox Host drivers.
+#
+
+#
+# Copyright (C) 2006-2023 Oracle and/or its affiliates.
+#
+# This file is part of VirtualBox base platform packages, as
+# available from https://www.virtualbox.org.
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation, in version 3 of the
+# License.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, see <https://www.gnu.org/licenses>.
+#
+# The contents of this file may alternatively be used under the terms
+# of the Common Development and Distribution License Version 1.0
+# (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+# in the VirtualBox distribution, in which case the provisions of the
+# CDDL are applicable instead of those of the GPL.
+#
+# You may elect to license modified versions of this file under the
+# terms and conditions of either the GPL or the CDDL or both.
+#
+# SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+#
+
+SUB_DEPTH = ../../..
+include $(KBUILD_PATH)/subheader.kmk
+
+# Include sub-makefiles.
+include $(PATH_SUB_CURRENT)/Support/Makefile.kmk
+
+if !defined(VBOX_ONLY_DOCS) \
+ && !defined(VBOX_ONLY_EXTPACKS) \
+ && !defined(VBOX_ONLY_VALIDATIONKIT) \
+ && "$(intersects $(KBUILD_TARGET_ARCH),$(VBOX_SUPPORTED_HOST_ARCHS))" != ""
+ if1of ($(KBUILD_TARGET), win)
+ include $(PATH_SUB_CURRENT)/win/Makefile.kmk
+ endif
+ ifdef VBOX_WITH_USB
+ include $(PATH_SUB_CURRENT)/VBoxUSB/Makefile.kmk
+ endif
+ if1of ($(KBUILD_TARGET), darwin solaris win linux freebsd)
+ ifdef VBOX_WITH_NETFLT
+ include $(PATH_SUB_CURRENT)/VBoxNetFlt/Makefile.kmk
+ endif
+ endif
+ if1of ($(KBUILD_TARGET), darwin solaris win linux freebsd)
+ ifdef VBOX_WITH_NETADP
+ include $(PATH_SUB_CURRENT)/VBoxNetAdp/Makefile.kmk
+ endif
+ endif
+ if1of ($(KBUILD_TARGET), darwin freebsd linux solaris)
+ include $(PATH_SUB_CURRENT)/adpctl/Makefile.kmk
+ endif
+ if1of ($(KBUILD_TARGET), darwin freebsd linux)
+ include $(PATH_SUB_CURRENT)/$(KBUILD_TARGET)/Makefile.kmk
+ endif
+
+ if1of ($(KBUILD_TARGET), linux)
+ ifdef VBOX_WITH_PCI_PASSTHROUGH
+ include $(PATH_SUB_CURRENT)/VBoxPci/Makefile.kmk
+ endif
+ endif
+
+endif # !defined(VBOX_ONLY_DOCS) && !defined(VBOX_ONLY_VALIDATIONKIT)
+
+# Let kBuild generate the rules.
+include $(FILE_KBUILD_SUB_FOOTER)
+
diff --git a/src/VBox/HostDrivers/Support/Certificates/AppleRoot-2bd06947947609fef46b8d2e40a6f7474d7f085e.taf b/src/VBox/HostDrivers/Support/Certificates/AppleRoot-2bd06947947609fef46b8d2e40a6f7474d7f085e.taf
new file mode 100644
index 00000000..82a8d623
--- /dev/null
+++ b/src/VBox/HostDrivers/Support/Certificates/AppleRoot-2bd06947947609fef46b8d2e40a6f7474d7f085e.taf
Binary files differ
diff --git a/src/VBox/HostDrivers/Support/Certificates/AppleRoot-G2-c499136c1803c27bc0a3a00d7f72807a1c77268d.taf b/src/VBox/HostDrivers/Support/Certificates/AppleRoot-G2-c499136c1803c27bc0a3a00d7f72807a1c77268d.taf
new file mode 100644
index 00000000..71aaf14d
--- /dev/null
+++ b/src/VBox/HostDrivers/Support/Certificates/AppleRoot-G2-c499136c1803c27bc0a3a00d7f72807a1c77268d.taf
Binary files differ
diff --git a/src/VBox/HostDrivers/Support/Certificates/Makefile.kup b/src/VBox/HostDrivers/Support/Certificates/Makefile.kup
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/src/VBox/HostDrivers/Support/Certificates/Makefile.kup
diff --git a/src/VBox/HostDrivers/Support/Certificates/NtRoot-CrossSign-DigiCertAssuredIDRootCA-589567a6c1944d68f11ff3d86576092b.taf b/src/VBox/HostDrivers/Support/Certificates/NtRoot-CrossSign-DigiCertAssuredIDRootCA-589567a6c1944d68f11ff3d86576092b.taf
new file mode 100644
index 00000000..d8754c9a
--- /dev/null
+++ b/src/VBox/HostDrivers/Support/Certificates/NtRoot-CrossSign-DigiCertAssuredIDRootCA-589567a6c1944d68f11ff3d86576092b.taf
Binary files differ
diff --git a/src/VBox/HostDrivers/Support/Certificates/NtRoot-CrossSign-DigiCertHighAssuranceEVRootCA-f4a38dbe86386c554d25f1ce2557a4fe.taf b/src/VBox/HostDrivers/Support/Certificates/NtRoot-CrossSign-DigiCertHighAssuranceEVRootCA-f4a38dbe86386c554d25f1ce2557a4fe.taf
new file mode 100644
index 00000000..c1d18a26
--- /dev/null
+++ b/src/VBox/HostDrivers/Support/Certificates/NtRoot-CrossSign-DigiCertHighAssuranceEVRootCA-f4a38dbe86386c554d25f1ce2557a4fe.taf
Binary files differ
diff --git a/src/VBox/HostDrivers/Support/Certificates/NtRoot-MicrosoftCodeVerificationRoot-729404101f3e0ca347837fca175a8438.taf b/src/VBox/HostDrivers/Support/Certificates/NtRoot-MicrosoftCodeVerificationRoot-729404101f3e0ca347837fca175a8438.taf
new file mode 100644
index 00000000..486b6fd7
--- /dev/null
+++ b/src/VBox/HostDrivers/Support/Certificates/NtRoot-MicrosoftCodeVerificationRoot-729404101f3e0ca347837fca175a8438.taf
Binary files differ
diff --git a/src/VBox/HostDrivers/Support/Certificates/SpcRoot-MicrosoftAuthenticodeTmRootAuthority-01.taf b/src/VBox/HostDrivers/Support/Certificates/SpcRoot-MicrosoftAuthenticodeTmRootAuthority-01.taf
new file mode 100644
index 00000000..7b46d69b
--- /dev/null
+++ b/src/VBox/HostDrivers/Support/Certificates/SpcRoot-MicrosoftAuthenticodeTmRootAuthority-01.taf
Binary files differ
diff --git a/src/VBox/HostDrivers/Support/Certificates/SpcRoot-MicrosoftDevelopmentRootCertificateAuthority2014-078f0a9d03df119e434e4fec1bf0235a.taf b/src/VBox/HostDrivers/Support/Certificates/SpcRoot-MicrosoftDevelopmentRootCertificateAuthority2014-078f0a9d03df119e434e4fec1bf0235a.taf
new file mode 100644
index 00000000..f2fa96a3
--- /dev/null
+++ b/src/VBox/HostDrivers/Support/Certificates/SpcRoot-MicrosoftDevelopmentRootCertificateAuthority2014-078f0a9d03df119e434e4fec1bf0235a.taf
Binary files differ
diff --git a/src/VBox/HostDrivers/Support/Certificates/SpcRoot-MicrosoftDigitalMediaAuthority2005-6eff330eb6e7569740680870104baaba.taf b/src/VBox/HostDrivers/Support/Certificates/SpcRoot-MicrosoftDigitalMediaAuthority2005-6eff330eb6e7569740680870104baaba.taf
new file mode 100644
index 00000000..e55039ab
--- /dev/null
+++ b/src/VBox/HostDrivers/Support/Certificates/SpcRoot-MicrosoftDigitalMediaAuthority2005-6eff330eb6e7569740680870104baaba.taf
Binary files differ
diff --git a/src/VBox/HostDrivers/Support/Certificates/SpcRoot-MicrosoftRootAuthority-00c1008b3c3c8811d13ef663ecdf40.taf b/src/VBox/HostDrivers/Support/Certificates/SpcRoot-MicrosoftRootAuthority-00c1008b3c3c8811d13ef663ecdf40.taf
new file mode 100644
index 00000000..46c6250a
--- /dev/null
+++ b/src/VBox/HostDrivers/Support/Certificates/SpcRoot-MicrosoftRootAuthority-00c1008b3c3c8811d13ef663ecdf40.taf
Binary files differ
diff --git a/src/VBox/HostDrivers/Support/Certificates/SpcRoot-MicrosoftRootCertificateAuthority-79ad16a14aa0a5ad4c7358f407132e65.taf b/src/VBox/HostDrivers/Support/Certificates/SpcRoot-MicrosoftRootCertificateAuthority-79ad16a14aa0a5ad4c7358f407132e65.taf
new file mode 100644
index 00000000..3131fc27
--- /dev/null
+++ b/src/VBox/HostDrivers/Support/Certificates/SpcRoot-MicrosoftRootCertificateAuthority-79ad16a14aa0a5ad4c7358f407132e65.taf
Binary files differ
diff --git a/src/VBox/HostDrivers/Support/Certificates/SpcRoot-MicrosoftRootCertificateAuthority2010-28cc3a25bfba44ac449a9b586b4339aa.taf b/src/VBox/HostDrivers/Support/Certificates/SpcRoot-MicrosoftRootCertificateAuthority2010-28cc3a25bfba44ac449a9b586b4339aa.taf
new file mode 100644
index 00000000..945de02c
--- /dev/null
+++ b/src/VBox/HostDrivers/Support/Certificates/SpcRoot-MicrosoftRootCertificateAuthority2010-28cc3a25bfba44ac449a9b586b4339aa.taf
Binary files differ
diff --git a/src/VBox/HostDrivers/Support/Certificates/SpcRoot-MicrosoftRootCertificateAuthority2011-3f8bc8b5fc9fb29643b569d66c42e144.taf b/src/VBox/HostDrivers/Support/Certificates/SpcRoot-MicrosoftRootCertificateAuthority2011-3f8bc8b5fc9fb29643b569d66c42e144.taf
new file mode 100644
index 00000000..610b14f1
--- /dev/null
+++ b/src/VBox/HostDrivers/Support/Certificates/SpcRoot-MicrosoftRootCertificateAuthority2011-3f8bc8b5fc9fb29643b569d66c42e144.taf
Binary files differ
diff --git a/src/VBox/HostDrivers/Support/Certificates/SpcRoot-MicrosoftTestingRootCertificateAuthority2010-8a334aa8052dd244a647306a76b8178fa215f344.taf b/src/VBox/HostDrivers/Support/Certificates/SpcRoot-MicrosoftTestingRootCertificateAuthority2010-8a334aa8052dd244a647306a76b8178fa215f344.taf
new file mode 100644
index 00000000..0c1a1af7
--- /dev/null
+++ b/src/VBox/HostDrivers/Support/Certificates/SpcRoot-MicrosoftTestingRootCertificateAuthority2010-8a334aa8052dd244a647306a76b8178fa215f344.taf
Binary files differ
diff --git a/src/VBox/HostDrivers/Support/Certificates/Timestamp-CopyrightC1997MicrosoftCorp-01.taf b/src/VBox/HostDrivers/Support/Certificates/Timestamp-CopyrightC1997MicrosoftCorp-01.taf
new file mode 100644
index 00000000..b9d8f7b8
--- /dev/null
+++ b/src/VBox/HostDrivers/Support/Certificates/Timestamp-CopyrightC1997MicrosoftCorp-01.taf
Binary files differ
diff --git a/src/VBox/HostDrivers/Support/Certificates/Timestamp-VBoxLegacyWinCA.crt b/src/VBox/HostDrivers/Support/Certificates/Timestamp-VBoxLegacyWinCA.crt
new file mode 100644
index 00000000..bb6c0bcf
--- /dev/null
+++ b/src/VBox/HostDrivers/Support/Certificates/Timestamp-VBoxLegacyWinCA.crt
Binary files differ
diff --git a/src/VBox/HostDrivers/Support/Certificates/Timestamp-VBoxLegacyWinCA.taf b/src/VBox/HostDrivers/Support/Certificates/Timestamp-VBoxLegacyWinCA.taf
new file mode 100644
index 00000000..f7d4e6ba
--- /dev/null
+++ b/src/VBox/HostDrivers/Support/Certificates/Timestamp-VBoxLegacyWinCA.taf
Binary files differ
diff --git a/src/VBox/HostDrivers/Support/Certificates/Timestamp-VBoxLegacyWinSha1CA.crt b/src/VBox/HostDrivers/Support/Certificates/Timestamp-VBoxLegacyWinSha1CA.crt
new file mode 100644
index 00000000..57d117d1
--- /dev/null
+++ b/src/VBox/HostDrivers/Support/Certificates/Timestamp-VBoxLegacyWinSha1CA.crt
Binary files differ
diff --git a/src/VBox/HostDrivers/Support/Certificates/Timestamp-VBoxLegacyWinSha1CA.taf b/src/VBox/HostDrivers/Support/Certificates/Timestamp-VBoxLegacyWinSha1CA.taf
new file mode 100644
index 00000000..a3216fb8
--- /dev/null
+++ b/src/VBox/HostDrivers/Support/Certificates/Timestamp-VBoxLegacyWinSha1CA.taf
Binary files differ
diff --git a/src/VBox/HostDrivers/Support/Certificates/Trusted-OracleCorporationVirtualBox-05308b76ac2e15b29720fb4395f65f38.cer b/src/VBox/HostDrivers/Support/Certificates/Trusted-OracleCorporationVirtualBox-05308b76ac2e15b29720fb4395f65f38.cer
new file mode 100644
index 00000000..e47fb25a
--- /dev/null
+++ b/src/VBox/HostDrivers/Support/Certificates/Trusted-OracleCorporationVirtualBox-05308b76ac2e15b29720fb4395f65f38.cer
Binary files differ
diff --git a/src/VBox/HostDrivers/Support/Certificates/Trusted-OracleCorporationVirtualBox-05308b76ac2e15b29720fb4395f65f38.taf b/src/VBox/HostDrivers/Support/Certificates/Trusted-OracleCorporationVirtualBox-05308b76ac2e15b29720fb4395f65f38.taf
new file mode 100644
index 00000000..a592810b
--- /dev/null
+++ b/src/VBox/HostDrivers/Support/Certificates/Trusted-OracleCorporationVirtualBox-05308b76ac2e15b29720fb4395f65f38.taf
Binary files differ
diff --git a/src/VBox/HostDrivers/Support/Makefile.kmk b/src/VBox/HostDrivers/Support/Makefile.kmk
new file mode 100644
index 00000000..6543b624
--- /dev/null
+++ b/src/VBox/HostDrivers/Support/Makefile.kmk
@@ -0,0 +1,938 @@
+# $Id: Makefile.kmk $
+## @file
+# Sub-Makefile for the support library and the drivers/modules/kexts it uses.
+#
+
+#
+# Copyright (C) 2006-2023 Oracle and/or its affiliates.
+#
+# This file is part of VirtualBox base platform packages, as
+# available from https://www.virtualbox.org.
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation, in version 3 of the
+# License.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, see <https://www.gnu.org/licenses>.
+#
+# The contents of this file may alternatively be used under the terms
+# of the Common Development and Distribution License Version 1.0
+# (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+# in the VirtualBox distribution, in which case the provisions of the
+# CDDL are applicable instead of those of the GPL.
+#
+# You may elect to license modified versions of this file under the
+# terms and conditions of either the GPL or the CDDL or both.
+#
+# SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+#
+
+SUB_DEPTH = ../../../..
+include $(KBUILD_PATH)/subheader.kmk
+
+#
+# Globals.
+#
+VBOX_PATH_SUP_SRC := $(PATH_SUB_CURRENT)
+VBOX_PATH_SUPR3_CERTIFICATES := $(PATH_SUB_CURRENT)/Certificates
+VBOX_PATH_RUNTIME_SRC ?= $(PATH_ROOT)/src/VBox/Runtime
+
+
+#
+# Targets
+#
+LIBRARIES += SUPR3 SUPR3Static
+if defined(VBOX_WITH_HARDENING) \
+ && !defined(VBOX_ONLY_VALIDATIONKIT)
+ LIBRARIES += SUPR3HardenedStatic
+endif
+ifndef VBOX_ONLY_BUILD
+ DLLS.win += VBoxSupLib
+endif
+ifdef VBOX_WITH_32_ON_64_MAIN_API
+ LIBRARIES += SUPR3-x86
+endif
+if !defined(VBOX_ONLY_DOCS)
+ if1of ($(VBOX_LDR_FMT), pe lx)
+ LIBRARIES += SUPR0
+ endif
+endif
+if !defined(VBOX_ONLY_BUILD) && defined(VBOX_WITH_RAW_MODE)
+ LIBRARIES += SUPRC
+endif
+if !defined(VBOX_ONLY_DOCS) \
+ && !defined(VBOX_ONLY_EXTPACKS) \
+ && !defined(VBOX_ONLY_VALIDATIONKIT) \
+ && "$(intersects $(KBUILD_TARGET_ARCH),$(VBOX_SUPPORTED_HOST_ARCHS))" != ""
+ ifdef VBOX_WITH_SUPSVC
+ PROGRAMS += VBoxSupSvc
+ endif
+ ifdef VBOX_WITH_VBOXDRV
+ LIBRARIES += SUPR0IdcClient
+ SYSMODS.os2 += VBoxDrv
+ endif
+ INSTALLS.linux += vboxdrv-src
+ INSTALLS.freebsd += vboxdrv-src
+
+ #
+ # Include sub-makefile(s).
+ #
+ include $(PATH_SUB_CURRENT)/testcase/Makefile.kmk
+
+ #
+ # Populate FILES_VBOXDRV_NOBIN and FILES_VBOXDRV_BIN
+ #
+ ifeq ($(KBUILD_TARGET),linux)
+ include $(PATH_SUB_CURRENT)/linux/files_vboxdrv
+ endif
+ ifeq ($(KBUILD_TARGET),freebsd)
+ include $(PATH_SUB_CURRENT)/freebsd/files_vboxdrv
+ endif
+endif # !VBOX_ONLY_DOCS && !VBOX_ONLY_EXTPACKS && !VBOX_ONLY_VALIDATIONKIT
+
+#
+# Authenticode related trust anchors and certificates -> .cpp
+#
+VBOX_SUP_WIN_CERTS_FILE = $(SUPR3_0_OUTDIR)/TrustAnchorsAndCerts.cpp
+VBOX_SUP_WIN_CERTS := \
+ SpcRootMicrosoft0=SpcRoot-MicrosoftAuthenticodeTmRootAuthority-01.taf \
+ SpcRootMicrosoft1=SpcRoot-MicrosoftRootAuthority-00c1008b3c3c8811d13ef663ecdf40.taf \
+ SpcRootMicrosoft2=SpcRoot-MicrosoftRootCertificateAuthority-79ad16a14aa0a5ad4c7358f407132e65.taf \
+ SpcRootMicrosoft3=SpcRoot-MicrosoftRootCertificateAuthority2010-28cc3a25bfba44ac449a9b586b4339aa.taf \
+ SpcRootMicrosoft4=SpcRoot-MicrosoftRootCertificateAuthority2011-3f8bc8b5fc9fb29643b569d66c42e144.taf \
+ SpcRootMicrosoft5=SpcRoot-MicrosoftDigitalMediaAuthority2005-6eff330eb6e7569740680870104baaba.taf \
+ SpcRootMicrosoft6=SpcRoot-MicrosoftDevelopmentRootCertificateAuthority2014-078f0a9d03df119e434e4fec1bf0235a.taf \
+ SpcRootMicrosoft7=SpcRoot-MicrosoftTestingRootCertificateAuthority2010-8a334aa8052dd244a647306a76b8178fa215f344.taf \
+ NtRootMicrosoft8=NtRoot-MicrosoftCodeVerificationRoot-729404101f3e0ca347837fca175a8438.taf \
+ TimeRootMicrosoft0=Timestamp-CopyrightC1997MicrosoftCorp-01.taf \
+ TimeRootOracle0=Timestamp-VBoxLegacyWinCA.taf \
+ TimeRootOracle1=Timestamp-VBoxLegacyWinSha1CA.taf \
+ TrustedCertVBox0=Trusted-OracleCorporationVirtualBox-05308b76ac2e15b29720fb4395f65f38.taf \
+ AppleRoot0=AppleRoot-2bd06947947609fef46b8d2e40a6f7474d7f085e.taf \
+ AppleRoot1=AppleRoot-G2-c499136c1803c27bc0a3a00d7f72807a1c77268d.taf
+# Hack Alert! Because the DigiCert cross signing certificate we use expired 2020-04-15, we add it as a trusted NT kernel
+# signing root instead, so we can bypass the expiry check in IPRT. NtRootMicrosoft8 is the one found in the
+# 6.1.20 extpack, NtRootMicrosoft9 is the one bird uses at home.
+VBOX_SUP_WIN_CERTS += NtRootDigiCert8=NtRoot-CrossSign-DigiCertAssuredIDRootCA-589567a6c1944d68f11ff3d86576092b.taf
+VBOX_SUP_WIN_CERTS += NtRootDigiCert9=NtRoot-CrossSign-DigiCertHighAssuranceEVRootCA-f4a38dbe86386c554d25f1ce2557a4fe.taf
+
+VBOX_SUP_WIN_CERT_NAMES := $(foreach cert,$(VBOX_SUP_WIN_CERTS),$(firstword $(subst =,$(SPACE) ,$(cert))))
+
+# 1=name, 2=filter, 3=buildcert?.
+if "$(KBUILD_TARGET)" == "win" && defined(VBOX_WITH_HARDENING)
+ VBOX_SUP_GEN_CERT_MACRO = 'SUPTAENTRY const g_aSUP$(1)TAs[] =' '{' \
+ $(if-expr "$(3)" == "",,' SUPTAENTRY_GEN(g_abSUPBuildCert),') \
+ $(foreach certnm,$(filter $(2),$(VBOX_SUP_WIN_CERT_NAMES)), ' SUPTAENTRY_GEN(g_abSUP$(certnm)),') \
+ '};' 'unsigned const g_cSUP$(1)TAs = RT_ELEMENTS(g_aSUP$(1)TAs);' '' ''
+else
+ VBOX_SUP_GEN_CERT_MACRO = 'SUPTAENTRY const g_aSUP$(1)TAs[] =' '{' \
+ $(foreach certnm,$(filter $(2),$(VBOX_SUP_WIN_CERT_NAMES)), ' SUPTAENTRY_GEN(g_abSUP$(certnm)),') \
+ '};' 'unsigned const g_cSUP$(1)TAs = RT_ELEMENTS(g_aSUP$(1)TAs);' '' ''
+endif
+
+if ("$(KBUILD_TARGET)" == "win" && defined(VBOX_SIGNING_MODE)) \
+ || ("$(KBUILD_TARGET)" == "darwin" && defined(VBOX_WITH_DARWIN_R0_DARWIN_IMAGE_VERIFICATION) && defined(VBOX_SIGNING_MODE))
+ BLDPROGS += bldSUPSignedDummy
+ bldSUPSignedDummy_TEMPLATE = VBoxBldProg
+ bldSUPSignedDummy_SOURCES = bldSUPSignedDummy.cpp
+ bldSUPSignedDummy_ORDERDEPS.win = $(VBOX_SIGN_IMAGE_ORDERDEPS)
+ bldSUPSignedDummy_POST_CMDS.win = $(call VBOX_SIGN_IMAGE_FN,$(out),,2)
+ bldSUPSignedDummy_POST_CMDS.darwin = $(call VBOX_SIGN_MACHO_FN,$(out),org.virtualbox.org.bldtool.$(target))
+endif
+
+$$(VBOX_SUP_WIN_CERTS_FILE): $(MAKEFILE_CURRENT) \
+ $(foreach cert,$(VBOX_SUP_WIN_CERTS),$(VBOX_PATH_SUPR3_CERTIFICATES)/$(lastword $(subst =,$(SPACE) ,$(cert)))) \
+ $(VBOX_BIN2C) \
+ $(if-expr defined(bldSUPSignedDummy_SOURCES),$(VBOX_RTSIGNTOOL) $$(bldSUPSignedDummy_1_TARGET),) \
+ | $$(dir $$@)
+ $(QUIET)$(RM) -f -- $@ $@.cer
+ $(QUIET)$(APPEND) -n "$@" \
+ '' \
+ '#include <VBox/sup.h>' \
+ ''
+ $(foreach cert,$(VBOX_SUP_WIN_CERTS), $(NLTAB)$(VBOX_BIN2C) -ascii --append --static --no-size \
+ "SUP$(firstword $(subst =,$(SP) ,$(cert)))" \
+ "$(VBOX_PATH_SUPR3_CERTIFICATES)/$(lastword $(subst =,$(SP) ,$(cert)))" \
+ "$@")
+# The build certificate.
+ifdef bldSUPSignedDummy_SOURCES
+ $(VBOX_RTSIGNTOOL) extract-exe-signer-cert --exe "$(bldSUPSignedDummy_1_TARGET)" --output "$@.cer" --der
+ $(VBOX_BIN2C) -ascii --append SUPBuildCert "$@.cer" $@
+ $(QUIET)$(RM) -f -- $@.cer
+endif
+# Generate certificate lists.
+ $(QUIET)$(APPEND) -n "$@" '' \
+ $(call VBOX_SUP_GEN_CERT_MACRO,All,%,build) \
+ $(call VBOX_SUP_GEN_CERT_MACRO,SpcRoot,SpcRoot%) \
+ $(call VBOX_SUP_GEN_CERT_MACRO,NtKernelRoot,NtRoot%) \
+ $(call VBOX_SUP_GEN_CERT_MACRO,Timestamp,TimeRoot%) \
+ $(call VBOX_SUP_GEN_CERT_MACRO,AppleRoot,AppleRoot%) \
+ $(call VBOX_SUP_GEN_CERT_MACRO,Trusted,TrustedCert%,build)
+
+OTHER_CLEAN += $(VBOX_SUP_WIN_CERTS_FILE)
+
+tst: $(VBOX_SUP_WIN_CERTS_FILE)
+
+
+#
+# The Ring-3 Support Library (this is linked into the IPRT dll, VBoxRT).
+#
+SUPR3_TEMPLATE = VBoxR3Dll
+SUPR3_DEFS = \
+ IN_SUP_R3 IN_RT_R3 \
+ $(if $(VBOX_WITH_SUPSVC),VBOX_WITH_SUPSVC) \
+ $(if $(VBOX_WITH_MAIN),VBOX_WITH_MAIN,) \
+ $(if $(VBOX_WITH_RAW_MODE),VBOX_WITH_RAW_MODE,) \
+ $(if $(VBOX_WITH_DRIVERLESS_NEM_FALLBACK),VBOX_WITH_DRIVERLESS_NEM_FALLBACK,) \
+ VBOX_PERMIT_MORE \
+ VBOX_PERMIT_EVEN_MORE
+SUPR3_INCS := $(PATH_SUB_CURRENT)
+SUPR3_SOURCES = \
+ SUPLib.cpp \
+ SUPLibLdr.cpp \
+ SUPLibSem.cpp \
+ SUPLibAll.cpp \
+ SUPR3HardenedIPRT.cpp \
+ SUPR3HardenedVerify.cpp \
+ $(KBUILD_TARGET)/SUPLib-$(KBUILD_TARGET).cpp \
+ $(VBOX_SUP_WIN_CERTS_FILE)
+SUPR3_SOURCES.amd64 = \
+ SUPLibTracerA.asm
+SUPR3_SOURCES.x86 = \
+ SUPLibTracerA.asm
+ifdef VBOX_WITH_HARDENING
+ SUPR3_SOURCES.win = \
+ win/SUPHardenedVerifyImage-win.cpp
+endif
+
+SUPR3-x86_TEMPLATE = VBoxR3Dll-x86
+SUPR3-x86_EXTENDS = SUPR3
+
+
+#
+# Static version of SUPR3. This is more of a stub than anything else in a
+# hardened build, at least on windows.
+#
+SUPR3Static_TEMPLATE := VBoxR3Static
+SUPR3Static_EXTENDS := SUPR3
+SUPR3Static_DEFS = $(SUPR3_DEFS) IN_SUP_R3_STATIC
+SUPR3Static_SOURCES.win = $(filter-out win/SUPHardenedVerifyImage-win.cpp, $(SUPR3_SOURCES.win))
+
+
+#
+# The static part of the hardened support library (ring-3).
+#
+SUPR3HardenedStatic_TEMPLATE = VBoxR3HardenedLib
+SUPR3HardenedStatic_DEFS = IN_SUP_HARDENED_R3
+SUPR3HardenedStatic_DEFS += \
+ $(if $(VBOX_WITH_SUPSVC),VBOX_WITH_SUPSVC,) \
+ $(if $(VBOX_WITH_MAIN),VBOX_WITH_MAIN,) \
+ $(if $(VBOX_WITH_RAW_MODE),VBOX_WITH_RAW_MODE,) \
+ $(if $(VBOX_WITH_DRIVERLESS_NEM_FALLBACK),VBOX_WITH_DRIVERLESS_NEM_FALLBACK,) \
+ $(if $(VBOX_WITHOUT_DEBUGGER_CHECKS),VBOX_WITHOUT_DEBUGGER_CHECKS,) \
+ $(if $(VBOX_PERMIT_VISUAL_STUDIO_PROFILING),VBOX_PERMIT_VISUAL_STUDIO_PROFILING,) \
+ VBOX_PERMIT_MORE \
+ VBOX_PERMIT_EVEN_MORE
+ifdef VBOX_WITH_VISTA_NO_SP
+ SUPR3HardenedStatic_DEFS.win += VBOX_WITH_VISTA_NO_SP
+endif
+SUPR3HardenedStatic_INCS = .
+SUPR3HardenedStatic_SOURCES = \
+ SUPR3HardenedMain.cpp \
+ SUPR3HardenedVerify.cpp \
+ SUPR3HardenedNoCrt.cpp \
+ $(KBUILD_TARGET)/SUPLib-$(KBUILD_TARGET).cpp
+SUPR3HardenedStatic_SOURCES.win = \
+ win/SUPR3HardenedMain-win.cpp \
+ win/SUPR3HardenedMainA-win.asm \
+ win/SUPR3HardenedMainImports-win.cpp \
+ win/SUPHardenedVerifyProcess-win.cpp \
+ win/SUPHardenedVerifyImage-win.cpp \
+ $(VBOX_SUP_WIN_CERTS_FILE)
+SUPR3HardenedStatic_SOURCES.x86 += \
+ $(VBOX_PATH_RUNTIME_SRC)/common/asm/ASMMemFirstMismatchingU8.asm
+SUPR3HardenedStatic_SOURCES.amd64 += \
+ $(VBOX_PATH_RUNTIME_SRC)/common/asm/ASMMemFirstMismatchingU8.asm
+
+
+if "$(KBUILD_TARGET)" == "win" && defined(VBOX_WITH_HARDENING) && !defined(VBOX_ONLY_VALIDATIONKIT) ## @todo some of this move up.
+ SUPR3HardenedStatic_DEFS += \
+ IN_RT_R3 \
+ IN_RT_STATIC \
+ IN_DIS \
+ IN_DIS_STATIC \
+ DIS_CORE_ONLY \
+ IPRT_NO_CRT \
+ RT_WITH_NOCRT_ALIASES \
+ LOG_DISABLED \
+ IPRT_NO_ERROR_DATA \
+ IPRT_WITHOUT_DIGEST_MD4
+ SUPR3HardenedStatic_DEFS.win += LDR_ONLY_PE __STRALIGN_H_
+
+ SUPR3HardenedStatic_INCS += $(PATH_ROOT)/include/iprt/nocrt $(VBOX_PATH_RUNTIME_SRC)/include
+
+ SUPR3HardenedStatic_SOURCES += \
+ $(VBOX_PATH_RUNTIME_SRC)/common/ldr/ldr.cpp \
+ $(VBOX_PATH_RUNTIME_SRC)/common/ldr/ldrEx.cpp \
+ $(VBOX_PATH_RUNTIME_SRC)/common/ldr/ldrPE.cpp \
+ $(VBOX_PATH_RUNTIME_SRC)/common/alloc/heapsimple.cpp \
+ $(VBOX_PATH_RUNTIME_SRC)/common/asn1/asn1-basics.cpp \
+ $(VBOX_PATH_RUNTIME_SRC)/common/asn1/asn1-cursor.cpp \
+ $(VBOX_PATH_RUNTIME_SRC)/common/asn1/asn1-default-allocator.cpp \
+ $(VBOX_PATH_RUNTIME_SRC)/common/asn1/asn1-safer-allocator.cpp \
+ $(VBOX_PATH_RUNTIME_SRC)/common/asn1/asn1-dump.cpp \
+ $(VBOX_PATH_RUNTIME_SRC)/common/asn1/asn1-encode.cpp \
+ $(VBOX_PATH_RUNTIME_SRC)/common/asn1/asn1-ut-bitstring.cpp \
+ $(VBOX_PATH_RUNTIME_SRC)/common/asn1/asn1-ut-bitstring-decode.cpp \
+ $(VBOX_PATH_RUNTIME_SRC)/common/asn1/asn1-ut-boolean.cpp \
+ $(VBOX_PATH_RUNTIME_SRC)/common/asn1/asn1-ut-boolean-decode.cpp \
+ $(VBOX_PATH_RUNTIME_SRC)/common/asn1/asn1-ut-core.cpp \
+ $(VBOX_PATH_RUNTIME_SRC)/common/asn1/asn1-ut-core-decode.cpp \
+ $(VBOX_PATH_RUNTIME_SRC)/common/asn1/asn1-ut-dyntype.cpp \
+ $(VBOX_PATH_RUNTIME_SRC)/common/asn1/asn1-ut-dyntype-decode.cpp \
+ $(VBOX_PATH_RUNTIME_SRC)/common/asn1/asn1-ut-integer.cpp \
+ $(VBOX_PATH_RUNTIME_SRC)/common/asn1/asn1-ut-integer-decode.cpp \
+ $(VBOX_PATH_RUNTIME_SRC)/common/asn1/asn1-ut-null.cpp \
+ $(VBOX_PATH_RUNTIME_SRC)/common/asn1/asn1-ut-null-decode.cpp \
+ $(VBOX_PATH_RUNTIME_SRC)/common/asn1/asn1-ut-objid.cpp \
+ $(VBOX_PATH_RUNTIME_SRC)/common/asn1/asn1-ut-objid-decode.cpp \
+ $(VBOX_PATH_RUNTIME_SRC)/common/asn1/asn1-ut-octetstring.cpp \
+ $(VBOX_PATH_RUNTIME_SRC)/common/asn1/asn1-ut-octetstring-decode.cpp \
+ $(VBOX_PATH_RUNTIME_SRC)/common/asn1/asn1-ut-string.cpp \
+ $(VBOX_PATH_RUNTIME_SRC)/common/asn1/asn1-ut-string-decode.cpp \
+ $(VBOX_PATH_RUNTIME_SRC)/common/asn1/asn1-ut-time.cpp \
+ $(VBOX_PATH_RUNTIME_SRC)/common/asn1/asn1-ut-time-decode.cpp \
+ $(VBOX_PATH_RUNTIME_SRC)/common/crypto/digest-core.cpp \
+ $(VBOX_PATH_RUNTIME_SRC)/common/crypto/digest-builtin.cpp \
+ $(VBOX_PATH_RUNTIME_SRC)/common/crypto/key.cpp \
+ $(VBOX_PATH_RUNTIME_SRC)/common/crypto/pkcs7-asn1-decoder.cpp \
+ $(VBOX_PATH_RUNTIME_SRC)/common/crypto/pkcs7-core.cpp \
+ $(VBOX_PATH_RUNTIME_SRC)/common/crypto/pkcs7-init.cpp \
+ $(VBOX_PATH_RUNTIME_SRC)/common/crypto/pkcs7-sanity.cpp \
+ $(VBOX_PATH_RUNTIME_SRC)/common/crypto/pkcs7-verify.cpp \
+ $(VBOX_PATH_RUNTIME_SRC)/common/crypto/pkix-signature-builtin.cpp \
+ $(VBOX_PATH_RUNTIME_SRC)/common/crypto/pkix-signature-core.cpp \
+ $(VBOX_PATH_RUNTIME_SRC)/common/crypto/pkix-signature-rsa.cpp \
+ $(VBOX_PATH_RUNTIME_SRC)/common/crypto/pkix-verify.cpp \
+ $(VBOX_PATH_RUNTIME_SRC)/common/crypto/pkix-util.cpp \
+ $(VBOX_PATH_RUNTIME_SRC)/common/crypto/rsa-asn1-decoder.cpp \
+ $(VBOX_PATH_RUNTIME_SRC)/common/crypto/rsa-core.cpp \
+ $(VBOX_PATH_RUNTIME_SRC)/common/crypto/rsa-init.cpp \
+ $(VBOX_PATH_RUNTIME_SRC)/common/crypto/rsa-sanity.cpp \
+ $(VBOX_PATH_RUNTIME_SRC)/common/crypto/spc-asn1-decoder.cpp \
+ $(VBOX_PATH_RUNTIME_SRC)/common/crypto/spc-core.cpp \
+ $(VBOX_PATH_RUNTIME_SRC)/common/crypto/spc-init.cpp \
+ $(VBOX_PATH_RUNTIME_SRC)/common/crypto/spc-sanity.cpp \
+ $(VBOX_PATH_RUNTIME_SRC)/common/crypto/x509-asn1-decoder.cpp \
+ $(VBOX_PATH_RUNTIME_SRC)/common/crypto/x509-certpaths.cpp \
+ $(VBOX_PATH_RUNTIME_SRC)/common/crypto/x509-core.cpp \
+ $(VBOX_PATH_RUNTIME_SRC)/common/crypto/x509-init.cpp \
+ $(VBOX_PATH_RUNTIME_SRC)/common/crypto/x509-sanity.cpp \
+ $(VBOX_PATH_RUNTIME_SRC)/common/crypto/x509-verify.cpp \
+ $(VBOX_PATH_RUNTIME_SRC)/common/crypto/store.cpp \
+ $(VBOX_PATH_RUNTIME_SRC)/common/crypto/store-inmem.cpp \
+ $(VBOX_PATH_RUNTIME_SRC)/common/crypto/taf-asn1-decoder.cpp \
+ $(VBOX_PATH_RUNTIME_SRC)/common/crypto/taf-core.cpp \
+ $(VBOX_PATH_RUNTIME_SRC)/common/crypto/taf-init.cpp \
+ $(VBOX_PATH_RUNTIME_SRC)/common/crypto/taf-sanity.cpp \
+ $(VBOX_PATH_RUNTIME_SRC)/common/crypto/tsp-asn1-decoder.cpp \
+ $(VBOX_PATH_RUNTIME_SRC)/common/crypto/tsp-core.cpp \
+ $(VBOX_PATH_RUNTIME_SRC)/common/crypto/tsp-init.cpp \
+ $(VBOX_PATH_RUNTIME_SRC)/common/crypto/tsp-sanity.cpp \
+ $(VBOX_PATH_RUNTIME_SRC)/common/checksum/alt-md2.cpp \
+ $(VBOX_PATH_RUNTIME_SRC)/common/checksum/alt-md5.cpp \
+ $(VBOX_PATH_RUNTIME_SRC)/common/checksum/alt-sha1.cpp \
+ $(VBOX_PATH_RUNTIME_SRC)/common/checksum/alt-sha256.cpp \
+ $(VBOX_PATH_RUNTIME_SRC)/common/checksum/alt-sha512.cpp \
+ $(VBOX_PATH_RUNTIME_SRC)/common/checksum/alt-sha3.cpp \
+ $(VBOX_PATH_RUNTIME_SRC)/common/checksum/md2str.cpp \
+ $(VBOX_PATH_RUNTIME_SRC)/common/checksum/md5str.cpp \
+ $(VBOX_PATH_RUNTIME_SRC)/common/checksum/sha1str.cpp \
+ $(VBOX_PATH_RUNTIME_SRC)/common/checksum/sha256str.cpp \
+ $(VBOX_PATH_RUNTIME_SRC)/common/checksum/sha384str.cpp \
+ $(VBOX_PATH_RUNTIME_SRC)/common/checksum/sha512str.cpp \
+ $(VBOX_PATH_RUNTIME_SRC)/common/err/errinfo.cpp \
+ $(VBOX_PATH_RUNTIME_SRC)/common/path/RTPathChangeToUnixSlashes.cpp \
+ $(VBOX_PATH_RUNTIME_SRC)/common/path/RTPathExt.cpp \
+ $(VBOX_PATH_RUNTIME_SRC)/common/string/RTUtf16PrintHexBytes.cpp \
+ $(VBOX_PATH_RUNTIME_SRC)/common/string/RTUtf16ICmpAscii.cpp \
+ $(VBOX_PATH_RUNTIME_SRC)/common/string/RTUtf16NICmpAscii.cpp \
+ $(VBOX_PATH_RUNTIME_SRC)/common/string/RTUtf16CatAscii.cpp \
+ $(VBOX_PATH_RUNTIME_SRC)/common/string/RTUtf16CopyAscii.cpp \
+ $(VBOX_PATH_RUNTIME_SRC)/common/string/RTUtf16End.cpp \
+ $(VBOX_PATH_RUNTIME_SRC)/common/string/strstrip.cpp \
+ \
+ $(VBOX_PATH_RUNTIME_SRC)/common/err/errmsg.cpp \
+ $(VBOX_PATH_RUNTIME_SRC)/common/math/bignum.cpp \
+ $(VBOX_PATH_RUNTIME_SRC)/common/math/bignum-amd64-x86.asm \
+ $(VBOX_PATH_RUNTIME_SRC)/common/misc/RTAssertMsg1Weak.cpp \
+ $(VBOX_PATH_RUNTIME_SRC)/common/misc/RTAssertMsg2.cpp \
+ $(VBOX_PATH_RUNTIME_SRC)/common/misc/RTAssertMsg2Weak.cpp \
+ $(VBOX_PATH_RUNTIME_SRC)/common/misc/RTAssertMsg2WeakV.cpp \
+ $(VBOX_PATH_RUNTIME_SRC)/common/misc/zero.asm \
+ $(VBOX_PATH_RUNTIME_SRC)/common/path/RTPathAbsEx.cpp \
+ $(VBOX_PATH_RUNTIME_SRC)/common/path/RTPathFilename.cpp \
+ $(VBOX_PATH_RUNTIME_SRC)/common/path/RTPathParse.cpp \
+ $(VBOX_PATH_RUNTIME_SRC)/common/path/RTPathParsedReassemble.cpp \
+ $(VBOX_PATH_RUNTIME_SRC)/common/string/memchr.asm \
+ $(VBOX_PATH_RUNTIME_SRC)/common/string/memcmp.asm \
+ $(VBOX_PATH_RUNTIME_SRC)/common/string/memcpy.asm \
+ $(VBOX_PATH_RUNTIME_SRC)/common/string/memmove.asm \
+ $(VBOX_PATH_RUNTIME_SRC)/common/string/mempcpy.asm \
+ $(VBOX_PATH_RUNTIME_SRC)/common/string/memset.asm \
+ $(VBOX_PATH_RUNTIME_SRC)/common/string/strversion.cpp \
+ $(VBOX_PATH_RUNTIME_SRC)/common/string/RTStrPrintHexBytes.cpp \
+ $(VBOX_PATH_RUNTIME_SRC)/common/string/RTStrCat.cpp \
+ $(VBOX_PATH_RUNTIME_SRC)/common/string/RTStrCmp.cpp \
+ $(VBOX_PATH_RUNTIME_SRC)/common/string/RTStrCopy.cpp \
+ $(VBOX_PATH_RUNTIME_SRC)/common/string/RTStrEnd.asm \
+ $(VBOX_PATH_RUNTIME_SRC)/common/string/RTStrICmpAscii.cpp \
+ $(VBOX_PATH_RUNTIME_SRC)/common/string/RTStrNCmp.cpp \
+ $(VBOX_PATH_RUNTIME_SRC)/common/string/RTStrNLen.cpp \
+ $(VBOX_PATH_RUNTIME_SRC)/common/string/RTUtf16Copy.cpp \
+ $(VBOX_PATH_RUNTIME_SRC)/common/string/RTUtf16NLenEx.cpp \
+ $(VBOX_PATH_RUNTIME_SRC)/common/string/strchr.asm \
+ $(VBOX_PATH_RUNTIME_SRC)/common/string/strcmp.asm \
+ $(VBOX_PATH_RUNTIME_SRC)/common/string/strcpy.asm \
+ $(VBOX_PATH_RUNTIME_SRC)/common/string/strformat.cpp \
+ $(VBOX_PATH_RUNTIME_SRC)/common/string/RTStrFormat.cpp \
+ $(VBOX_PATH_RUNTIME_SRC)/common/string/strformatrt.cpp \
+ $(VBOX_PATH_RUNTIME_SRC)/common/string/strformattype.cpp \
+ $(VBOX_PATH_RUNTIME_SRC)/common/string/strformatnum.cpp \
+ $(VBOX_PATH_RUNTIME_SRC)/common/string/stringalloc.cpp \
+ $(VBOX_PATH_RUNTIME_SRC)/common/string/strlen.asm \
+ $(VBOX_PATH_RUNTIME_SRC)/common/string/strncmp.asm \
+ $(VBOX_PATH_RUNTIME_SRC)/common/string/strncpy.asm \
+ $(VBOX_PATH_RUNTIME_SRC)/common/string/strprintf.cpp \
+ $(VBOX_PATH_RUNTIME_SRC)/common/string/strprintf-ellipsis.cpp \
+ $(VBOX_PATH_RUNTIME_SRC)/common/string/strprintf2.cpp \
+ $(VBOX_PATH_RUNTIME_SRC)/common/string/strprintf2-ellipsis.cpp \
+ $(VBOX_PATH_RUNTIME_SRC)/common/string/strtonum.cpp \
+ $(VBOX_PATH_RUNTIME_SRC)/common/string/utf-16.cpp \
+ $(VBOX_PATH_RUNTIME_SRC)/common/string/utf-8.cpp \
+ $(VBOX_PATH_RUNTIME_SRC)/common/string/utf-8-case.cpp \
+ $(VBOX_PATH_RUNTIME_SRC)/common/string/unidata-upper.cpp \
+ $(VBOX_PATH_RUNTIME_SRC)/common/string/unidata-lower.cpp \
+ $(VBOX_PATH_RUNTIME_SRC)/common/time/time.cpp \
+ $(VBOX_PATH_RUNTIME_SRC)/generic/RTAssertShouldPanic-generic.cpp \
+ $(VBOX_PATH_RUNTIME_SRC)/generic/RTPathGetCurrentDrive-generic.cpp \
+ $(VBOX_PATH_RUNTIME_SRC)/generic/RTPathGetCurrentOnDrive-generic.cpp \
+ $(VBOX_PATH_RUNTIME_SRC)/generic/rtStrFormatKernelAddress-generic.cpp \
+ $(VBOX_PATH_RUNTIME_SRC)/generic/memsafer-generic.cpp \
+ $(VBOX_PATH_RUNTIME_SRC)/generic/uuid-generic.cpp \
+ \
+ ../../Disassembler/DisasmCore.cpp \
+ ../../Disassembler/DisasmTables.cpp \
+ ../../Disassembler/DisasmTablesX64.cpp \
+ ../../Disassembler/DisasmMisc.cpp
+
+ SUPR3HardenedStatic_SOURCES.amd64 += \
+ $(VBOX_PATH_RUNTIME_SRC)/common/math/RTUInt128MulByU64.asm \
+ $(VBOX_PATH_RUNTIME_SRC)/win/amd64/ASMGetCS.asm \
+ $(VBOX_PATH_RUNTIME_SRC)/win/amd64/ASMGetSS.asm
+
+ SUPR3HardenedStatic_SOURCES.win += \
+ win/SUPR3HardenedNoCrt-win.cpp \
+ $(VBOX_PATH_RUNTIME_SRC)/nt/RTErrConvertFromNtStatus.cpp \
+ $(VBOX_PATH_RUNTIME_SRC)/nt/RTNtPathFindPossible8dot3Name.cpp \
+ $(VBOX_PATH_RUNTIME_SRC)/nt/RTNtPathExpand8dot3Path.cpp \
+ $(VBOX_PATH_RUNTIME_SRC)/nt/RTNtPathExpand8dot3PathA.cpp \
+ $(VBOX_PATH_RUNTIME_SRC)/r3/nt/pathint-nt.cpp \
+ $(VBOX_PATH_RUNTIME_SRC)/win/RTErrConvertFromWin32.cpp \
+ $(VBOX_PATH_RUNTIME_SRC)/win/errmsgwin.cpp
+
+ # Add necessary compiler specific files from the compiler lib dir.
+ ifeq ($(KBUILD_TARGET),win)
+ include $(KBUILD_PATH)/tools/$(VBOX_VCC_TOOL).kmk
+ SUPR3HardenedStatic_SOURCES.win += \
+ $(PATH_TOOL_$(VBOX_VCC_TOOL)_LIB)/chkstk.obj
+
+ # And a few extracted from the static libc to support -guard and cookes.
+ # In 14.2 these files does not import anything from the win32 API and are
+ # mostly tiny bits of code. Needs not initialization that I can spot.
+ ifneq ($(VBOX_VCC_LD_GUARD_CF),)
+ SUPR3HardenedStatic_SOURCES.win += \
+ $(SUPR3HardenedStatic_0_OUTDIR)/loadcfg.obj
+ # These are for the /guard option.
+ SUPR3HardenedStatic_SOURCES.win += \
+ $(SUPR3HardenedStatic_0_OUTDIR)/gs_cookie.obj \
+ $(SUPR3HardenedStatic_0_OUTDIR)/guard_support.obj
+ SUPR3HardenedStatic_SOURCES.win.amd64 += \
+ $(SUPR3HardenedStatic_0_OUTDIR)/guard_dispatch.obj \
+ $(SUPR3HardenedStatic_0_OUTDIR)/guard_xfg_dispatch.obj
+ # These next ones are for supporting the /GS option. We skip gs_report.obj as it
+ # import lots from kernel32 and we're better of reporting the problem ourselves.
+ SUPR3HardenedStatic_SOURCES.win.amd64 += \
+ $(SUPR3HardenedStatic_0_OUTDIR)/amdsecgs.obj \
+ $(SUPR3HardenedStatic_0_OUTDIR)/gshandler.obj
+ SUPR3HardenedStatic_SOURCES.win.x86 += \
+ $(SUPR3HardenedStatic_0_OUTDIR)/secchk.obj \
+ $(SUPR3HardenedStatic_0_OUTDIR)/alloca16.obj
+
+ $$(SUPR3HardenedStatic_0_OUTDIR)/loadcfg.obj \
+ $$(SUPR3HardenedStatic_0_OUTDIR)/gs_cookie.obj \
+ $$(SUPR3HardenedStatic_0_OUTDIR)/guard_support.obj \
+ $$(SUPR3HardenedStatic_0_OUTDIR)/guard_dispatch.obj \
+ $$(SUPR3HardenedStatic_0_OUTDIR)/guard_xfg_dispatch.obj \
+ $$(SUPR3HardenedStatic_0_OUTDIR)/amdsecgs.obj \
+ $$(SUPR3HardenedStatic_0_OUTDIR)/gs_report.obj \
+ $$(SUPR3HardenedStatic_0_OUTDIR)/gshandler.obj \
+ $$(SUPR3HardenedStatic_0_OUTDIR)/secchk.obj \
+ $$(SUPR3HardenedStatic_0_OUTDIR)/alloca16.obj: \
+ $(PATH_TOOL_$(VBOX_VCC_TOOL)_LIB)/libcmt.lib | $$(dir $$@)
+ set -x; $(TOOL_$(VBOX_VCC_TOOL)_AR) "/EXTRACT:$$($(TOOL_$(VBOX_VCC_TOOL)_AR) /LIST "$<" | $(SED_EXT) -e '/$(notdir $@)/!d' )" "/OUT:$@" "$<"
+ endif
+ endif
+endif
+
+# macOS specifics.
+ifeq ($(KBUILD_TARGET),darwin)
+ SUPR3HardenedStatic_DEFS += \
+ LOG_DISABLED
+
+ SUPR3HardenedStatic_INCS += $(VBOX_PATH_RUNTIME_SRC)/include
+
+ SUPR3HardenedStatic_SOURCES += \
+ darwin/SUPR3HardenedMain-darwin.cpp \
+ \
+ $(VBOX_PATH_RUNTIME_SRC)/common/misc/RTAssertMsg1Weak.cpp \
+ $(VBOX_PATH_RUNTIME_SRC)/common/misc/RTAssertMsg2.cpp \
+ $(VBOX_PATH_RUNTIME_SRC)/common/misc/RTAssertMsg2Weak.cpp \
+ $(VBOX_PATH_RUNTIME_SRC)/common/misc/RTAssertMsg2WeakV.cpp \
+ $(VBOX_PATH_RUNTIME_SRC)/generic/RTAssertShouldPanic-generic.cpp
+endif
+
+# Things specific to the rest of the posix crowd.
+if1of ($(KBUILD_TARGET), linux solaris)
+ SUPR3HardenedStatic_DEFS += \
+ IN_DIS \
+ IN_DIS_STATIC \
+ DIS_CORE_ONLY \
+ LOG_DISABLED
+ SUPR3HardenedStatic_DEFS.linux += \
+ SUP_HARDENED_WITH_DLMOPEN
+ SUPR3HardenedStatic_DEFS.solaris += \
+ SUP_HARDENED_WITH_DLMOPEN
+ SUPR3HardenedStatic_DEFS.asan += SUP_HARDENED_WITHOUT_DLOPEN_PATCHING
+
+ SUPR3HardenedStatic_INCS += $(VBOX_PATH_RUNTIME_SRC)/include
+
+ SUPR3HardenedStatic_SOURCES += \
+ posix/SUPR3HardenedMain-posix.cpp \
+ posix/SUPR3HardenedMainA-posix.asm \
+ \
+ $(VBOX_PATH_RUNTIME_SRC)/common/misc/RTAssertMsg1Weak.cpp \
+ $(VBOX_PATH_RUNTIME_SRC)/common/misc/RTAssertMsg2.cpp \
+ $(VBOX_PATH_RUNTIME_SRC)/common/misc/RTAssertMsg2Weak.cpp \
+ $(VBOX_PATH_RUNTIME_SRC)/common/misc/RTAssertMsg2WeakV.cpp \
+ $(VBOX_PATH_RUNTIME_SRC)/generic/RTAssertShouldPanic-generic.cpp \
+ \
+ ../../Disassembler/DisasmCore.cpp \
+ ../../Disassembler/DisasmTables.cpp \
+ ../../Disassembler/DisasmTablesX64.cpp \
+ ../../Disassembler/DisasmMisc.cpp
+endif
+
+SUPR3HardenedMain.cpp_DEFS = VBOX_SVN_REV=$(VBOX_SVN_REV)
+
+
+#
+# VBoxSupLib - Windows DLL for catching thread creation and termination.
+#
+VBoxSupLib_TEMPLATE = VBoxR3StaticNoCrt
+VBoxSupLib_SDKS.win = VBoxNtDll
+VBoxSupLib_LDFLAGS.win.amd64 = -Entry:DllMainEntrypoint
+VBoxSupLib_LDFLAGS.win.x86 = -Entry:DllMainEntrypoint
+VBoxSupLib_DEFS = \
+ $(if $(VBOX_WITHOUT_DEBUGGER_CHECKS),VBOX_WITHOUT_DEBUGGER_CHECKS,)
+VBoxSupLib_SOURCES = \
+ $(KBUILD_TARGET)/VBoxSupLib-$(KBUILD_TARGET).cpp
+VBoxSupLib_SOURCES.win.amd64 = \
+ $(VBOX_PATH_RUNTIME_SRC)/common/compiler/vcc/stack-probe-vcc.asm
+VBoxSupLib_SOURCES.win.x86 = \
+ $(VBOX_PATH_RUNTIME_SRC)/common/compiler/vcc/stack-probe-vcc.asm
+VBoxSupLib_SOURCES.win = \
+ win/VBoxSupLib.rc
+ifndef VBOX_WITH_NOCRT_STATIC
+ ifdef VBOX_WITH_HARDENING # for /guard:cf stuff
+ VBoxSupLib_LIBS.win.x86 = \
+ $(PATH_TOOL_$(TEMPLATE_VBoxR3StaticNoCrt_TOOL.win.x86)_LIB)/libcmt.lib
+ VBoxSupLib_LIBS.win.amd64 = \
+ $(PATH_TOOL_$(TEMPLATE_VBoxR3StaticNoCrt_TOOL.win.amd64)_LIB)/libcmt.lib
+ endif
+endif
+VBoxSupLib_VBOX_IMPORT_CHECKER.win.x86 = xp
+VBoxSupLib_VBOX_IMPORT_CHECKER.win.amd64 = xp64
+
+
+#
+# VBoxSupSvc - The system wide service/daemon.
+#
+VBoxSupSvc_TEMPLATE = VBoxR3Exe
+VBoxSupSvc_SOURCES = \
+ SUPSvc.cpp \
+ SUPSvcGlobal.cpp \
+ $(KBUILD_TARGET)/SUPSvc-$(KBUILD_TARGET).cpp
+if1of ($(KBUILD_TARGET), win)
+ VBoxSupSvc_SOURCES += \
+ SUPSvcGrant.cpp
+endif
+ifn1of ($(KBUILD_TARGET), win)
+ VBoxSupSvc_SOURCES += \
+ SUPSvcMain-posix.cpp
+endif
+VBoxSupSvc_LIBS = \
+ $(LIB_RUNTIME)
+
+
+#
+# SUPR0 - The Ring-0 Import library.
+#
+SUPR0_TEMPLATE = VBoxR0
+if1of ($(VBOX_LDR_FMT), pe lx)
+ SUPR0_SOURCES = $(SUPR0_0_OUTDIR)/SUPR0.def
+ SUPR0_CLEAN = $(SUPR0_0_OUTDIR)/SUPR0.def
+ $$(SUPR0_0_OUTDIR)/SUPR0.def: \
+ $(PATH_SUB_CURRENT)/SUPDrv.cpp \
+ $(PATH_SUB_CURRENT)/SUPR0-def-$(VBOX_LDR_FMT).sed \
+ | $$(dir $$@)
+ $(SED) \
+ -f $(dir $<)/SUPR0-def-$(VBOX_LDR_FMT).sed \
+ --output $@ \
+ $<
+ # Experiment: Let's see how blunt the ones messing our NULL_THUNK_DATA entries on W10 are.
+ ifeq ($(KBUILD_TARGET),win)
+ ifdef KLIBTWEAKER_EXT
+ SUPR0_POST_CMDS = $(KLIBTWEAKER_EXT) --clear-timestamps --fill-null_thunk_data $(out)
+ endif
+ endif
+endif
+
+
+#
+# SUPRC - The raw-mode context library.
+#
+SUPRC_TEMPLATE := VBoxRc
+SUPRC_DEFS := IN_SUP_RC IN_RT_RC IN_VMM_RC
+SUPRC_SOURCES := SUPLibAll.cpp
+
+
+#
+# SUPR0IdcClient - The Ring-0 IDC client driver library.
+#
+SUPR0IdcClient_TEMPLATE = VBoxR0DrvLib
+SUPR0IdcClient_DEFS = IN_RT_R0 IN_SUP_R0 IN_SUP_STATIC
+SUPR0IdcClient_SDKS.win = ReorderCompilerIncs $(VBOX_WINDDK) $(VBOX_WINPSDK_INCS)
+SUPR0IdcClient_SOURCES.$(KBUILD_TARGET) = \
+ $(KBUILD_TARGET)/SUPR0IdcClient-$(KBUILD_TARGET).c
+SUPR0IdcClient_SOURCES = \
+ SUPR0IdcClient.c \
+ SUPR0IdcClientComponent.c \
+ SUPR0IdcClientStubs.c
+
+
+
+if !defined(VBOX_ONLY_DOCS) \
+ && !defined(VBOX_ONLY_EXTPACKS) \
+ && !defined(VBOX_ONLY_VALIDATIONKIT)
+
+ ifeq ($(KBUILD_TARGET),os2)
+
+ #
+ # VBoxDrv.sys - The OS/2 driver.
+ #
+ VBoxDrv_TEMPLATE = VBoxR0Drv
+ VBoxDrv_DEFS = IN_RT_R0 IN_SUP_R0
+ VBoxDrv_INCS := $(PATH_SUB_CURRENT)
+ #VBoxDrv_LDFLAGS = -s -t -v
+ VBoxDrv_SOURCES = \
+ os2/SUPDrvA-os2.asm \
+ os2/SUPDrv-os2.def
+ VBoxDrv_LIBS = \
+ $(VBoxDrvLib_1_TARGET) \
+ $(PATH_STAGE_LIB)/RuntimeR0Drv$(VBOX_SUFF_LIB) \
+ $(VBOX_GCC_LIBGCC) \
+ end
+
+ # temp hack to ensure that SUPDrvA-os2.asm is first in the link.
+ LIBRARIES += VBoxDrvLib
+ VBoxDrvLib_TEMPLATE = VBoxR0Drv
+ VBoxDrvLib_INSTTYPE = none
+ VBoxDrvLib_DEFS = IN_RT_R0 IN_SUP_R0
+ VBoxDrvLib_INCS := \
+ . \
+ $(PATH_ROOT)/src/VBox/Runtime/include
+ VBoxDrvLib_SOURCES = \
+ os2/SUPDrv-os2.cpp \
+ SUPDrv.cpp \
+ SUPDrvGip.cpp \
+ SUPDrvSem.cpp \
+ SUPLibAll.cpp
+
+ endif # os2
+
+
+ #
+ # New VBoxDrv target. TODO: Convert all the above to use this!
+ #
+ if1of ($(KBUILD_TARGET), darwin freebsd solaris win)
+ ifdef VBOX_WITH_VBOXDRV
+ SYSMODS += VBoxDrv
+ endif
+ VBoxDrv_TEMPLATE = VBoxR0Drv
+ VBoxDrv_NAME.freebsd = vboxdrv
+ VBoxDrv_NAME.solaris = vboxdrv
+ VBoxDrv_NAME.win = VBoxSup
+ ifdef VBOX_SIGNING_MODE
+ VBoxDrv_INSTTYPE.win = none
+ VBoxDrv_DEBUG_INSTTYPE.win = both
+ endif
+ VBoxDrv_INST.darwin = $(INST_VBOXDRV)Contents/MacOS/
+ VBoxDrv_DEBUG_INST.darwin = $(patsubst %/,%,$(INST_VBOXDRV))
+ VBoxDrv_SDKS.win = ReorderCompilerIncs $(VBOX_WINDDK) $(VBOX_WINPSDK_INCS)
+
+ VBoxDrv_DEFS := IN_RT_R0 IN_SUP_R0 SUPDRV_WITH_RELEASE_LOGGER VBOX_SVN_REV=$(VBOX_SVN_REV)
+ ifdef VBOX_WITH_DTRACE_R0DRV
+ VBoxDrv_DEFS += VBOX_WITH_DTRACE VBOX_WITH_DTRACE_R0DRV
+ endif
+ ifdef VBOX_WITHOUT_DEBUGGER_CHECKS
+ VBoxDrv_DEFS += VBOX_WITHOUT_DEBUGGER_CHECKS
+ endif
+ ifdef VBOX_PERMIT_VISUAL_STUDIO_PROFILING
+ VBoxDrv_DEFS += VBOX_PERMIT_VISUAL_STUDIO_PROFILING
+ endif
+ VBoxDrv_DEFS += VBOX_PERMIT_MORE VBOX_PERMIT_EVEN_MORE
+ #VBoxDrv_DEFS.debug += DEBUG_DARWIN_GIP
+ VBoxDrv_DEFS.darwin := VBOX_WITH_HOST_VMX
+ ifdef VBOX_WITH_RAW_MODE
+ VBoxDrv_DEFS.darwin += VBOX_WITH_RAW_MODE
+ endif
+ if defined(VBOX_WITH_DARWIN_R0_DARWIN_IMAGE_VERIFICATION) && defined(VBOX_SIGNING_MODE)
+ VBoxDrv_DEFS.darwin += VBOX_WITH_DARWIN_R0_DARWIN_IMAGE_VERIFICATION
+ ifeq ($(VBOX_SIGNING_MODE),test)
+ VBoxDrv_DEFS.darwin += VBOX_WITH_DARWIN_R0_TEST_SIGN
+ endif
+ endif
+ ifdef VBOX_WITH_NETFLT
+ VBoxDrv_DEFS.solaris += VBOX_WITH_NETFLT
+ endif
+ ifdef VBOX_WITH_NATIVE_SOLARIS_LOADING
+ VBoxDrv_DEFS.solaris += VBOX_WITH_NATIVE_SOLARIS_LOADING
+ endif
+ ifdef VBOX_WITHOUT_NATIVE_R0_LOADER
+ VBoxDrv_DEFS.win += VBOX_WITHOUT_NATIVE_R0_LOADER
+ endif
+ ifdef VBOX_WITH_VISTA_NO_SP
+ VBoxDrv_DEFS.win += VBOX_WITH_VISTA_NO_SP
+ endif
+ ifdef VBOX_WITH_HARDENING
+ VBoxDrv_ASDEFS += VBOX_WITH_HARDENING
+ endif
+ ifdef VBOX_WITH_RAM_IN_KERNEL
+ VBoxDrv_DEFS += VBOX_WITHOUT_EFLAGS_AC_SET_IN_VBOXDRV IPRT_WITHOUT_EFLAGS_AC_PRESERVING
+ else if ($(VBOX_VERSION_BUILD) % 2) == 1
+ VBoxDrv_DEFS += VBOX_WITH_EFLAGS_AC_SET_IN_VBOXDRV IPRT_WITH_EFLAGS_AC_PRESERVING
+ endif
+
+ VBoxDrv_INCS = . $(VBoxDrv_0_OUTDIR)
+ VBoxDrv_INCS.darwin = ./darwin
+
+ VBoxDrv_LIBS = $(PATH_STAGE_LIB)/RuntimeR0Drv$(VBOX_SUFF_LIB)
+ VBoxDrv_LIBS.win = \
+ $(PATH_STAGE_LIB)/RuntimeR0Drv$(VBOX_SUFF_LIB) \
+ $(PATH_SDK_$(VBOX_WINDDK)_LIB)/ntoskrnl.lib \
+ $(PATH_SDK_$(VBOX_WINDDK)_LIB)/hal.lib
+ ifn1of ($(VBOX_WINDDK), WINDDK80 WINDDK71)
+ VBoxDrv_LIBS.win.x86 = \
+ $(PATH_SDK_$(VBOX_WINDDK)_LIB)/BufferOverflowK.lib
+ endif
+
+ #VBoxDrv_LDFLAGS.darwin = -Wl,-sectcreate,__TEXT,__info_plist,$(VBoxDrv.kext_0_OUTDIR)/Info.plist
+ #VBoxDrv_LDFLAGS.darwin = -v -Wl,-whyload -Wl,-v -Wl,-whatsloaded
+ VBoxDrv_LDFLAGS.solaris += -N misc/ctf
+ ifdef VBOX_WITH_NATIVE_DTRACE
+ VBoxDrv_LDFLAGS.solaris += -N drv/dtrace
+ endif
+ VBoxDrv_LDFLAGS.win.x86 = -Entry:DriverEntry@8
+ VBoxDrv_LDFLAGS.win.amd64 = -Entry:DriverEntry
+
+ VBoxDrv_SOURCES.darwin = \
+ darwin/SUPDrv-darwin.cpp
+ ifdef VBOX_WITH_DARWIN_R0_DARWIN_IMAGE_VERIFICATION
+ VBoxDrv_SOURCES.darwin += \
+ $(VBOX_SUP_WIN_CERTS_FILE)
+ endif
+ VBoxDrv_SOURCES.solaris = \
+ solaris/SUPDrv-solaris.c
+ VBoxDrv_SOURCES.win = \
+ win/SUPDrv-win.cpp \
+ win/SUPDrvA-win.asm \
+ win/VBoxDrv.rc
+ ifdef VBOX_WITH_HARDENING
+ VBoxDrv_SOURCES.win += \
+ win/SUPHardenedVerifyImage-win.cpp \
+ win/SUPHardenedVerifyProcess-win.cpp \
+ $(VBOX_SUP_WIN_CERTS_FILE)
+ endif
+ VBoxDrv_SOURCES = \
+ SUPDrv.d \
+ SUPDrv.cpp \
+ SUPDrvGip.cpp \
+ SUPDrvSem.cpp \
+ SUPDrvTracer.cpp \
+ SUPLibAll.cpp
+ ifdef VBOX_WITH_NATIVE_DTRACE
+ VBoxDrv_SOURCES += \
+ SUPDrv-dtrace.cpp
+ SUPDrv-dtrace.cpp_DEFS.darwin += VBOX_PATH_MACOSX_DTRACE_H=\"$(VBOX_PATH_MACOSX_SDK)/usr/include/sys/dtrace.h\"
+ endif
+ ifn1of ($(KBUILD_TARGET), linux freebsd)
+ VBoxDrv_SOURCES += \
+ SUPDrvTracerA.asm
+ endif
+
+ linux/SUPDrv-linux.c_DEPS = $(VBOX_SVN_REV_HEADER)
+
+ endif
+
+
+
+ if defined(VBOX_WITH_VBOXDRV) && "$(KBUILD_TARGET)" == "darwin"
+ # Files necessary to make a darwin kernel extension bundle.
+ INSTALLS.darwin += VBoxDrv.kext
+ VBoxDrv.kext_INST = $(INST_VBOXDRV)Contents/
+ VBoxDrv.kext_SOURCES = $(VBoxDrv.kext_0_OUTDIR)/Contents/Info.plist
+ VBoxDrv.kext_CLEAN = $(VBoxDrv.kext_0_OUTDIR)/Contents/Info.plist
+ VBoxDrv.kext_BLDDIRS = $(VBoxDrv.kext_0_OUTDIR)/Contents/
+
+ $$(VBoxDrv.kext_0_OUTDIR)/Contents/Info.plist: \
+ $(PATH_SUB_CURRENT)/darwin/Info.plist \
+ $(VBOX_VERSION_MK) | $$(dir $$@)
+ $(call MSG_GENERATE,VBoxDrv,$@,$<)
+ $(QUIET)$(RM) -f $@
+ $(QUIET)$(SED) \
+ -e 's+@VBOX_VERSION_STRING@+$(if !defined(VBOX_MAVERICS_CODE_SIGNING_HACK),$(VBOX_VERSION_STRING),4.2.51)+g' \
+ -e 's+@VBOX_VERSION_MAJOR@+$(if !defined(VBOX_MAVERICS_CODE_SIGNING_HACK),$(VBOX_VERSION_MAJOR),4)+g' \
+ -e 's+@VBOX_VERSION_MINOR@+$(if !defined(VBOX_MAVERICS_CODE_SIGNING_HACK),$(VBOX_VERSION_MINOR),2)+g' \
+ -e 's+@VBOX_VERSION_BUILD@+$(if !defined(VBOX_MAVERICS_CODE_SIGNING_HACK),$(VBOX_VERSION_BUILD),51)+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,VBoxDrv)
+ endif
+
+
+ if1of ($(KBUILD_TARGET), darwin solaris)
+ ifdef VBOX_WITH_VBOXDRV
+ # Common manual loader script.
+ INSTALLS += SUPDrvScripts
+ SUPDrvScripts_INST = $(INST_DIST)
+ SUPDrvScripts_EXEC_SOURCES = \
+ $(KBUILD_TARGET)/load.sh
+ endif
+ endif
+
+
+ if1of ($(KBUILD_TARGET), linux freebsd)
+ if1of ($(KBUILD_TARGET_ARCH), $(VBOX_SUPPORTED_HOST_ARCHS))
+ #
+ # Targets for installing the linux sources.
+ #
+ vboxdrv-src_INST = bin/src/vboxdrv/
+ vboxdrv-src_SOURCES = \
+ $(subst $(DQUOTE),,$(FILES_VBOXDRV_NOBIN)) \
+ $(vboxdrv-src_0_OUTDIR)/Makefile
+ vboxdrv-src_EXEC_SOURCES = \
+ $(subst $(DQUOTE),,$(FILES_VBOXDRV_BIN))
+ vboxdrv-src_CLEAN = \
+ $(vboxdrv-src_0_OUTDIR)/Makefile \
+ $(PATH_TARGET)/vboxdrv-src-1.dep
+
+ # Scripts needed for building the kernel modules
+ includedep $(PATH_TARGET)/vboxdrv-src-1.dep
+ $$(vboxdrv-src_0_OUTDIR)/Makefile: \
+ $(PATH_SUB_CURRENT)/$(KBUILD_TARGET)/Makefile \
+ $$(if $$(eq $$(Support/$(KBUILD_TARGET)/Makefile_VBOX_HARDENED),$$(VBOX_WITH_HARDENING)),,FORCE) \
+ $$(if $$(eq $$(Support/$(KBUILD_TARGET)/Makefile_VBOX_RAM_IN_KERNEL),$$(VBOX_WITH_RAM_IN_KERNEL)),,FORCE) \
+ | $$(dir $$@)
+ $(call MSG_TOOL,Creating,,$@)
+ $(QUIET)$(SED) -e "" \
+ $(if-expr !defined(VBOX_WITH_HARDENING),-e "s;VBOX_WITH_HARDENING;;g",) \
+ --output $@ $<
+ %$(QUIET2)$(APPEND) -t -n '$(PATH_TARGET)/vboxdrv-src-1.dep' \
+ 'Support/$(KBUILD_TARGET)/Makefile_VBOX_HARDENED=$(VBOX_WITH_HARDENING)' \
+ 'Support/$(KBUILD_TARGET)/Makefile_VBOX_RAM_IN_KERNEL=$(VBOX_WITH_RAM_IN_KERNEL)'
+
+ #
+ # Build test for the linux host kernel modules.
+ #
+ $(evalcall2 VBOX_LINUX_KMOD_TEST_BUILD_RULE_FN,vboxdrv-src,,save_symvers)
+
+ ifdef VBOX_WITH_KMOD_WRAPPED_R0_MODS
+ #
+ # Common wrapper module files.
+ #
+ INSTALLS.linux += vboxwrappermod-common-src
+ vboxwrappermod-common-src_INST = bin/src/common/
+ vboxwrappermod-common-src_SOURCES = \
+ linux/SUPWrapperMod-linux.c=>SUPWrapperMod-linux.c \
+ linux/Makefile-wrapper.gmk=>Makefile-wrapper.gmk \
+ $(PATH_ROOT)/src/VBox/Installer/linux/Makefile-header.gmk=>Makefile-header.gmk \
+ $(PATH_ROOT)/src/VBox/Installer/linux/Makefile-footer.gmk=>Makefile-footer.gmk
+ endif
+
+ endif # supported host arch
+ endif # linux freebsd
+
+
+ ifeq ($(KBUILD_TARGET),win)
+ INSTALLS.win += VBoxSup-inf
+ VBoxSup-inf_TEMPLATE = VBoxR0DrvInfCat
+ VBoxSup-inf_SOURCES = \
+ $(PATH_TARGET)/VBoxSupCat.dir/VBoxSup.inf
+ VBoxSup-inf_CLEAN = $(VBoxSup-inf_SOURCES)
+ VBoxSup-inf_BLDDIRS = $(PATH_TARGET)/VBoxSupCat.dir
+
+ $(PATH_TARGET)/VBoxSupCat.dir/VBoxSup.inf: $(PATH_SUB_CURRENT)/win/VBoxSup.inf $(MAKEFILE_CURRENT) | $$(dir $$@)
+ $(call MSG_GENERATE,VBoxSup-inf,$@,$<)
+ $(call VBOX_EDIT_INF_FN,$<,$@)
+
+ ifdef VBOX_SIGNING_MODE
+ VBoxSup-inf_SOURCES += \
+ $(PATH_TARGET)/VBoxSupCat.dir/VBoxSup.sys \
+ $(PATH_TARGET)/VBoxSupCat.dir/VBoxSup.cat \
+ $(PATH_TARGET)/VBoxSupCat.dir/VBoxSup.cat=>VBoxSup-PreW10.cat
+
+ $(PATH_TARGET)/VBoxSupCat.dir/VBoxSup.sys: $$(VBoxDrv_1_TARGET) | $$(dir $$@)
+ $(INSTALL) -m 644 -- "$<" "$(@D)"
+
+ $(PATH_TARGET)/VBoxSupCat.dir/VBoxSup.cat: \
+ $(PATH_TARGET)/VBoxSupCat.dir/VBoxSup.inf \
+ $(PATH_TARGET)/VBoxSupCat.dir/VBoxSup.sys
+ $(call MSG_TOOL,Inf2Cat,VBoxSup-inf,$@,$<)
+ $(call VBOX_MAKE_CAT_FN, $(@D),$@)
+ endif # signing
+ endif # win
+
+ #
+ # Linux only.
+ #
+ PROGRAMS.linux += LnxPerfHack
+ LnxPerfHack_TEMPLATE = VBoxR3Tool
+ LnxPerfHack_SOURCES = linux/LnxPerfHack.cpp
+
+
+
+endif # !VBOX_ONLY_DOCS && !VBOX_ONLY_EXTPACKS && !VBOX_ONLY_VALIDATIONKIT
+include $(FILE_KBUILD_SUB_FOOTER)
+
diff --git a/src/VBox/HostDrivers/Support/SUPDrv-dtrace.cpp b/src/VBox/HostDrivers/Support/SUPDrv-dtrace.cpp
new file mode 100644
index 00000000..e33f244a
--- /dev/null
+++ b/src/VBox/HostDrivers/Support/SUPDrv-dtrace.cpp
@@ -0,0 +1,1201 @@
+/* $Id: SUPDrv-dtrace.cpp $ */
+/** @file
+ * VBoxDrv - The VirtualBox Support Driver - DTrace Provider.
+ */
+
+/*
+ * Copyright (C) 2012-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#define LOG_GROUP LOG_GROUP_SUP_DRV
+#include "SUPDrvInternal.h"
+
+#include <VBox/err.h>
+#include <VBox/log.h>
+#include <VBox/VBoxTpG.h>
+
+#include <iprt/assert.h>
+#include <iprt/ctype.h>
+#include <iprt/mem.h>
+#include <iprt/errno.h>
+#ifdef RT_OS_DARWIN
+# include <iprt/dbg.h>
+#endif
+
+#ifdef RT_OS_DARWIN
+# include VBOX_PATH_MACOSX_DTRACE_H
+#elif defined(RT_OS_LINUX)
+/* Avoid type and define conflicts. */
+# undef UINT8_MAX
+# undef UINT16_MAX
+# undef UINT32_MAX
+# undef UINT64_MAX
+# undef INT64_MAX
+# undef INT64_MIN
+# define intptr_t dtrace_intptr_t
+
+# if 0
+/* DTrace experiments with the Unbreakable Enterprise Kernel (UEK2)
+ (Oracle Linux).
+ 1. The dtrace.h here is from the dtrace module source, not
+ /usr/include/sys/dtrace.h nor /usr/include/dtrace.h.
+ 2. To generate the missing entries for the dtrace module in Module.symvers
+ of UEK:
+ nm /lib/modules/....../kernel/drivers/dtrace/dtrace.ko \
+ | grep _crc_ \
+ | sed -e 's/^......../0x/' -e 's/ A __crc_/\t/' \
+ -e 's/$/\tdrivers\/dtrace\/dtrace\tEXPORT_SYMBOL/' \
+ >> Module.symvers
+ Update: Althernative workaround (active), resolve symbols dynamically.
+ 3. No tracepoints in vboxdrv, vboxnet* or vboxpci yet. This requires yasm
+ and VBoxTpG and build time. */
+# include "dtrace.h"
+# else
+/* DTrace experiments with the Unbreakable Enterprise Kernel (UEKR3)
+ (Oracle Linux).
+ 1. To generate the missing entries for the dtrace module in Module.symvers
+ of UEK:
+ nm /lib/modules/....../kernel/drivers/dtrace/dtrace.ko \
+ | grep _crc_ \
+ | sed -e 's/^......../0x/' -e 's/ A __crc_/\t/' \
+ -e 's/$/\tdrivers\/dtrace\/dtrace\tEXPORT_SYMBOL/' \
+ >> Module.symvers
+ Update: Althernative workaround (active), resolve symbols dynamically.
+ 2. No tracepoints in vboxdrv, vboxnet* or vboxpci yet. This requires yasm
+ and VBoxTpG and build time. */
+# include <dtrace/provider.h>
+# include <dtrace/enabling.h> /* Missing from provider.h. */
+# include <dtrace/arg.h> /* Missing from provider.h. */
+# endif
+# include <linux/module.h>
+/** Status code fixer (UEK uses linux convension unlike the others). */
+# define FIX_UEK_RC(a_rc) (-(a_rc))
+#else
+# include <sys/dtrace.h>
+# undef u
+#endif
+
+
+/**
+ * The UEK DTrace port is trying to be smart and seems to have turned all
+ * errno return codes negative. While this conforms to the linux kernel way of
+ * doing things, it breaks with the way the interfaces work on Solaris and
+ * Mac OS X.
+ */
+#ifndef FIX_UEK_RC
+# define FIX_UEK_RC(a_rc) (a_rc)
+#endif
+
+
+/** @name Macros for preserving EFLAGS.AC (despair / paranoid)
+ * @remarks We have to restore it unconditionally on darwin.
+ * @{ */
+#if defined(RT_OS_DARWIN) \
+ || ( defined(RT_OS_LINUX) \
+ && (defined(CONFIG_X86_SMAP) || defined(RT_STRICT) || defined(IPRT_WITH_EFLAGS_AC_PRESERVING) ) )
+# include <iprt/asm-amd64-x86.h>
+# include <iprt/x86.h>
+# define SUPDRV_SAVE_EFL_AC() RTCCUINTREG const fSavedEfl = ASMGetFlags();
+# define SUPDRV_RESTORE_EFL_AC() ASMSetFlags(fSavedEfl)
+# define SUPDRV_RESTORE_EFL_ONLY_AC() ASMChangeFlags(~X86_EFL_AC, fSavedEfl & X86_EFL_AC)
+#else
+# define SUPDRV_SAVE_EFL_AC() do { } while (0)
+# define SUPDRV_RESTORE_EFL_AC() do { } while (0)
+# define SUPDRV_RESTORE_EFL_ONLY_AC() do { } while (0)
+#endif
+/** @} */
+
+
+/*********************************************************************************************************************************
+* Structures and Typedefs *
+*********************************************************************************************************************************/
+/* Seems there is some return code difference here. Keep the return code and
+ case it to whatever the host desires. */
+#ifdef RT_OS_DARWIN
+# if MAC_OS_X_VERSION_MIN_REQUIRED < 1070
+typedef void FNPOPS_ENABLE(void *, dtrace_id_t, void *);
+# else
+typedef int FNPOPS_ENABLE(void *, dtrace_id_t, void *);
+# endif
+#else
+# if !defined(RT_OS_SOLARIS) || defined(DOF_SEC_ISLOADABLE) /* DOF_SEC_ISLOADABLE was added in the next commit after dtps_enable change signature, but it's the simplest check we can do. */
+typedef int FNPOPS_ENABLE(void *, dtrace_id_t, void *);
+# else
+typedef void FNPOPS_ENABLE(void *, dtrace_id_t, void *);
+# endif
+#endif
+
+/** Caller indicator. */
+typedef enum VBOXDTCALLER
+{
+ kVBoxDtCaller_Invalid = 0,
+ kVBoxDtCaller_Generic,
+ kVBoxDtCaller_ProbeFireUser,
+ kVBoxDtCaller_ProbeFireKernel
+} VBOXDTCALLER;
+
+
+/**
+ * Stack data planted before calling dtrace_probe so that we can easily find the
+ * stack argument later.
+ */
+typedef struct VBDTSTACKDATA
+{
+ /** Eyecatcher no. 1 (SUPDRVDT_STACK_DATA_MAGIC2). */
+ uint32_t u32Magic1;
+ /** Eyecatcher no. 2 (SUPDRVDT_STACK_DATA_MAGIC2). */
+ uint32_t u32Magic2;
+ /** The format of the caller specific data. */
+ VBOXDTCALLER enmCaller;
+ /** Caller specific data. */
+ union
+ {
+ /** kVBoxDtCaller_ProbeFireKernel. */
+ struct
+ {
+ /** Pointer to the stack arguments of a probe function call. */
+ uintptr_t *pauStackArgs;
+ } ProbeFireKernel;
+ /** kVBoxDtCaller_ProbeFireUser. */
+ struct
+ {
+ /** The user context. */
+ PCSUPDRVTRACERUSRCTX pCtx;
+ /** The argument displacement caused by 64-bit arguments passed directly to
+ * dtrace_probe. */
+ int offArg;
+ } ProbeFireUser;
+ } u;
+ /** Pointer to this structure.
+ * This is the final bit of integrity checking. */
+ struct VBDTSTACKDATA *pSelf;
+} VBDTSTACKDATA;
+/** Pointer to the on-stack thread specific data. */
+typedef VBDTSTACKDATA *PVBDTSTACKDATA;
+
+
+/*********************************************************************************************************************************
+* Defined Constants And Macros *
+*********************************************************************************************************************************/
+/** The first magic value. */
+#define SUPDRVDT_STACK_DATA_MAGIC1 RT_MAKE_U32_FROM_U8('S', 'U', 'P', 'D')
+/** The second magic value. */
+#define SUPDRVDT_STACK_DATA_MAGIC2 RT_MAKE_U32_FROM_U8('D', 'T', 'r', 'c')
+
+/** The alignment of the stack data.
+ * The data doesn't require more than sizeof(uintptr_t) alignment, but the
+ * greater alignment the quicker lookup. */
+#define SUPDRVDT_STACK_DATA_ALIGN 32
+
+/** Plants the stack data. */
+#define VBDT_SETUP_STACK_DATA(a_enmCaller) \
+ uint8_t abBlob[sizeof(VBDTSTACKDATA) + SUPDRVDT_STACK_DATA_ALIGN - 1]; \
+ PVBDTSTACKDATA pStackData = (PVBDTSTACKDATA)( (uintptr_t)&abBlob[SUPDRVDT_STACK_DATA_ALIGN - 1] \
+ & ~(uintptr_t)(SUPDRVDT_STACK_DATA_ALIGN - 1)); \
+ pStackData->u32Magic1 = SUPDRVDT_STACK_DATA_MAGIC1; \
+ pStackData->u32Magic2 = SUPDRVDT_STACK_DATA_MAGIC2; \
+ pStackData->enmCaller = a_enmCaller; \
+ pStackData->pSelf = pStackData
+
+/** Passifies the stack data and frees up resource held within it. */
+#define VBDT_CLEAR_STACK_DATA() \
+ do \
+ { \
+ pStackData->u32Magic1 = 0; \
+ pStackData->u32Magic2 = 0; \
+ pStackData->pSelf = NULL; \
+ } while (0)
+
+/** Simple SUPR0Printf-style logging. */
+#if 0 /*def DEBUG_bird*/
+# define LOG_DTRACE(a) SUPR0Printf a
+#else
+# define LOG_DTRACE(a) do { } while (0)
+#endif
+
+
+/*********************************************************************************************************************************
+* Global Variables *
+*********************************************************************************************************************************/
+#if defined(RT_OS_DARWIN) || defined(RT_OS_LINUX)
+/** @name DTrace kernel interface used on Darwin and Linux.
+ * @{ */
+static DECLCALLBACKPTR_EX(void, RT_NOTHING, g_pfnDTraceProbeFire,(dtrace_id_t, uint64_t, uint64_t, uint64_t, uint64_t,
+ uint64_t));
+static DECLCALLBACKPTR_EX(dtrace_id_t, RT_NOTHING, g_pfnDTraceProbeCreate,(dtrace_provider_id_t, const char *, const char *,
+ const char *, int, void *));
+static DECLCALLBACKPTR_EX(dtrace_id_t, RT_NOTHING, g_pfnDTraceProbeLookup,(dtrace_provider_id_t, const char *, const char *,
+ const char *));
+static DECLCALLBACKPTR_EX(int, RT_NOTHING, g_pfnDTraceProviderRegister,(const char *, const dtrace_pattr_t *, uint32_t,
+ /*cred_t*/ void *, const dtrace_pops_t *,
+ void *, dtrace_provider_id_t *));
+static DECLCALLBACKPTR_EX(void, RT_NOTHING, g_pfnDTraceProviderInvalidate,(dtrace_provider_id_t));
+static DECLCALLBACKPTR_EX(int, RT_NOTHING, g_pfnDTraceProviderUnregister,(dtrace_provider_id_t));
+
+#define dtrace_probe g_pfnDTraceProbeFire
+#define dtrace_probe_create g_pfnDTraceProbeCreate
+#define dtrace_probe_lookup g_pfnDTraceProbeLookup
+#define dtrace_register g_pfnDTraceProviderRegister
+#define dtrace_invalidate g_pfnDTraceProviderInvalidate
+#define dtrace_unregister g_pfnDTraceProviderUnregister
+
+/** For dynamical resolving and releasing. */
+static const struct
+{
+ const char *pszName;
+ uintptr_t *ppfn; /**< @note Clang 11 nothrow weirdness forced this from PFNRT * to uintptr_t *. */
+} g_aDTraceFunctions[] =
+{
+ { "dtrace_probe", (uintptr_t *)&dtrace_probe },
+ { "dtrace_probe_create", (uintptr_t *)&dtrace_probe_create },
+ { "dtrace_probe_lookup", (uintptr_t *)&dtrace_probe_lookup },
+ { "dtrace_register", (uintptr_t *)&dtrace_register },
+ { "dtrace_invalidate", (uintptr_t *)&dtrace_invalidate },
+ { "dtrace_unregister", (uintptr_t *)&dtrace_unregister },
+};
+
+/** @} */
+#endif
+
+
+/**
+ * Gets the stack data.
+ *
+ * @returns Pointer to the stack data. Never NULL.
+ */
+static PVBDTSTACKDATA vboxDtGetStackData(void)
+{
+ /*
+ * Locate the caller of probe_dtrace.
+ */
+ int volatile iDummy = 1; /* use this to get the stack address. */
+ PVBDTSTACKDATA pData = (PVBDTSTACKDATA)( ((uintptr_t)&iDummy + SUPDRVDT_STACK_DATA_ALIGN - 1)
+ & ~(uintptr_t)(SUPDRVDT_STACK_DATA_ALIGN - 1));
+ for (;;)
+ {
+ if ( pData->u32Magic1 == SUPDRVDT_STACK_DATA_MAGIC1
+ && pData->u32Magic2 == SUPDRVDT_STACK_DATA_MAGIC2
+ && pData->pSelf == pData)
+ return pData;
+ pData = (PVBDTSTACKDATA)((uintptr_t)pData + SUPDRVDT_STACK_DATA_ALIGN);
+ }
+}
+
+
+/*
+ *
+ * Helpers for handling VTG structures.
+ * Helpers for handling VTG structures.
+ * Helpers for handling VTG structures.
+ *
+ */
+
+
+
+/**
+ * Converts an attribute from VTG description speak to DTrace.
+ *
+ * @param pDtAttr The DTrace attribute (dst).
+ * @param pVtgAttr The VTG attribute descriptor (src).
+ */
+static void vboxDtVtgConvAttr(dtrace_attribute_t *pDtAttr, PCVTGDESCATTR pVtgAttr)
+{
+ pDtAttr->dtat_name = pVtgAttr->u8Code - 1;
+ pDtAttr->dtat_data = pVtgAttr->u8Data - 1;
+ pDtAttr->dtat_class = pVtgAttr->u8DataDep - 1;
+}
+
+/**
+ * Gets a string from the string table.
+ *
+ * @returns Pointer to the string.
+ * @param pVtgHdr The VTG object header.
+ * @param offStrTab The string table offset.
+ */
+static const char *vboxDtVtgGetString(PVTGOBJHDR pVtgHdr, uint32_t offStrTab)
+{
+ Assert(offStrTab < pVtgHdr->cbStrTab);
+ return (const char *)pVtgHdr + pVtgHdr->offStrTab + offStrTab;
+}
+
+
+
+/*
+ *
+ * DTrace Provider Interface.
+ * DTrace Provider Interface.
+ * DTrace Provider Interface.
+ *
+ */
+
+
+/**
+ * @callback_method_impl{dtrace_pops_t,dtps_provide}
+ */
+static void vboxDtPOps_Provide(void *pvProv, const dtrace_probedesc_t *pDtProbeDesc)
+{
+ PSUPDRVVDTPROVIDERCORE pProv = (PSUPDRVVDTPROVIDERCORE)pvProv;
+ AssertPtrReturnVoid(pProv);
+ LOG_DTRACE(("%s: %p / %p pDtProbeDesc=%p\n", __FUNCTION__, pProv, pProv->TracerData.DTrace.idProvider, pDtProbeDesc));
+
+ if (pDtProbeDesc)
+ return; /* We don't generate probes, so never mind these requests. */
+
+ if (pProv->TracerData.DTrace.fZombie)
+ return;
+
+ dtrace_provider_id_t const idProvider = pProv->TracerData.DTrace.idProvider;
+ AssertPtrReturnVoid(idProvider);
+
+ AssertPtrReturnVoid(pProv->pHdr);
+ AssertReturnVoid(pProv->pHdr->offProbeLocs != 0);
+ uint32_t const cProbeLocs = pProv->pHdr->cbProbeLocs / sizeof(VTGPROBELOC);
+
+ /* Need a buffer for extracting the function names and mangling them in
+ case of collision. */
+ size_t const cbFnNmBuf = _4K + _1K;
+ char *pszFnNmBuf = (char *)RTMemAlloc(cbFnNmBuf);
+ if (!pszFnNmBuf)
+ return;
+
+ /*
+ * Itereate the probe location list and register all probes related to
+ * this provider.
+ */
+ uint16_t const idxProv = (uint16_t)((PVTGDESCPROVIDER)((uintptr_t)pProv->pHdr + pProv->pHdr->offProviders) - pProv->pDesc);
+ uint32_t idxProbeLoc;
+ for (idxProbeLoc = 0; idxProbeLoc < cProbeLocs; idxProbeLoc++)
+ {
+ /* Skip probe location belonging to other providers or once that
+ we've already reported. */
+ PCVTGPROBELOC pProbeLocRO = &pProv->paProbeLocsRO[idxProbeLoc];
+ PVTGDESCPROBE pProbeDesc = pProbeLocRO->pProbe;
+ if (pProbeDesc->idxProvider != idxProv)
+ continue;
+
+ uint32_t *pidProbe;
+ if (!pProv->fUmod)
+ pidProbe = (uint32_t *)&pProbeLocRO->idProbe;
+ else
+ pidProbe = &pProv->paR0ProbeLocs[idxProbeLoc].idProbe;
+ if (*pidProbe != 0)
+ continue;
+
+ /* The function name may need to be stripped since we're using C++
+ compilers for most of the code. ASSUMES nobody are brave/stupid
+ enough to use function pointer returns without typedef'ing
+ properly them (e.g. signal). */
+ const char *pszPrbName = vboxDtVtgGetString(pProv->pHdr, pProbeDesc->offName);
+ const char *pszFunc = pProbeLocRO->pszFunction;
+ const char *psz = strchr(pProbeLocRO->pszFunction, '(');
+ size_t cch;
+ if (psz)
+ {
+ /* skip blanks preceeding the parameter parenthesis. */
+ while ( (uintptr_t)psz > (uintptr_t)pProbeLocRO->pszFunction
+ && RT_C_IS_BLANK(psz[-1]))
+ psz--;
+
+ /* Find the start of the function name. */
+ pszFunc = psz - 1;
+ while ((uintptr_t)pszFunc > (uintptr_t)pProbeLocRO->pszFunction)
+ {
+ char ch = pszFunc[-1];
+ if (!RT_C_IS_ALNUM(ch) && ch != '_' && ch != ':')
+ break;
+ pszFunc--;
+ }
+ cch = psz - pszFunc;
+ }
+ else
+ cch = strlen(pszFunc);
+ RTStrCopyEx(pszFnNmBuf, cbFnNmBuf, pszFunc, cch);
+
+ /* Look up the probe, if we have one in the same function, mangle
+ the function name a little to avoid having to deal with having
+ multiple location entries with the same probe ID. (lazy bird) */
+ Assert(!*pidProbe);
+ if (dtrace_probe_lookup(idProvider, pProv->pszModName, pszFnNmBuf, pszPrbName) != DTRACE_IDNONE)
+ {
+ RTStrPrintf(pszFnNmBuf+cch, cbFnNmBuf - cch, "-%u", pProbeLocRO->uLine);
+ if (dtrace_probe_lookup(idProvider, pProv->pszModName, pszFnNmBuf, pszPrbName) != DTRACE_IDNONE)
+ {
+ unsigned iOrd = 2;
+ while (iOrd < 128)
+ {
+ RTStrPrintf(pszFnNmBuf+cch, cbFnNmBuf - cch, "-%u-%u", pProbeLocRO->uLine, iOrd);
+ if (dtrace_probe_lookup(idProvider, pProv->pszModName, pszFnNmBuf, pszPrbName) == DTRACE_IDNONE)
+ break;
+ iOrd++;
+ }
+ if (iOrd >= 128)
+ {
+ LogRel(("VBoxDrv: More than 128 duplicate probe location instances %s at line %u in function %s [%s], probe %s\n",
+ pProbeLocRO->uLine, pProbeLocRO->pszFunction, pszFnNmBuf, pszPrbName));
+ continue;
+ }
+ }
+ }
+
+ /* Create the probe. */
+ AssertCompile(sizeof(*pidProbe) == sizeof(dtrace_id_t));
+ *pidProbe = dtrace_probe_create(idProvider, pProv->pszModName, pszFnNmBuf, pszPrbName,
+ 1 /*aframes*/, (void *)(uintptr_t)idxProbeLoc);
+ pProv->TracerData.DTrace.cProvidedProbes++;
+ }
+
+ RTMemFree(pszFnNmBuf);
+ LOG_DTRACE(("%s: returns\n", __FUNCTION__));
+}
+
+
+/**
+ * @callback_method_impl{dtrace_pops_t,dtps_enable}
+ */
+static int vboxDtPOps_Enable(void *pvProv, dtrace_id_t idProbe, void *pvProbe)
+{
+ PSUPDRVVDTPROVIDERCORE pProv = (PSUPDRVVDTPROVIDERCORE)pvProv;
+ RT_NOREF(idProbe);
+ LOG_DTRACE(("%s: %p / %p - %#x / %p\n", __FUNCTION__, pProv, pProv->TracerData.DTrace.idProvider, idProbe, pvProbe));
+ AssertPtrReturn(pProv->TracerData.DTrace.idProvider, EINVAL);
+
+ if (!pProv->TracerData.DTrace.fZombie)
+ {
+ uint32_t idxProbeLoc = (uint32_t)(uintptr_t)pvProbe;
+ PVTGPROBELOC32 pProbeLocEn = (PVTGPROBELOC32)( (uintptr_t)pProv->pvProbeLocsEn + idxProbeLoc * pProv->cbProbeLocsEn);
+ PCVTGPROBELOC pProbeLocRO = (PVTGPROBELOC)&pProv->paProbeLocsRO[idxProbeLoc];
+ PCVTGDESCPROBE pProbeDesc = pProbeLocRO->pProbe;
+ uint32_t const idxProbe = pProbeDesc->idxEnabled;
+
+ if (!pProv->fUmod)
+ {
+ if (!pProbeLocEn->fEnabled)
+ {
+ pProbeLocEn->fEnabled = 1;
+ ASMAtomicIncU32(&pProv->pacProbeEnabled[idxProbe]);
+ ASMAtomicIncU32(&pProv->pDesc->cProbesEnabled);
+ ASMAtomicIncU32(&pProv->pDesc->uSettingsSerialNo);
+ }
+ }
+ else
+ {
+ /* Update kernel mode structure */
+ if (!pProv->paR0ProbeLocs[idxProbeLoc].fEnabled)
+ {
+ pProv->paR0ProbeLocs[idxProbeLoc].fEnabled = 1;
+ ASMAtomicIncU32(&pProv->paR0Probes[idxProbe].cEnabled);
+ ASMAtomicIncU32(&pProv->pDesc->cProbesEnabled);
+ ASMAtomicIncU32(&pProv->pDesc->uSettingsSerialNo);
+ }
+
+ /* Update user mode structure. */
+ pProbeLocEn->fEnabled = 1;
+ pProv->pacProbeEnabled[idxProbe] = pProv->paR0Probes[idxProbe].cEnabled;
+ }
+ }
+
+ return 0;
+}
+
+
+/**
+ * @callback_method_impl{dtrace_pops_t,dtps_disable}
+ */
+static void vboxDtPOps_Disable(void *pvProv, dtrace_id_t idProbe, void *pvProbe)
+{
+ PSUPDRVVDTPROVIDERCORE pProv = (PSUPDRVVDTPROVIDERCORE)pvProv;
+ AssertPtrReturnVoid(pProv);
+ RT_NOREF(idProbe);
+ LOG_DTRACE(("%s: %p / %p - %#x / %p\n", __FUNCTION__, pProv, pProv->TracerData.DTrace.idProvider, idProbe, pvProbe));
+ AssertPtrReturnVoid(pProv->TracerData.DTrace.idProvider);
+
+ if (!pProv->TracerData.DTrace.fZombie)
+ {
+ uint32_t idxProbeLoc = (uint32_t)(uintptr_t)pvProbe;
+ PVTGPROBELOC32 pProbeLocEn = (PVTGPROBELOC32)( (uintptr_t)pProv->pvProbeLocsEn + idxProbeLoc * pProv->cbProbeLocsEn);
+ PCVTGPROBELOC pProbeLocRO = (PVTGPROBELOC)&pProv->paProbeLocsRO[idxProbeLoc];
+ PCVTGDESCPROBE pProbeDesc = pProbeLocRO->pProbe;
+ uint32_t const idxProbe = pProbeDesc->idxEnabled;
+
+ if (!pProv->fUmod)
+ {
+ if (pProbeLocEn->fEnabled)
+ {
+ pProbeLocEn->fEnabled = 0;
+ ASMAtomicDecU32(&pProv->pacProbeEnabled[idxProbe]);
+ ASMAtomicIncU32(&pProv->pDesc->cProbesEnabled);
+ ASMAtomicIncU32(&pProv->pDesc->uSettingsSerialNo);
+ }
+ }
+ else
+ {
+ /* Update kernel mode structure */
+ if (pProv->paR0ProbeLocs[idxProbeLoc].fEnabled)
+ {
+ pProv->paR0ProbeLocs[idxProbeLoc].fEnabled = 0;
+ ASMAtomicDecU32(&pProv->paR0Probes[idxProbe].cEnabled);
+ ASMAtomicDecU32(&pProv->pDesc->cProbesEnabled);
+ ASMAtomicIncU32(&pProv->pDesc->uSettingsSerialNo);
+ }
+
+ /* Update user mode structure. */
+ pProbeLocEn->fEnabled = 0;
+ pProv->pacProbeEnabled[idxProbe] = pProv->paR0Probes[idxProbe].cEnabled;
+ }
+ }
+}
+
+
+/**
+ * @callback_method_impl{dtrace_pops_t,dtps_getargdesc}
+ */
+static void vboxDtPOps_GetArgDesc(void *pvProv, dtrace_id_t idProbe, void *pvProbe,
+ dtrace_argdesc_t *pArgDesc)
+{
+ PSUPDRVVDTPROVIDERCORE pProv = (PSUPDRVVDTPROVIDERCORE)pvProv;
+ unsigned uArg = pArgDesc->dtargd_ndx;
+ RT_NOREF(idProbe);
+
+ pArgDesc->dtargd_ndx = DTRACE_ARGNONE;
+ AssertPtrReturnVoid(pProv);
+ LOG_DTRACE(("%s: %p / %p - %#x / %p uArg=%d\n", __FUNCTION__, pProv, pProv->TracerData.DTrace.idProvider, idProbe, pvProbe, uArg));
+ AssertPtrReturnVoid(pProv->TracerData.DTrace.idProvider);
+
+ if (!pProv->TracerData.DTrace.fZombie)
+ {
+ uint32_t idxProbeLoc = (uint32_t)(uintptr_t)pvProbe;
+ PCVTGPROBELOC pProbeLocRO = (PVTGPROBELOC)&pProv->paProbeLocsRO[idxProbeLoc];
+ PCVTGDESCPROBE pProbeDesc = pProbeLocRO->pProbe;
+ PCVTGDESCARGLIST pArgList = (PCVTGDESCARGLIST)( (uintptr_t)pProv->pHdr
+ + pProv->pHdr->offArgLists
+ + pProbeDesc->offArgList);
+ AssertReturnVoid(pProbeDesc->offArgList < pProv->pHdr->cbArgLists);
+
+ if (uArg < pArgList->cArgs)
+ {
+ const char *pszType = vboxDtVtgGetString(pProv->pHdr, pArgList->aArgs[uArg].offType);
+ size_t cchType = strlen(pszType);
+ if (cchType < sizeof(pArgDesc->dtargd_native))
+ {
+ memcpy(pArgDesc->dtargd_native, pszType, cchType + 1);
+ /** @todo mapping? */
+ pArgDesc->dtargd_ndx = uArg;
+ LOG_DTRACE(("%s: returns dtargd_native = %s\n", __FUNCTION__, pArgDesc->dtargd_native));
+ return;
+ }
+ }
+ }
+}
+
+
+/**
+ * @callback_method_impl{dtrace_pops_t,dtps_getargval}
+ *
+ *
+ * We just cook our own stuff here, using a stack marker for finding the
+ * required information. That's more reliable than subjecting oneself to the
+ * solaris bugs and 32-bit apple peculiarities.
+ *
+ *
+ * @remarks Solaris Bug
+ *
+ * dtrace_getarg on AMD64 has a different opinion about how to use the cFrames
+ * argument than dtrace_caller() and/or dtrace_getpcstack(), at least when the
+ * probe is fired by dtrace_probe() the way we do.
+ *
+ * Setting aframes to 1 when calling dtrace_probe_create gives me the right
+ * arguments, but the wrong 'caller'. Since I cannot do anything about
+ * 'caller', the only solution is this hack.
+ *
+ * Not sure why the Solaris guys hasn't seen this issue before, but maybe there
+ * isn't anyone using the default argument getter path for ring-0 dtrace_probe()
+ * calls, SDT surely isn't.
+ *
+ * @todo File a solaris bug on dtrace_probe() + dtrace_getarg().
+ *
+ *
+ * @remarks 32-bit XNU (Apple)
+ *
+ * The dtrace_probe arguments are 64-bit unsigned integers instead of uintptr_t,
+ * so we need to make an extra call.
+ *
+ */
+static uint64_t vboxDtPOps_GetArgVal(void *pvProv, dtrace_id_t idProbe, void *pvProbe,
+ int iArg, int cFrames)
+{
+ PSUPDRVVDTPROVIDERCORE pProv = (PSUPDRVVDTPROVIDERCORE)pvProv;
+ AssertPtrReturn(pProv, UINT64_MAX);
+ RT_NOREF(idProbe, cFrames);
+ LOG_DTRACE(("%s: %p / %p - %#x / %p iArg=%d cFrames=%u\n", __FUNCTION__, pProv, pProv->TracerData.DTrace.idProvider, idProbe, pvProbe, iArg, cFrames));
+ AssertReturn(iArg >= 5, UINT64_MAX);
+ if (pProv->TracerData.DTrace.fZombie)
+ return UINT64_MAX;
+
+ uint32_t idxProbeLoc = (uint32_t)(uintptr_t)pvProbe;
+ PCVTGPROBELOC pProbeLocRO = (PVTGPROBELOC)&pProv->paProbeLocsRO[idxProbeLoc];
+ PCVTGDESCPROBE pProbeDesc = pProbeLocRO->pProbe;
+ PCVTGDESCARGLIST pArgList = (PCVTGDESCARGLIST)( (uintptr_t)pProv->pHdr
+ + pProv->pHdr->offArgLists
+ + pProbeDesc->offArgList);
+ AssertReturn(pProbeDesc->offArgList < pProv->pHdr->cbArgLists, UINT64_MAX);
+
+ PVBDTSTACKDATA pData = vboxDtGetStackData();
+
+ /*
+ * Get the stack data. This is a wee bit complicated on 32-bit systems
+ * since we want to support 64-bit integer arguments.
+ */
+ uint64_t u64Ret;
+ if (iArg >= 20)
+ u64Ret = UINT64_MAX;
+ else if (pData->enmCaller == kVBoxDtCaller_ProbeFireKernel)
+ {
+#if ARCH_BITS == 64
+ u64Ret = pData->u.ProbeFireKernel.pauStackArgs[iArg - 5];
+#else
+ if ( !pArgList->fHaveLargeArgs
+ || iArg >= pArgList->cArgs)
+ u64Ret = pData->u.ProbeFireKernel.pauStackArgs[iArg - 5];
+ else
+ {
+ /* Similar to what we did for mac in when calling dtrace_probe(). */
+ uint32_t offArg = 0;
+ for (int i = 5; i < iArg; i++)
+ if (VTG_TYPE_IS_LARGE(pArgList->aArgs[iArg].fType))
+ offArg++;
+ u64Ret = pData->u.ProbeFireKernel.pauStackArgs[iArg - 5 + offArg];
+ if (VTG_TYPE_IS_LARGE(pArgList->aArgs[iArg].fType))
+ u64Ret |= (uint64_t)pData->u.ProbeFireKernel.pauStackArgs[iArg - 5 + offArg + 1] << 32;
+ }
+#endif
+ }
+ else if (pData->enmCaller == kVBoxDtCaller_ProbeFireUser)
+ {
+ int offArg = pData->u.ProbeFireUser.offArg;
+ PCSUPDRVTRACERUSRCTX pCtx = pData->u.ProbeFireUser.pCtx;
+ AssertPtrReturn(pCtx, UINT64_MAX);
+
+ if (pCtx->cBits == 32)
+ {
+ if ( !pArgList->fHaveLargeArgs
+ || iArg >= pArgList->cArgs)
+ {
+ if (iArg + offArg < (int)RT_ELEMENTS(pCtx->u.X86.aArgs))
+ u64Ret = pCtx->u.X86.aArgs[iArg + offArg];
+ else
+ u64Ret = UINT64_MAX;
+ }
+ else
+ {
+ int i;
+ for (i = 5; i < iArg; i++)
+ if (VTG_TYPE_IS_LARGE(pArgList->aArgs[iArg].fType))
+ offArg++;
+ if (offArg + iArg < (int)RT_ELEMENTS(pCtx->u.X86.aArgs))
+ {
+ u64Ret = pCtx->u.X86.aArgs[iArg + offArg];
+ if ( VTG_TYPE_IS_LARGE(pArgList->aArgs[iArg].fType)
+ && offArg + iArg + 1 < (int)RT_ELEMENTS(pCtx->u.X86.aArgs))
+ u64Ret |= (uint64_t)pCtx->u.X86.aArgs[iArg + offArg + 1] << 32;
+ }
+ else
+ u64Ret = UINT64_MAX;
+ }
+ }
+ else
+ {
+ if (iArg + offArg < (int)RT_ELEMENTS(pCtx->u.Amd64.aArgs))
+ u64Ret = pCtx->u.Amd64.aArgs[iArg + offArg];
+ else
+ u64Ret = UINT64_MAX;
+ }
+ }
+ else
+ AssertFailedReturn(UINT64_MAX);
+
+ LOG_DTRACE(("%s: returns %#llx\n", __FUNCTION__, u64Ret));
+ return u64Ret;
+}
+
+
+/**
+ * @callback_method_impl{dtrace_pops_t,dtps_destroy}
+ */
+static void vboxDtPOps_Destroy(void *pvProv, dtrace_id_t idProbe, void *pvProbe)
+{
+ PSUPDRVVDTPROVIDERCORE pProv = (PSUPDRVVDTPROVIDERCORE)pvProv;
+ AssertPtrReturnVoid(pProv);
+ LOG_DTRACE(("%s: %p / %p - %#x / %p\n", __FUNCTION__, pProv, pProv->TracerData.DTrace.idProvider, idProbe, pvProbe));
+ AssertReturnVoid(pProv->TracerData.DTrace.cProvidedProbes > 0);
+ AssertPtrReturnVoid(pProv->TracerData.DTrace.idProvider);
+
+ if (!pProv->TracerData.DTrace.fZombie)
+ {
+ uint32_t idxProbeLoc = (uint32_t)(uintptr_t)pvProbe;
+ PCVTGPROBELOC pProbeLocRO = (PVTGPROBELOC)&pProv->paProbeLocsRO[idxProbeLoc];
+ uint32_t *pidProbe;
+ if (!pProv->fUmod)
+ {
+ pidProbe = (uint32_t *)&pProbeLocRO->idProbe;
+ Assert(!pProbeLocRO->fEnabled);
+ Assert(*pidProbe == idProbe);
+ }
+ else
+ {
+ pidProbe = &pProv->paR0ProbeLocs[idxProbeLoc].idProbe;
+ Assert(!pProv->paR0ProbeLocs[idxProbeLoc].fEnabled);
+ Assert(*pidProbe == idProbe); NOREF(idProbe);
+ }
+ *pidProbe = 0;
+ }
+ pProv->TracerData.DTrace.cProvidedProbes--;
+}
+
+
+
+/**
+ * DTrace provider method table.
+ */
+static const dtrace_pops_t g_vboxDtVtgProvOps =
+{
+ /* .dtps_provide = */ vboxDtPOps_Provide,
+ /* .dtps_provide_module = */ NULL,
+ /* .dtps_enable = */ (FNPOPS_ENABLE *)vboxDtPOps_Enable,
+ /* .dtps_disable = */ vboxDtPOps_Disable,
+ /* .dtps_suspend = */ NULL,
+ /* .dtps_resume = */ NULL,
+ /* .dtps_getargdesc = */ vboxDtPOps_GetArgDesc,
+ /* .dtps_getargval = */ vboxDtPOps_GetArgVal,
+ /* .dtps_usermode = */ NULL,
+ /* .dtps_destroy = */ vboxDtPOps_Destroy
+};
+
+
+
+
+/*
+ *
+ * Support Driver Tracer Interface.
+ * Support Driver Tracer Interface.
+ * Support Driver Tracer Interface.
+ *
+ */
+
+
+
+/**
+ * interface_method_impl{SUPDRVTRACERREG,pfnProbeFireKernel}
+ */
+static DECLCALLBACK(void) vboxDtTOps_ProbeFireKernel(struct VTGPROBELOC *pVtgProbeLoc, uintptr_t uArg0, uintptr_t uArg1, uintptr_t uArg2,
+ uintptr_t uArg3, uintptr_t uArg4)
+{
+ AssertPtrReturnVoid(pVtgProbeLoc);
+ LOG_DTRACE(("%s: %p / %p\n", __FUNCTION__, pVtgProbeLoc, pVtgProbeLoc->idProbe));
+ AssertPtrReturnVoid(pVtgProbeLoc->pProbe);
+ AssertPtrReturnVoid(pVtgProbeLoc->pszFunction);
+
+ SUPDRV_SAVE_EFL_AC();
+ VBDT_SETUP_STACK_DATA(kVBoxDtCaller_ProbeFireKernel);
+
+ pStackData->u.ProbeFireKernel.pauStackArgs = &uArg4 + 1;
+
+#if defined(RT_OS_DARWIN) && ARCH_BITS == 32
+ /*
+ * Convert arguments from uintptr_t to uint64_t.
+ */
+ PVTGDESCPROBE pProbe = (PVTGDESCPROBE)((PVTGPROBELOC)pVtgProbeLoc)->pProbe;
+ AssertPtrReturnVoid(pProbe);
+ PVTGOBJHDR pVtgHdr = (PVTGOBJHDR)((uintptr_t)pProbe + pProbe->offObjHdr);
+ AssertPtrReturnVoid(pVtgHdr);
+ PVTGDESCARGLIST pArgList = (PVTGDESCARGLIST)((uintptr_t)pVtgHdr + pVtgHdr->offArgLists + pProbe->offArgList);
+ AssertPtrReturnVoid(pArgList);
+ if (!pArgList->fHaveLargeArgs)
+ dtrace_probe(pVtgProbeLoc->idProbe, uArg0, uArg1, uArg2, uArg3, uArg4);
+ else
+ {
+ uintptr_t *auSrcArgs = &uArg0;
+ uint32_t iSrcArg = 0;
+ uint32_t iDstArg = 0;
+ uint64_t au64DstArgs[5];
+
+ while ( iDstArg < RT_ELEMENTS(au64DstArgs)
+ && iSrcArg < pArgList->cArgs)
+ {
+ au64DstArgs[iDstArg] = auSrcArgs[iSrcArg];
+ if (VTG_TYPE_IS_LARGE(pArgList->aArgs[iDstArg].fType))
+ au64DstArgs[iDstArg] |= (uint64_t)auSrcArgs[++iSrcArg] << 32;
+ iSrcArg++;
+ iDstArg++;
+ }
+ while (iDstArg < RT_ELEMENTS(au64DstArgs))
+ au64DstArgs[iDstArg++] = auSrcArgs[iSrcArg++];
+
+ pStackData->u.ProbeFireKernel.pauStackArgs = &auSrcArgs[iSrcArg];
+ dtrace_probe(pVtgProbeLoc->idProbe, au64DstArgs[0], au64DstArgs[1], au64DstArgs[2], au64DstArgs[3], au64DstArgs[4]);
+ }
+#else
+ dtrace_probe(pVtgProbeLoc->idProbe, uArg0, uArg1, uArg2, uArg3, uArg4);
+#endif
+
+ VBDT_CLEAR_STACK_DATA();
+ SUPDRV_RESTORE_EFL_AC();
+ LOG_DTRACE(("%s: returns\n", __FUNCTION__));
+}
+
+
+/**
+ * interface_method_impl{SUPDRVTRACERREG,pfnProbeFireUser}
+ */
+static DECLCALLBACK(void) vboxDtTOps_ProbeFireUser(PCSUPDRVTRACERREG pThis, PSUPDRVSESSION pSession, PCSUPDRVTRACERUSRCTX pCtx,
+ PCVTGOBJHDR pVtgHdr, PCVTGPROBELOC pProbeLocRO)
+{
+ RT_NOREF(pThis, pSession);
+ LOG_DTRACE(("%s: %p / %p\n", __FUNCTION__, pCtx, pCtx->idProbe));
+ AssertPtrReturnVoid(pProbeLocRO);
+ AssertPtrReturnVoid(pVtgHdr);
+
+ SUPDRV_SAVE_EFL_AC();
+ VBDT_SETUP_STACK_DATA(kVBoxDtCaller_ProbeFireUser);
+
+ if (pCtx->cBits == 32)
+ {
+ pStackData->u.ProbeFireUser.pCtx = pCtx;
+ pStackData->u.ProbeFireUser.offArg = 0;
+
+#if ARCH_BITS == 64 || defined(RT_OS_DARWIN)
+ /*
+ * Combine two 32-bit arguments into one 64-bit argument where needed.
+ */
+ PVTGDESCPROBE pProbeDesc = pProbeLocRO->pProbe;
+ AssertPtrReturnVoid(pProbeDesc);
+ PVTGDESCARGLIST pArgList = (PVTGDESCARGLIST)((uintptr_t)pVtgHdr + pVtgHdr->offArgLists + pProbeDesc->offArgList);
+ AssertPtrReturnVoid(pArgList);
+
+ if (!pArgList->fHaveLargeArgs)
+ dtrace_probe(pCtx->idProbe,
+ pCtx->u.X86.aArgs[0],
+ pCtx->u.X86.aArgs[1],
+ pCtx->u.X86.aArgs[2],
+ pCtx->u.X86.aArgs[3],
+ pCtx->u.X86.aArgs[4]);
+ else
+ {
+ uint32_t const *auSrcArgs = &pCtx->u.X86.aArgs[0];
+ uint32_t iSrcArg = 0;
+ uint32_t iDstArg = 0;
+ uint64_t au64DstArgs[5];
+
+ while ( iDstArg < RT_ELEMENTS(au64DstArgs)
+ && iSrcArg < pArgList->cArgs)
+ {
+ au64DstArgs[iDstArg] = auSrcArgs[iSrcArg];
+ if (VTG_TYPE_IS_LARGE(pArgList->aArgs[iDstArg].fType))
+ au64DstArgs[iDstArg] |= (uint64_t)auSrcArgs[++iSrcArg] << 32;
+ iSrcArg++;
+ iDstArg++;
+ }
+ while (iDstArg < RT_ELEMENTS(au64DstArgs))
+ au64DstArgs[iDstArg++] = auSrcArgs[iSrcArg++];
+
+ pStackData->u.ProbeFireUser.offArg = iSrcArg - RT_ELEMENTS(au64DstArgs);
+ dtrace_probe(pCtx->idProbe, au64DstArgs[0], au64DstArgs[1], au64DstArgs[2], au64DstArgs[3], au64DstArgs[4]);
+ }
+#else
+ dtrace_probe(pCtx->idProbe,
+ pCtx->u.X86.aArgs[0],
+ pCtx->u.X86.aArgs[1],
+ pCtx->u.X86.aArgs[2],
+ pCtx->u.X86.aArgs[3],
+ pCtx->u.X86.aArgs[4]);
+#endif
+ }
+ else if (pCtx->cBits == 64)
+ {
+ pStackData->u.ProbeFireUser.pCtx = pCtx;
+ pStackData->u.ProbeFireUser.offArg = 0;
+ dtrace_probe(pCtx->idProbe,
+ pCtx->u.Amd64.aArgs[0],
+ pCtx->u.Amd64.aArgs[1],
+ pCtx->u.Amd64.aArgs[2],
+ pCtx->u.Amd64.aArgs[3],
+ pCtx->u.Amd64.aArgs[4]);
+ }
+ else
+ AssertFailed();
+
+ VBDT_CLEAR_STACK_DATA();
+ SUPDRV_RESTORE_EFL_AC();
+ LOG_DTRACE(("%s: returns\n", __FUNCTION__));
+}
+
+
+/**
+ * interface_method_impl{SUPDRVTRACERREG,pfnTracerOpen}
+ */
+static DECLCALLBACK(int) vboxDtTOps_TracerOpen(PCSUPDRVTRACERREG pThis, PSUPDRVSESSION pSession, uint32_t uCookie,
+ uintptr_t uArg, uintptr_t *puSessionData)
+{
+ NOREF(pThis); NOREF(pSession); NOREF(uCookie); NOREF(uArg);
+ *puSessionData = 0;
+ return VERR_NOT_SUPPORTED;
+}
+
+
+/**
+ * interface_method_impl{SUPDRVTRACERREG,pfnTracerClose}
+ */
+static DECLCALLBACK(int) vboxDtTOps_TracerIoCtl(PCSUPDRVTRACERREG pThis, PSUPDRVSESSION pSession, uintptr_t uSessionData,
+ uintptr_t uCmd, uintptr_t uArg, int32_t *piRetVal)
+{
+ NOREF(pThis); NOREF(pSession); NOREF(uSessionData);
+ NOREF(uCmd); NOREF(uArg); NOREF(piRetVal);
+ return VERR_NOT_SUPPORTED;
+}
+
+
+/**
+ * interface_method_impl{SUPDRVTRACERREG,pfnTracerClose}
+ */
+static DECLCALLBACK(void) vboxDtTOps_TracerClose(PCSUPDRVTRACERREG pThis, PSUPDRVSESSION pSession, uintptr_t uSessionData)
+{
+ NOREF(pThis); NOREF(pSession); NOREF(uSessionData);
+ return;
+}
+
+
+/**
+ * interface_method_impl{SUPDRVTRACERREG,pfnProviderRegister}
+ */
+static DECLCALLBACK(int) vboxDtTOps_ProviderRegister(PCSUPDRVTRACERREG pThis, PSUPDRVVDTPROVIDERCORE pCore)
+{
+ RT_NOREF(pThis);
+ LOG_DTRACE(("%s: %p %s/%s\n", __FUNCTION__, pThis, pCore->pszModName, pCore->pszName));
+ AssertReturn(pCore->TracerData.DTrace.idProvider == 0, VERR_INTERNAL_ERROR_3);
+
+ PVTGDESCPROVIDER pDesc = pCore->pDesc;
+ dtrace_pattr_t DtAttrs;
+ vboxDtVtgConvAttr(&DtAttrs.dtpa_provider, &pDesc->AttrSelf);
+ vboxDtVtgConvAttr(&DtAttrs.dtpa_mod, &pDesc->AttrModules);
+ vboxDtVtgConvAttr(&DtAttrs.dtpa_func, &pDesc->AttrFunctions);
+ vboxDtVtgConvAttr(&DtAttrs.dtpa_name, &pDesc->AttrNames);
+ vboxDtVtgConvAttr(&DtAttrs.dtpa_args, &pDesc->AttrArguments);
+
+ /* Note! DTrace may call us back before dtrace_register returns, so we
+ have to point it to pCore->TracerData.DTrace.idProvider. */
+ AssertCompile(sizeof(dtrace_provider_id_t) == sizeof(pCore->TracerData.DTrace.idProvider));
+ SUPDRV_SAVE_EFL_AC();
+ int rc = dtrace_register(pCore->pszName,
+ &DtAttrs,
+ DTRACE_PRIV_KERNEL,
+ NULL /* cred */,
+ &g_vboxDtVtgProvOps,
+ pCore,
+ &pCore->TracerData.DTrace.idProvider);
+ SUPDRV_RESTORE_EFL_AC();
+ if (!rc)
+ {
+ LOG_DTRACE(("%s: idProvider=%p\n", __FUNCTION__, pCore->TracerData.DTrace.idProvider));
+ AssertPtr(pCore->TracerData.DTrace.idProvider);
+ rc = VINF_SUCCESS;
+ }
+ else
+ {
+ pCore->TracerData.DTrace.idProvider = 0;
+ rc = RTErrConvertFromErrno(FIX_UEK_RC(rc));
+ }
+
+ LOG_DTRACE(("%s: returns %Rrc\n", __FUNCTION__, rc));
+ return rc;
+}
+
+
+/**
+ * interface_method_impl{SUPDRVTRACERREG,pfnProviderDeregister}
+ */
+static DECLCALLBACK(int) vboxDtTOps_ProviderDeregister(PCSUPDRVTRACERREG pThis, PSUPDRVVDTPROVIDERCORE pCore)
+{
+ uintptr_t idProvider = pCore->TracerData.DTrace.idProvider;
+ RT_NOREF(pThis);
+ LOG_DTRACE(("%s: %p / %p\n", __FUNCTION__, pThis, idProvider));
+ AssertPtrReturn(idProvider, VERR_INTERNAL_ERROR_3);
+
+ SUPDRV_SAVE_EFL_AC();
+ dtrace_invalidate(idProvider);
+ int rc = dtrace_unregister(idProvider);
+ SUPDRV_RESTORE_EFL_AC();
+ if (!rc)
+ {
+ pCore->TracerData.DTrace.idProvider = 0;
+ rc = VINF_SUCCESS;
+ }
+ else
+ {
+ AssertMsg(FIX_UEK_RC(rc) == EBUSY, ("%d\n", rc));
+ pCore->TracerData.DTrace.fZombie = true;
+ rc = VERR_TRY_AGAIN;
+ }
+
+ LOG_DTRACE(("%s: returns %Rrc\n", __FUNCTION__, rc));
+ return rc;
+}
+
+
+/**
+ * interface_method_impl{SUPDRVTRACERREG,pfnProviderDeregisterZombie}
+ */
+static DECLCALLBACK(int) vboxDtTOps_ProviderDeregisterZombie(PCSUPDRVTRACERREG pThis, PSUPDRVVDTPROVIDERCORE pCore)
+{
+ uintptr_t idProvider = pCore->TracerData.DTrace.idProvider;
+ RT_NOREF(pThis);
+ LOG_DTRACE(("%s: %p / %p\n", __FUNCTION__, pThis, idProvider));
+ AssertPtrReturn(idProvider, VERR_INTERNAL_ERROR_3);
+ Assert(pCore->TracerData.DTrace.fZombie);
+
+ SUPDRV_SAVE_EFL_AC();
+ int rc = dtrace_unregister(idProvider);
+ SUPDRV_RESTORE_EFL_AC();
+ if (!rc)
+ {
+ pCore->TracerData.DTrace.idProvider = 0;
+ rc = VINF_SUCCESS;
+ }
+ else
+ {
+ AssertMsg(FIX_UEK_RC(rc) == EBUSY, ("%d\n", rc));
+ rc = VERR_TRY_AGAIN;
+ }
+
+ LOG_DTRACE(("%s: returns %Rrc\n", __FUNCTION__, rc));
+ return rc;
+}
+
+
+
+/**
+ * The tracer registration record of the VBox DTrace implementation
+ */
+static SUPDRVTRACERREG g_VBoxDTraceReg =
+{
+ SUPDRVTRACERREG_MAGIC,
+ SUPDRVTRACERREG_VERSION,
+ vboxDtTOps_ProbeFireKernel,
+ vboxDtTOps_ProbeFireUser,
+ vboxDtTOps_TracerOpen,
+ vboxDtTOps_TracerIoCtl,
+ vboxDtTOps_TracerClose,
+ vboxDtTOps_ProviderRegister,
+ vboxDtTOps_ProviderDeregister,
+ vboxDtTOps_ProviderDeregisterZombie,
+ SUPDRVTRACERREG_MAGIC
+};
+
+
+
+/**
+ * Module initialization code.
+ */
+const SUPDRVTRACERREG * VBOXCALL supdrvDTraceInit(void)
+{
+#if defined(RT_OS_DARWIN) || defined(RT_OS_LINUX)
+ /*
+ * Resolve the kernel symbols we need.
+ */
+# ifndef RT_OS_LINUX
+ RTDBGKRNLINFO hKrnlInfo;
+ int rc = RTR0DbgKrnlInfoOpen(&hKrnlInfo, 0);
+ if (RT_FAILURE(rc))
+ {
+ SUPR0Printf("supdrvDTraceInit: RTR0DbgKrnlInfoOpen failed with rc=%d.\n", rc);
+ return NULL;
+ }
+# endif
+
+ unsigned i;
+ for (i = 0; i < RT_ELEMENTS(g_aDTraceFunctions); i++)
+ {
+# ifndef RT_OS_LINUX
+ rc = RTR0DbgKrnlInfoQuerySymbol(hKrnlInfo, NULL, g_aDTraceFunctions[i].pszName,
+ (void **)g_aDTraceFunctions[i].ppfn);
+ if (RT_FAILURE(rc))
+ {
+ SUPR0Printf("supdrvDTraceInit: Failed to resolved '%s' (rc=%Rrc, i=%u).\n", g_aDTraceFunctions[i].pszName, rc, i);
+ break;
+ }
+# else /* RT_OS_LINUX */
+ uintptr_t ulAddr = (uintptr_t)__symbol_get(g_aDTraceFunctions[i].pszName);
+ if (!ulAddr)
+ {
+ SUPR0Printf("supdrvDTraceInit: Failed to resolved '%s' (i=%u).\n", g_aDTraceFunctions[i].pszName, i);
+ while (i-- > 0)
+ {
+ __symbol_put(g_aDTraceFunctions[i].pszName);
+ *g_aDTraceFunctions[i].ppfn = NULL;
+ }
+ return NULL;
+ }
+ *g_aDTraceFunctions[i].ppfn = (PFNRT)ulAddr;
+# endif /* RT_OS_LINUX */
+ }
+
+# ifndef RT_OS_LINUX
+ RTR0DbgKrnlInfoRelease(hKrnlInfo);
+ if (RT_FAILURE(rc))
+ return NULL;
+# endif
+#endif
+
+ return &g_VBoxDTraceReg;
+}
+
+/**
+ * Module teardown code.
+ */
+void VBOXCALL supdrvDTraceFini(void)
+{
+#ifdef RT_OS_LINUX
+ /* Release the references. */
+ unsigned i;
+ for (i = 0; i < RT_ELEMENTS(g_aDTraceFunctions); i++)
+ if (*g_aDTraceFunctions[i].ppfn)
+ {
+ __symbol_put(g_aDTraceFunctions[i].pszName);
+ *g_aDTraceFunctions[i].ppfn = NULL;
+ }
+#endif
+}
+
+#ifndef VBOX_WITH_NATIVE_DTRACE
+# error "VBOX_WITH_NATIVE_DTRACE is not defined as it should"
+#endif
+
diff --git a/src/VBox/HostDrivers/Support/SUPDrv.cpp b/src/VBox/HostDrivers/Support/SUPDrv.cpp
new file mode 100644
index 00000000..59573a31
--- /dev/null
+++ b/src/VBox/HostDrivers/Support/SUPDrv.cpp
@@ -0,0 +1,7204 @@
+/* $Id: SUPDrv.cpp $ */
+/** @file
+ * VBoxDrv - The VirtualBox Support Driver - Common code.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#define LOG_GROUP LOG_GROUP_SUP_DRV
+#define SUPDRV_AGNOSTIC
+#include "SUPDrvInternal.h"
+#ifndef PAGE_SHIFT
+# include <iprt/param.h>
+#endif
+#include <iprt/asm.h>
+#include <iprt/asm-amd64-x86.h>
+#include <iprt/asm-math.h>
+#include <iprt/cpuset.h>
+#if defined(RT_OS_DARWIN) || defined(RT_OS_SOLARIS) || defined(RT_OS_WINDOWS)
+# include <iprt/dbg.h>
+#endif
+#include <iprt/handletable.h>
+#include <iprt/mem.h>
+#include <iprt/mp.h>
+#include <iprt/power.h>
+#include <iprt/process.h>
+#include <iprt/semaphore.h>
+#include <iprt/spinlock.h>
+#include <iprt/thread.h>
+#include <iprt/uuid.h>
+#include <iprt/net.h>
+#include <iprt/crc.h>
+#include <iprt/string.h>
+#include <iprt/timer.h>
+#if defined(RT_OS_DARWIN) || defined(RT_OS_SOLARIS) || defined(RT_OS_FREEBSD)
+# include <iprt/rand.h>
+# include <iprt/path.h>
+#endif
+#include <iprt/uint128.h>
+#include <iprt/x86.h>
+
+#include <VBox/param.h>
+#include <VBox/log.h>
+#include <VBox/err.h>
+#include <VBox/vmm/hm_vmx.h>
+
+#if defined(RT_OS_SOLARIS) || defined(RT_OS_DARWIN)
+# include "dtrace/SUPDrv.h"
+#else
+# define VBOXDRV_SESSION_CREATE(pvSession, fUser) do { } while (0)
+# define VBOXDRV_SESSION_CLOSE(pvSession) do { } while (0)
+# define VBOXDRV_IOCTL_ENTRY(pvSession, uIOCtl, pvReqHdr) do { } while (0)
+# define VBOXDRV_IOCTL_RETURN(pvSession, uIOCtl, pvReqHdr, rcRet, rcReq) do { } while (0)
+#endif
+
+#ifdef __cplusplus
+# if __cplusplus >= 201100 || RT_MSC_PREREQ(RT_MSC_VER_VS2019)
+# define SUPDRV_CAN_COUNT_FUNCTION_ARGS
+# ifdef _MSC_VER
+# pragma warning(push)
+# pragma warning(disable:4577)
+# include <type_traits>
+# pragma warning(pop)
+
+# elif defined(RT_OS_DARWIN)
+# define _LIBCPP_CSTDDEF
+# include <__nullptr>
+# include <type_traits>
+
+# else
+# include <type_traits>
+# endif
+# endif
+#endif
+
+
+/*
+ * Logging assignments:
+ * Log - useful stuff, like failures.
+ * LogFlow - program flow, except the really noisy bits.
+ * Log2 - Cleanup.
+ * Log3 - Loader flow noise.
+ * Log4 - Call VMMR0 flow noise.
+ * Log5 - Native yet-to-be-defined noise.
+ * Log6 - Native ioctl flow noise.
+ *
+ * Logging requires KBUILD_TYPE=debug and possibly changes to the logger
+ * instantiation in log-vbox.c(pp).
+ */
+
+
+/*********************************************************************************************************************************
+* Defined Constants And Macros *
+*********************************************************************************************************************************/
+/** @def VBOX_SVN_REV
+ * The makefile should define this if it can. */
+#ifndef VBOX_SVN_REV
+# define VBOX_SVN_REV 0
+#endif
+
+/** @ SUPDRV_CHECK_SMAP_SETUP
+ * SMAP check setup. */
+/** @def SUPDRV_CHECK_SMAP_CHECK
+ * Checks that the AC flag is set if SMAP is enabled. If AC is not set, it
+ * will be logged and @a a_BadExpr is executed. */
+#if (defined(RT_OS_DARWIN) || defined(RT_OS_LINUX)) && !defined(VBOX_WITHOUT_EFLAGS_AC_SET_IN_VBOXDRV)
+# define SUPDRV_CHECK_SMAP_SETUP() uint32_t const fKernelFeatures = SUPR0GetKernelFeatures()
+# define SUPDRV_CHECK_SMAP_CHECK(a_pDevExt, a_BadExpr) \
+ do { \
+ if (fKernelFeatures & SUPKERNELFEATURES_SMAP) \
+ { \
+ RTCCUINTREG fEfl = ASMGetFlags(); \
+ if (RT_LIKELY(fEfl & X86_EFL_AC)) \
+ { /* likely */ } \
+ else \
+ { \
+ supdrvBadContext(a_pDevExt, "SUPDrv.cpp", __LINE__, "EFLAGS.AC is 0!"); \
+ a_BadExpr; \
+ } \
+ } \
+ } while (0)
+#else
+# define SUPDRV_CHECK_SMAP_SETUP() uint32_t const fKernelFeatures = 0
+# define SUPDRV_CHECK_SMAP_CHECK(a_pDevExt, a_BadExpr) NOREF(fKernelFeatures)
+#endif
+
+
+/*********************************************************************************************************************************
+* Internal Functions *
+*********************************************************************************************************************************/
+static DECLCALLBACK(int) supdrvSessionObjHandleRetain(RTHANDLETABLE hHandleTable, void *pvObj, void *pvCtx, void *pvUser);
+static DECLCALLBACK(void) supdrvSessionObjHandleDelete(RTHANDLETABLE hHandleTable, uint32_t h, void *pvObj, void *pvCtx, void *pvUser);
+static int supdrvMemAdd(PSUPDRVMEMREF pMem, PSUPDRVSESSION pSession);
+static int supdrvMemRelease(PSUPDRVSESSION pSession, RTHCUINTPTR uPtr, SUPDRVMEMREFTYPE eType);
+static int supdrvIOCtl_LdrOpen(PSUPDRVDEVEXT pDevExt, PSUPDRVSESSION pSession, PSUPLDROPEN pReq);
+static int supdrvIOCtl_LdrLoad(PSUPDRVDEVEXT pDevExt, PSUPDRVSESSION pSession, PSUPLDRLOAD pReq);
+static int supdrvIOCtl_LdrFree(PSUPDRVDEVEXT pDevExt, PSUPDRVSESSION pSession, PSUPLDRFREE pReq);
+static int supdrvIOCtl_LdrLockDown(PSUPDRVDEVEXT pDevExt);
+static int supdrvIOCtl_LdrQuerySymbol(PSUPDRVDEVEXT pDevExt, PSUPDRVSESSION pSession, PSUPLDRGETSYMBOL pReq);
+static int supdrvIDC_LdrGetSymbol(PSUPDRVDEVEXT pDevExt, PSUPDRVSESSION pSession, PSUPDRVIDCREQGETSYM pReq);
+static int supdrvLdrAddUsage(PSUPDRVDEVEXT pDevExt, PSUPDRVSESSION pSession, PSUPDRVLDRIMAGE pImage, bool fRing3Usage);
+DECLINLINE(void) supdrvLdrSubtractUsage(PSUPDRVDEVEXT pDevExt, PSUPDRVLDRIMAGE pImage, uint32_t cReference);
+static void supdrvLdrFree(PSUPDRVDEVEXT pDevExt, PSUPDRVLDRIMAGE pImage);
+DECLINLINE(int) supdrvLdrLock(PSUPDRVDEVEXT pDevExt);
+DECLINLINE(int) supdrvLdrUnlock(PSUPDRVDEVEXT pDevExt);
+static int supdrvIOCtl_CallServiceModule(PSUPDRVDEVEXT pDevExt, PSUPDRVSESSION pSession, PSUPCALLSERVICE pReq);
+static int supdrvIOCtl_LoggerSettings(PSUPLOGGERSETTINGS pReq);
+static int supdrvIOCtl_MsrProber(PSUPDRVDEVEXT pDevExt, PSUPMSRPROBER pReq);
+static int supdrvIOCtl_ResumeSuspendedKbds(void);
+
+
+/*********************************************************************************************************************************
+* Global Variables *
+*********************************************************************************************************************************/
+/** @def SUPEXP_CHECK_ARGS
+ * This is for checking the argument count of the function in the entry,
+ * just to make sure we don't accidentally export something the wrapper
+ * can't deal with.
+ *
+ * Using some C++11 magic to do the counting.
+ *
+ * The error is reported by overflowing the SUPFUNC::cArgs field, so the
+ * warnings can probably be a little mysterious.
+ *
+ * @note Doesn't work for CLANG 11. Works for Visual C++, unless there
+ * are function pointers in the argument list.
+ */
+#if defined(SUPDRV_CAN_COUNT_FUNCTION_ARGS) && RT_CLANG_PREREQ(99, 0)
+template <typename RetType, typename ... Types>
+constexpr std::integral_constant<unsigned, sizeof ...(Types)>
+CountFunctionArguments(RetType(RTCALL *)(Types ...))
+{
+ return std::integral_constant<unsigned, sizeof ...(Types)>{};
+}
+# define SUPEXP_CHECK_ARGS(a_cArgs, a_Name) \
+ ((a_cArgs) >= decltype(CountFunctionArguments(a_Name))::value ? (uint8_t)(a_cArgs) : 1023)
+
+#else
+# define SUPEXP_CHECK_ARGS(a_cArgs, a_Name) a_cArgs
+#endif
+
+/** @name Function table entry macros.
+ * @note The SUPEXP_STK_BACKF macro is because VC++ has trouble with functions
+ * with function pointer arguments (probably noexcept related).
+ * @{ */
+#define SUPEXP_CUSTOM(a_cArgs, a_Name, a_Value) { #a_Name, a_cArgs, (void *)(uintptr_t)(a_Value) }
+#define SUPEXP_STK_OKAY(a_cArgs, a_Name) { #a_Name, SUPEXP_CHECK_ARGS(a_cArgs, a_Name), (void *)(uintptr_t)a_Name }
+#if 0
+# define SUPEXP_STK_BACK(a_cArgs, a_Name) { "StkBack_" #a_Name, SUPEXP_CHECK_ARGS(a_cArgs, a_Name), (void *)(uintptr_t)a_Name }
+# define SUPEXP_STK_BACKF(a_cArgs, a_Name) { "StkBack_" #a_Name, SUPEXP_CHECK_ARGS(a_cArgs, a_Name), (void *)(uintptr_t)a_Name }
+#else
+# define SUPEXP_STK_BACK(a_cArgs, a_Name) { #a_Name, SUPEXP_CHECK_ARGS(a_cArgs, a_Name), (void *)(uintptr_t)a_Name }
+# ifdef _MSC_VER
+# define SUPEXP_STK_BACKF(a_cArgs, a_Name) { #a_Name, a_cArgs, (void *)(uintptr_t)a_Name }
+# else
+# define SUPEXP_STK_BACKF(a_cArgs, a_Name) { #a_Name, SUPEXP_CHECK_ARGS(a_cArgs, a_Name), (void *)(uintptr_t)a_Name }
+# endif
+#endif
+/** @} */
+
+/**
+ * Array of the R0 SUP API.
+ *
+ * While making changes to these exports, make sure to update the IOC
+ * minor version (SUPDRV_IOC_VERSION).
+ *
+ * @remarks This array is processed by SUPR0-def-pe.sed and SUPR0-def-lx.sed to
+ * produce definition files from which import libraries are generated.
+ * Take care when commenting things and especially with \#ifdef'ing.
+ */
+static SUPFUNC g_aFunctions[] =
+{
+/* SED: START */
+ /* name function */
+ /* Entries with absolute addresses determined at runtime, fixup
+ code makes ugly ASSUMPTIONS about the order here: */
+ SUPEXP_CUSTOM( 0, SUPR0AbsIs64bit, 0),
+ SUPEXP_CUSTOM( 0, SUPR0Abs64bitKernelCS, 0),
+ SUPEXP_CUSTOM( 0, SUPR0Abs64bitKernelSS, 0),
+ SUPEXP_CUSTOM( 0, SUPR0Abs64bitKernelDS, 0),
+ SUPEXP_CUSTOM( 0, SUPR0AbsKernelCS, 0),
+ SUPEXP_CUSTOM( 0, SUPR0AbsKernelSS, 0),
+ SUPEXP_CUSTOM( 0, SUPR0AbsKernelDS, 0),
+ SUPEXP_CUSTOM( 0, SUPR0AbsKernelES, 0),
+ SUPEXP_CUSTOM( 0, SUPR0AbsKernelFS, 0),
+ SUPEXP_CUSTOM( 0, SUPR0AbsKernelGS, 0),
+ /* Normal function & data pointers: */
+ SUPEXP_CUSTOM( 0, g_pSUPGlobalInfoPage, &g_pSUPGlobalInfoPage), /* SED: DATA */
+ SUPEXP_STK_OKAY( 0, SUPGetGIP),
+ SUPEXP_STK_BACK( 1, SUPReadTscWithDelta),
+ SUPEXP_STK_BACK( 1, SUPGetTscDeltaSlow),
+ SUPEXP_STK_BACK( 1, SUPGetCpuHzFromGipForAsyncMode),
+ SUPEXP_STK_OKAY( 3, SUPIsTscFreqCompatible),
+ SUPEXP_STK_OKAY( 3, SUPIsTscFreqCompatibleEx),
+ SUPEXP_STK_BACK( 4, SUPR0BadContext),
+ SUPEXP_STK_BACK( 2, SUPR0ComponentDeregisterFactory),
+ SUPEXP_STK_BACK( 4, SUPR0ComponentQueryFactory),
+ SUPEXP_STK_BACK( 2, SUPR0ComponentRegisterFactory),
+ SUPEXP_STK_BACK( 5, SUPR0ContAlloc),
+ SUPEXP_STK_BACK( 2, SUPR0ContFree),
+ SUPEXP_STK_BACK( 2, SUPR0ChangeCR4),
+ SUPEXP_STK_BACK( 1, SUPR0EnableVTx),
+ SUPEXP_STK_OKAY( 1, SUPR0FpuBegin),
+ SUPEXP_STK_OKAY( 1, SUPR0FpuEnd),
+ SUPEXP_STK_BACK( 0, SUPR0SuspendVTxOnCpu),
+ SUPEXP_STK_BACK( 1, SUPR0ResumeVTxOnCpu),
+ SUPEXP_STK_OKAY( 1, SUPR0GetCurrentGdtRw),
+ SUPEXP_STK_OKAY( 0, SUPR0GetKernelFeatures),
+ SUPEXP_STK_BACK( 3, SUPR0GetHwvirtMsrs),
+ SUPEXP_STK_BACK( 0, SUPR0GetPagingMode),
+ SUPEXP_STK_BACK( 1, SUPR0GetSvmUsability),
+ SUPEXP_STK_BACK( 1, SUPR0GetVTSupport),
+ SUPEXP_STK_BACK( 1, SUPR0GetVmxUsability),
+ SUPEXP_STK_BACK( 2, SUPR0LdrIsLockOwnerByMod),
+ SUPEXP_STK_BACK( 1, SUPR0LdrLock),
+ SUPEXP_STK_BACK( 1, SUPR0LdrUnlock),
+ SUPEXP_STK_BACK( 3, SUPR0LdrModByName),
+ SUPEXP_STK_BACK( 2, SUPR0LdrModRelease),
+ SUPEXP_STK_BACK( 2, SUPR0LdrModRetain),
+ SUPEXP_STK_BACK( 4, SUPR0LockMem),
+ SUPEXP_STK_BACK( 5, SUPR0LowAlloc),
+ SUPEXP_STK_BACK( 2, SUPR0LowFree),
+ SUPEXP_STK_BACK( 4, SUPR0MemAlloc),
+ SUPEXP_STK_BACK( 2, SUPR0MemFree),
+ SUPEXP_STK_BACK( 3, SUPR0MemGetPhys),
+ SUPEXP_STK_BACK( 2, SUPR0ObjAddRef),
+ SUPEXP_STK_BACK( 3, SUPR0ObjAddRefEx),
+ SUPEXP_STK_BACKF( 5, SUPR0ObjRegister),
+ SUPEXP_STK_BACK( 2, SUPR0ObjRelease),
+ SUPEXP_STK_BACK( 3, SUPR0ObjVerifyAccess),
+ SUPEXP_STK_BACK( 6, SUPR0PageAllocEx),
+ SUPEXP_STK_BACK( 2, SUPR0PageFree),
+ SUPEXP_STK_BACK( 6, SUPR0PageMapKernel),
+ SUPEXP_STK_BACK( 6, SUPR0PageProtect),
+#if defined(RT_OS_LINUX) || defined(RT_OS_SOLARIS) || defined(RT_OS_FREEBSD)
+ SUPEXP_STK_OKAY( 2, SUPR0HCPhysToVirt), /* only-linux, only-solaris, only-freebsd */
+#endif
+ SUPEXP_STK_BACK( 2, SUPR0PrintfV),
+ SUPEXP_STK_BACK( 1, SUPR0GetSessionGVM),
+ SUPEXP_STK_BACK( 1, SUPR0GetSessionVM),
+ SUPEXP_STK_BACK( 3, SUPR0SetSessionVM),
+ SUPEXP_STK_BACK( 1, SUPR0GetSessionUid),
+ SUPEXP_STK_BACK( 6, SUPR0TscDeltaMeasureBySetIndex),
+ SUPEXP_STK_BACK( 1, SUPR0TracerDeregisterDrv),
+ SUPEXP_STK_BACK( 2, SUPR0TracerDeregisterImpl),
+ SUPEXP_STK_BACK( 6, SUPR0TracerFireProbe),
+ SUPEXP_STK_BACK( 3, SUPR0TracerRegisterDrv),
+ SUPEXP_STK_BACK( 4, SUPR0TracerRegisterImpl),
+ SUPEXP_STK_BACK( 2, SUPR0TracerRegisterModule),
+ SUPEXP_STK_BACK( 2, SUPR0TracerUmodProbeFire),
+ SUPEXP_STK_BACK( 2, SUPR0UnlockMem),
+#ifdef RT_OS_WINDOWS
+ SUPEXP_STK_BACK( 4, SUPR0IoCtlSetupForHandle), /* only-windows */
+ SUPEXP_STK_BACK( 9, SUPR0IoCtlPerform), /* only-windows */
+ SUPEXP_STK_BACK( 1, SUPR0IoCtlCleanup), /* only-windows */
+#endif
+ SUPEXP_STK_BACK( 2, SUPSemEventClose),
+ SUPEXP_STK_BACK( 2, SUPSemEventCreate),
+ SUPEXP_STK_BACK( 1, SUPSemEventGetResolution),
+ SUPEXP_STK_BACK( 2, SUPSemEventMultiClose),
+ SUPEXP_STK_BACK( 2, SUPSemEventMultiCreate),
+ SUPEXP_STK_BACK( 1, SUPSemEventMultiGetResolution),
+ SUPEXP_STK_BACK( 2, SUPSemEventMultiReset),
+ SUPEXP_STK_BACK( 2, SUPSemEventMultiSignal),
+ SUPEXP_STK_BACK( 3, SUPSemEventMultiWait),
+ SUPEXP_STK_BACK( 3, SUPSemEventMultiWaitNoResume),
+ SUPEXP_STK_BACK( 3, SUPSemEventMultiWaitNsAbsIntr),
+ SUPEXP_STK_BACK( 3, SUPSemEventMultiWaitNsRelIntr),
+ SUPEXP_STK_BACK( 2, SUPSemEventSignal),
+ SUPEXP_STK_BACK( 3, SUPSemEventWait),
+ SUPEXP_STK_BACK( 3, SUPSemEventWaitNoResume),
+ SUPEXP_STK_BACK( 3, SUPSemEventWaitNsAbsIntr),
+ SUPEXP_STK_BACK( 3, SUPSemEventWaitNsRelIntr),
+
+ SUPEXP_STK_BACK( 0, RTAssertAreQuiet),
+ SUPEXP_STK_BACK( 0, RTAssertMayPanic),
+ SUPEXP_STK_BACK( 4, RTAssertMsg1),
+ SUPEXP_STK_BACK( 2, RTAssertMsg2AddV),
+ SUPEXP_STK_BACK( 2, RTAssertMsg2V),
+ SUPEXP_STK_BACK( 1, RTAssertSetMayPanic),
+ SUPEXP_STK_BACK( 1, RTAssertSetQuiet),
+ SUPEXP_STK_OKAY( 2, RTCrc32),
+ SUPEXP_STK_OKAY( 1, RTCrc32Finish),
+ SUPEXP_STK_OKAY( 3, RTCrc32Process),
+ SUPEXP_STK_OKAY( 0, RTCrc32Start),
+ SUPEXP_STK_OKAY( 1, RTErrConvertFromErrno),
+ SUPEXP_STK_OKAY( 1, RTErrConvertToErrno),
+ SUPEXP_STK_BACK( 4, RTHandleTableAllocWithCtx),
+ SUPEXP_STK_BACK( 1, RTHandleTableCreate),
+ SUPEXP_STK_BACKF( 6, RTHandleTableCreateEx),
+ SUPEXP_STK_BACKF( 3, RTHandleTableDestroy),
+ SUPEXP_STK_BACK( 3, RTHandleTableFreeWithCtx),
+ SUPEXP_STK_BACK( 3, RTHandleTableLookupWithCtx),
+ SUPEXP_STK_BACK( 4, RTLogBulkNestedWrite),
+ SUPEXP_STK_BACK( 5, RTLogBulkUpdate),
+ SUPEXP_STK_BACK( 2, RTLogCheckGroupFlags),
+ SUPEXP_STK_BACKF( 17, RTLogCreateExV),
+ SUPEXP_STK_BACK( 1, RTLogDestroy),
+ SUPEXP_STK_BACK( 0, RTLogDefaultInstance),
+ SUPEXP_STK_BACK( 1, RTLogDefaultInstanceEx),
+ SUPEXP_STK_BACK( 1, SUPR0DefaultLogInstanceEx),
+ SUPEXP_STK_BACK( 0, RTLogGetDefaultInstance),
+ SUPEXP_STK_BACK( 1, RTLogGetDefaultInstanceEx),
+ SUPEXP_STK_BACK( 1, SUPR0GetDefaultLogInstanceEx),
+ SUPEXP_STK_BACK( 5, RTLogLoggerExV),
+ SUPEXP_STK_BACK( 2, RTLogPrintfV),
+ SUPEXP_STK_BACK( 0, RTLogRelGetDefaultInstance),
+ SUPEXP_STK_BACK( 1, RTLogRelGetDefaultInstanceEx),
+ SUPEXP_STK_BACK( 1, SUPR0GetDefaultLogRelInstanceEx),
+ SUPEXP_STK_BACK( 2, RTLogSetDefaultInstanceThread),
+ SUPEXP_STK_BACKF( 2, RTLogSetFlushCallback),
+ SUPEXP_STK_BACK( 2, RTLogSetR0ProgramStart),
+ SUPEXP_STK_BACK( 3, RTLogSetR0ThreadNameV),
+ SUPEXP_STK_BACK( 5, RTMemAllocExTag),
+ SUPEXP_STK_BACK( 2, RTMemAllocTag),
+ SUPEXP_STK_BACK( 2, RTMemAllocVarTag),
+ SUPEXP_STK_BACK( 2, RTMemAllocZTag),
+ SUPEXP_STK_BACK( 2, RTMemAllocZVarTag),
+ SUPEXP_STK_BACK( 4, RTMemDupExTag),
+ SUPEXP_STK_BACK( 3, RTMemDupTag),
+ SUPEXP_STK_BACK( 1, RTMemFree),
+ SUPEXP_STK_BACK( 2, RTMemFreeEx),
+ SUPEXP_STK_BACK( 3, RTMemReallocTag),
+ SUPEXP_STK_BACK( 0, RTMpCpuId),
+ SUPEXP_STK_BACK( 1, RTMpCpuIdFromSetIndex),
+ SUPEXP_STK_BACK( 1, RTMpCpuIdToSetIndex),
+ SUPEXP_STK_BACK( 0, RTMpCurSetIndex),
+ SUPEXP_STK_BACK( 1, RTMpCurSetIndexAndId),
+ SUPEXP_STK_BACK( 0, RTMpGetArraySize),
+ SUPEXP_STK_BACK( 0, RTMpGetCount),
+ SUPEXP_STK_BACK( 0, RTMpGetMaxCpuId),
+ SUPEXP_STK_BACK( 0, RTMpGetOnlineCount),
+ SUPEXP_STK_BACK( 1, RTMpGetOnlineSet),
+ SUPEXP_STK_BACK( 1, RTMpGetSet),
+ SUPEXP_STK_BACK( 1, RTMpIsCpuOnline),
+ SUPEXP_STK_BACK( 1, RTMpIsCpuPossible),
+ SUPEXP_STK_BACK( 0, RTMpIsCpuWorkPending),
+ SUPEXP_STK_BACKF( 2, RTMpNotificationDeregister),
+ SUPEXP_STK_BACKF( 2, RTMpNotificationRegister),
+ SUPEXP_STK_BACKF( 3, RTMpOnAll),
+ SUPEXP_STK_BACKF( 3, RTMpOnOthers),
+ SUPEXP_STK_BACKF( 4, RTMpOnSpecific),
+ SUPEXP_STK_BACK( 1, RTMpPokeCpu),
+ SUPEXP_STK_OKAY( 4, RTNetIPv4AddDataChecksum),
+ SUPEXP_STK_OKAY( 2, RTNetIPv4AddTCPChecksum),
+ SUPEXP_STK_OKAY( 2, RTNetIPv4AddUDPChecksum),
+ SUPEXP_STK_OKAY( 1, RTNetIPv4FinalizeChecksum),
+ SUPEXP_STK_OKAY( 1, RTNetIPv4HdrChecksum),
+ SUPEXP_STK_OKAY( 4, RTNetIPv4IsDHCPValid),
+ SUPEXP_STK_OKAY( 4, RTNetIPv4IsHdrValid),
+ SUPEXP_STK_OKAY( 4, RTNetIPv4IsTCPSizeValid),
+ SUPEXP_STK_OKAY( 6, RTNetIPv4IsTCPValid),
+ SUPEXP_STK_OKAY( 3, RTNetIPv4IsUDPSizeValid),
+ SUPEXP_STK_OKAY( 5, RTNetIPv4IsUDPValid),
+ SUPEXP_STK_OKAY( 1, RTNetIPv4PseudoChecksum),
+ SUPEXP_STK_OKAY( 4, RTNetIPv4PseudoChecksumBits),
+ SUPEXP_STK_OKAY( 3, RTNetIPv4TCPChecksum),
+ SUPEXP_STK_OKAY( 3, RTNetIPv4UDPChecksum),
+ SUPEXP_STK_OKAY( 1, RTNetIPv6PseudoChecksum),
+ SUPEXP_STK_OKAY( 4, RTNetIPv6PseudoChecksumBits),
+ SUPEXP_STK_OKAY( 3, RTNetIPv6PseudoChecksumEx),
+ SUPEXP_STK_OKAY( 4, RTNetTCPChecksum),
+ SUPEXP_STK_OKAY( 2, RTNetUDPChecksum),
+ SUPEXP_STK_BACKF( 2, RTPowerNotificationDeregister),
+ SUPEXP_STK_BACKF( 2, RTPowerNotificationRegister),
+ SUPEXP_STK_BACK( 0, RTProcSelf),
+ SUPEXP_STK_BACK( 0, RTR0AssertPanicSystem),
+#if defined(RT_OS_DARWIN) || defined(RT_OS_SOLARIS) || defined(RT_OS_WINDOWS)
+ SUPEXP_STK_BACK( 2, RTR0DbgKrnlInfoOpen), /* only-darwin, only-solaris, only-windows */
+ SUPEXP_STK_BACK( 5, RTR0DbgKrnlInfoQueryMember), /* only-darwin, only-solaris, only-windows */
+# if defined(RT_OS_SOLARIS)
+ SUPEXP_STK_BACK( 4, RTR0DbgKrnlInfoQuerySize), /* only-solaris */
+# endif
+ SUPEXP_STK_BACK( 4, RTR0DbgKrnlInfoQuerySymbol), /* only-darwin, only-solaris, only-windows */
+ SUPEXP_STK_BACK( 1, RTR0DbgKrnlInfoRelease), /* only-darwin, only-solaris, only-windows */
+ SUPEXP_STK_BACK( 1, RTR0DbgKrnlInfoRetain), /* only-darwin, only-solaris, only-windows */
+#endif
+ SUPEXP_STK_BACK( 0, RTR0MemAreKrnlAndUsrDifferent),
+ SUPEXP_STK_BACK( 1, RTR0MemKernelIsValidAddr),
+ SUPEXP_STK_BACK( 3, RTR0MemKernelCopyFrom),
+ SUPEXP_STK_BACK( 3, RTR0MemKernelCopyTo),
+ SUPEXP_STK_OKAY( 1, RTR0MemObjAddress),
+ SUPEXP_STK_OKAY( 1, RTR0MemObjAddressR3),
+ SUPEXP_STK_BACK( 4, RTR0MemObjAllocContTag),
+ SUPEXP_STK_BACK( 5, RTR0MemObjAllocLargeTag),
+ SUPEXP_STK_BACK( 4, RTR0MemObjAllocLowTag),
+ SUPEXP_STK_BACK( 4, RTR0MemObjAllocPageTag),
+ SUPEXP_STK_BACK( 5, RTR0MemObjAllocPhysExTag),
+ SUPEXP_STK_BACK( 4, RTR0MemObjAllocPhysNCTag),
+ SUPEXP_STK_BACK( 4, RTR0MemObjAllocPhysTag),
+ SUPEXP_STK_BACK( 5, RTR0MemObjEnterPhysTag),
+ SUPEXP_STK_BACK( 2, RTR0MemObjFree),
+ SUPEXP_STK_BACK( 2, RTR0MemObjGetPagePhysAddr),
+ SUPEXP_STK_OKAY( 1, RTR0MemObjIsMapping),
+ SUPEXP_STK_BACK( 6, RTR0MemObjLockUserTag),
+ SUPEXP_STK_BACK( 5, RTR0MemObjLockKernelTag),
+ SUPEXP_STK_BACK( 8, RTR0MemObjMapKernelExTag),
+ SUPEXP_STK_BACK( 6, RTR0MemObjMapKernelTag),
+ SUPEXP_STK_BACK( 9, RTR0MemObjMapUserExTag),
+ SUPEXP_STK_BACK( 7, RTR0MemObjMapUserTag),
+ SUPEXP_STK_BACK( 4, RTR0MemObjProtect),
+ SUPEXP_STK_OKAY( 1, RTR0MemObjSize),
+ SUPEXP_STK_OKAY( 1, RTR0MemObjWasZeroInitialized),
+ SUPEXP_STK_BACK( 3, RTR0MemUserCopyFrom),
+ SUPEXP_STK_BACK( 3, RTR0MemUserCopyTo),
+ SUPEXP_STK_BACK( 1, RTR0MemUserIsValidAddr),
+ SUPEXP_STK_BACK( 0, RTR0ProcHandleSelf),
+ SUPEXP_STK_BACK( 1, RTSemEventCreate),
+ SUPEXP_STK_BACK( 1, RTSemEventDestroy),
+ SUPEXP_STK_BACK( 0, RTSemEventGetResolution),
+ SUPEXP_STK_BACK( 0, RTSemEventIsSignalSafe),
+ SUPEXP_STK_BACK( 1, RTSemEventMultiCreate),
+ SUPEXP_STK_BACK( 1, RTSemEventMultiDestroy),
+ SUPEXP_STK_BACK( 0, RTSemEventMultiGetResolution),
+ SUPEXP_STK_BACK( 0, RTSemEventMultiIsSignalSafe),
+ SUPEXP_STK_BACK( 1, RTSemEventMultiReset),
+ SUPEXP_STK_BACK( 1, RTSemEventMultiSignal),
+ SUPEXP_STK_BACK( 2, RTSemEventMultiWait),
+ SUPEXP_STK_BACK( 3, RTSemEventMultiWaitEx),
+ SUPEXP_STK_BACK( 7, RTSemEventMultiWaitExDebug),
+ SUPEXP_STK_BACK( 2, RTSemEventMultiWaitNoResume),
+ SUPEXP_STK_BACK( 1, RTSemEventSignal),
+ SUPEXP_STK_BACK( 2, RTSemEventWait),
+ SUPEXP_STK_BACK( 3, RTSemEventWaitEx),
+ SUPEXP_STK_BACK( 7, RTSemEventWaitExDebug),
+ SUPEXP_STK_BACK( 2, RTSemEventWaitNoResume),
+ SUPEXP_STK_BACK( 1, RTSemFastMutexCreate),
+ SUPEXP_STK_BACK( 1, RTSemFastMutexDestroy),
+ SUPEXP_STK_BACK( 1, RTSemFastMutexRelease),
+ SUPEXP_STK_BACK( 1, RTSemFastMutexRequest),
+ SUPEXP_STK_BACK( 1, RTSemMutexCreate),
+ SUPEXP_STK_BACK( 1, RTSemMutexDestroy),
+ SUPEXP_STK_BACK( 1, RTSemMutexRelease),
+ SUPEXP_STK_BACK( 2, RTSemMutexRequest),
+ SUPEXP_STK_BACK( 6, RTSemMutexRequestDebug),
+ SUPEXP_STK_BACK( 2, RTSemMutexRequestNoResume),
+ SUPEXP_STK_BACK( 6, RTSemMutexRequestNoResumeDebug),
+ SUPEXP_STK_BACK( 1, RTSpinlockAcquire),
+ SUPEXP_STK_BACK( 3, RTSpinlockCreate),
+ SUPEXP_STK_BACK( 1, RTSpinlockDestroy),
+ SUPEXP_STK_BACK( 1, RTSpinlockRelease),
+ SUPEXP_STK_OKAY( 3, RTStrCopy),
+ SUPEXP_STK_BACK( 2, RTStrDupTag),
+ SUPEXP_STK_BACK( 6, RTStrFormatNumber),
+ SUPEXP_STK_BACK( 1, RTStrFormatTypeDeregister),
+ SUPEXP_STK_BACKF( 3, RTStrFormatTypeRegister),
+ SUPEXP_STK_BACKF( 2, RTStrFormatTypeSetUser),
+ SUPEXP_STK_BACKF( 6, RTStrFormatV),
+ SUPEXP_STK_BACK( 1, RTStrFree),
+ SUPEXP_STK_OKAY( 3, RTStrNCmp),
+ SUPEXP_STK_BACKF( 6, RTStrPrintfExV),
+ SUPEXP_STK_BACK( 4, RTStrPrintfV),
+ SUPEXP_STK_BACKF( 6, RTStrPrintf2ExV),
+ SUPEXP_STK_BACK( 4, RTStrPrintf2V),
+ SUPEXP_STK_BACKF( 7, RTThreadCreate),
+ SUPEXP_STK_BACK( 1, RTThreadCtxHookIsEnabled),
+ SUPEXP_STK_BACKF( 4, RTThreadCtxHookCreate),
+ SUPEXP_STK_BACK( 1, RTThreadCtxHookDestroy),
+ SUPEXP_STK_BACK( 1, RTThreadCtxHookDisable),
+ SUPEXP_STK_BACK( 1, RTThreadCtxHookEnable),
+ SUPEXP_STK_BACK( 1, RTThreadGetName),
+ SUPEXP_STK_BACK( 1, RTThreadGetNative),
+ SUPEXP_STK_BACK( 1, RTThreadGetType),
+ SUPEXP_STK_BACK( 1, RTThreadIsInInterrupt),
+ SUPEXP_STK_BACK( 0, RTThreadNativeSelf),
+ SUPEXP_STK_BACK( 1, RTThreadPreemptDisable),
+ SUPEXP_STK_BACK( 1, RTThreadPreemptIsEnabled),
+ SUPEXP_STK_BACK( 1, RTThreadPreemptIsPending),
+ SUPEXP_STK_BACK( 0, RTThreadPreemptIsPendingTrusty),
+ SUPEXP_STK_BACK( 0, RTThreadPreemptIsPossible),
+ SUPEXP_STK_BACK( 1, RTThreadPreemptRestore),
+ SUPEXP_STK_BACK( 1, RTThreadQueryTerminationStatus),
+ SUPEXP_STK_BACK( 0, RTThreadSelf),
+ SUPEXP_STK_BACK( 0, RTThreadSelfName),
+ SUPEXP_STK_BACK( 1, RTThreadSleep),
+ SUPEXP_STK_BACK( 1, RTThreadUserReset),
+ SUPEXP_STK_BACK( 1, RTThreadUserSignal),
+ SUPEXP_STK_BACK( 2, RTThreadUserWait),
+ SUPEXP_STK_BACK( 2, RTThreadUserWaitNoResume),
+ SUPEXP_STK_BACK( 3, RTThreadWait),
+ SUPEXP_STK_BACK( 3, RTThreadWaitNoResume),
+ SUPEXP_STK_BACK( 0, RTThreadYield),
+ SUPEXP_STK_BACK( 1, RTTimeNow),
+ SUPEXP_STK_BACK( 0, RTTimerCanDoHighResolution),
+ SUPEXP_STK_BACK( 2, RTTimerChangeInterval),
+ SUPEXP_STK_BACKF( 4, RTTimerCreate),
+ SUPEXP_STK_BACKF( 5, RTTimerCreateEx),
+ SUPEXP_STK_BACK( 1, RTTimerDestroy),
+ SUPEXP_STK_BACK( 0, RTTimerGetSystemGranularity),
+ SUPEXP_STK_BACK( 1, RTTimerReleaseSystemGranularity),
+ SUPEXP_STK_BACK( 2, RTTimerRequestSystemGranularity),
+ SUPEXP_STK_BACK( 2, RTTimerStart),
+ SUPEXP_STK_BACK( 1, RTTimerStop),
+ SUPEXP_STK_BACK( 0, RTTimeSystemMilliTS),
+ SUPEXP_STK_BACK( 0, RTTimeSystemNanoTS),
+ SUPEXP_STK_OKAY( 2, RTUuidCompare),
+ SUPEXP_STK_OKAY( 2, RTUuidCompareStr),
+ SUPEXP_STK_OKAY( 2, RTUuidFromStr),
+/* SED: END */
+};
+
+#if defined(RT_OS_DARWIN) || defined(RT_OS_SOLARIS) || defined(RT_OS_FREEBSD)
+/**
+ * Drag in the rest of IRPT since we share it with the
+ * rest of the kernel modules on darwin.
+ */
+struct CLANG11WERIDNESS { PFNRT pfn; } g_apfnVBoxDrvIPRTDeps[] =
+{
+ /* VBoxNetAdp */
+ { (PFNRT)RTRandBytes },
+ /* VBoxUSB */
+ { (PFNRT)RTPathStripFilename },
+#if !defined(RT_OS_FREEBSD)
+ { (PFNRT)RTHandleTableAlloc },
+ { (PFNRT)RTStrPurgeEncoding },
+#endif
+ { NULL }
+};
+#endif /* RT_OS_DARWIN || RT_OS_SOLARIS || RT_OS_FREEBSD */
+
+
+
+/**
+ * Initializes the device extentsion structure.
+ *
+ * @returns IPRT status code.
+ * @param pDevExt The device extension to initialize.
+ * @param cbSession The size of the session structure. The size of
+ * SUPDRVSESSION may be smaller when SUPDRV_AGNOSTIC is
+ * defined because we're skipping the OS specific members
+ * then.
+ */
+int VBOXCALL supdrvInitDevExt(PSUPDRVDEVEXT pDevExt, size_t cbSession)
+{
+ int rc;
+
+#ifdef SUPDRV_WITH_RELEASE_LOGGER
+ /*
+ * Create the release log.
+ */
+ static const char * const s_apszGroups[] = VBOX_LOGGROUP_NAMES;
+ PRTLOGGER pRelLogger;
+ rc = RTLogCreate(&pRelLogger, 0 /* fFlags */, "all",
+ "VBOX_RELEASE_LOG", RT_ELEMENTS(s_apszGroups), s_apszGroups, RTLOGDEST_STDOUT | RTLOGDEST_DEBUGGER, NULL);
+ if (RT_SUCCESS(rc))
+ RTLogRelSetDefaultInstance(pRelLogger);
+ /** @todo Add native hook for getting logger config parameters and setting
+ * them. On linux we should use the module parameter stuff... */
+#endif
+
+#if (defined(RT_ARCH_AMD64) || defined(RT_ARCH_X86)) && !defined(VBOX_WITH_OLD_CPU_SUPPORT)
+ /*
+ * Require SSE2 to be present.
+ */
+ if (!(ASMCpuId_EDX(1) & X86_CPUID_FEATURE_EDX_SSE2))
+ {
+ SUPR0Printf("vboxdrv: Requires SSE2 (cpuid(0).EDX=%#x)\n", ASMCpuId_EDX(1));
+ return VERR_UNSUPPORTED_CPU;
+ }
+#endif
+
+ /*
+ * Initialize it.
+ */
+ memset(pDevExt, 0, sizeof(*pDevExt)); /* Does not wipe OS specific tail section of the structure. */
+ pDevExt->Spinlock = NIL_RTSPINLOCK;
+ pDevExt->hGipSpinlock = NIL_RTSPINLOCK;
+ pDevExt->hSessionHashTabSpinlock = NIL_RTSPINLOCK;
+#ifdef SUPDRV_USE_MUTEX_FOR_LDR
+ pDevExt->mtxLdr = NIL_RTSEMMUTEX;
+#else
+ pDevExt->mtxLdr = NIL_RTSEMFASTMUTEX;
+#endif
+#ifdef SUPDRV_USE_MUTEX_FOR_GIP
+ pDevExt->mtxGip = NIL_RTSEMMUTEX;
+ pDevExt->mtxTscDelta = NIL_RTSEMMUTEX;
+#else
+ pDevExt->mtxGip = NIL_RTSEMFASTMUTEX;
+ pDevExt->mtxTscDelta = NIL_RTSEMFASTMUTEX;
+#endif
+
+ rc = RTSpinlockCreate(&pDevExt->Spinlock, RTSPINLOCK_FLAGS_INTERRUPT_SAFE, "SUPDrvDevExt");
+ if (RT_SUCCESS(rc))
+ rc = RTSpinlockCreate(&pDevExt->hGipSpinlock, RTSPINLOCK_FLAGS_INTERRUPT_SAFE, "SUPDrvGip");
+ if (RT_SUCCESS(rc))
+ rc = RTSpinlockCreate(&pDevExt->hSessionHashTabSpinlock, RTSPINLOCK_FLAGS_INTERRUPT_SAFE, "SUPDrvSession");
+
+ if (RT_SUCCESS(rc))
+#ifdef SUPDRV_USE_MUTEX_FOR_LDR
+ rc = RTSemMutexCreate(&pDevExt->mtxLdr);
+#else
+ rc = RTSemFastMutexCreate(&pDevExt->mtxLdr);
+#endif
+ if (RT_SUCCESS(rc))
+#ifdef SUPDRV_USE_MUTEX_FOR_GIP
+ rc = RTSemMutexCreate(&pDevExt->mtxTscDelta);
+#else
+ rc = RTSemFastMutexCreate(&pDevExt->mtxTscDelta);
+#endif
+ if (RT_SUCCESS(rc))
+ {
+ rc = RTSemFastMutexCreate(&pDevExt->mtxComponentFactory);
+ if (RT_SUCCESS(rc))
+ {
+#ifdef SUPDRV_USE_MUTEX_FOR_GIP
+ rc = RTSemMutexCreate(&pDevExt->mtxGip);
+#else
+ rc = RTSemFastMutexCreate(&pDevExt->mtxGip);
+#endif
+ if (RT_SUCCESS(rc))
+ {
+ rc = supdrvGipCreate(pDevExt);
+ if (RT_SUCCESS(rc))
+ {
+ rc = supdrvTracerInit(pDevExt);
+ if (RT_SUCCESS(rc))
+ {
+ pDevExt->pLdrInitImage = NULL;
+ pDevExt->hLdrInitThread = NIL_RTNATIVETHREAD;
+ pDevExt->hLdrTermThread = NIL_RTNATIVETHREAD;
+ pDevExt->u32Cookie = BIRD; /** @todo make this random? */
+ pDevExt->cbSession = (uint32_t)cbSession;
+
+ /*
+ * Fixup the absolute symbols.
+ *
+ * Because of the table indexing assumptions we'll have a little #ifdef orgy
+ * here rather than distributing this to OS specific files. At least for now.
+ */
+#ifdef RT_OS_DARWIN
+# if ARCH_BITS == 32
+ if (SUPR0GetPagingMode() >= SUPPAGINGMODE_AMD64)
+ {
+ g_aFunctions[0].pfn = (void *)1; /* SUPR0AbsIs64bit */
+ g_aFunctions[1].pfn = (void *)0x80; /* SUPR0Abs64bitKernelCS - KERNEL64_CS, seg.h */
+ g_aFunctions[2].pfn = (void *)0x88; /* SUPR0Abs64bitKernelSS - KERNEL64_SS, seg.h */
+ g_aFunctions[3].pfn = (void *)0x88; /* SUPR0Abs64bitKernelDS - KERNEL64_SS, seg.h */
+ }
+ else
+ g_aFunctions[0].pfn = g_aFunctions[1].pfn = g_aFunctions[2].pfn = g_aFunctions[3].pfn = (void *)0;
+ g_aFunctions[4].pfn = (void *)0x08; /* SUPR0AbsKernelCS - KERNEL_CS, seg.h */
+ g_aFunctions[5].pfn = (void *)0x10; /* SUPR0AbsKernelSS - KERNEL_DS, seg.h */
+ g_aFunctions[6].pfn = (void *)0x10; /* SUPR0AbsKernelDS - KERNEL_DS, seg.h */
+ g_aFunctions[7].pfn = (void *)0x10; /* SUPR0AbsKernelES - KERNEL_DS, seg.h */
+ g_aFunctions[8].pfn = (void *)0x10; /* SUPR0AbsKernelFS - KERNEL_DS, seg.h */
+ g_aFunctions[9].pfn = (void *)0x48; /* SUPR0AbsKernelGS - CPU_DATA_GS, seg.h */
+# else /* 64-bit darwin: */
+ g_aFunctions[0].pfn = (void *)1; /* SUPR0AbsIs64bit */
+ g_aFunctions[1].pfn = (void *)(uintptr_t)ASMGetCS(); /* SUPR0Abs64bitKernelCS */
+ g_aFunctions[2].pfn = (void *)(uintptr_t)ASMGetSS(); /* SUPR0Abs64bitKernelSS */
+ g_aFunctions[3].pfn = (void *)0; /* SUPR0Abs64bitKernelDS */
+ g_aFunctions[4].pfn = (void *)(uintptr_t)ASMGetCS(); /* SUPR0AbsKernelCS */
+ g_aFunctions[5].pfn = (void *)(uintptr_t)ASMGetSS(); /* SUPR0AbsKernelSS */
+ g_aFunctions[6].pfn = (void *)0; /* SUPR0AbsKernelDS */
+ g_aFunctions[7].pfn = (void *)0; /* SUPR0AbsKernelES */
+ g_aFunctions[8].pfn = (void *)0; /* SUPR0AbsKernelFS */
+ g_aFunctions[9].pfn = (void *)0; /* SUPR0AbsKernelGS */
+
+# endif
+#else /* !RT_OS_DARWIN */
+# if ARCH_BITS == 64
+ g_aFunctions[0].pfn = (void *)1; /* SUPR0AbsIs64bit */
+ g_aFunctions[1].pfn = (void *)(uintptr_t)ASMGetCS(); /* SUPR0Abs64bitKernelCS */
+ g_aFunctions[2].pfn = (void *)(uintptr_t)ASMGetSS(); /* SUPR0Abs64bitKernelSS */
+ g_aFunctions[3].pfn = (void *)(uintptr_t)ASMGetDS(); /* SUPR0Abs64bitKernelDS */
+# else
+ g_aFunctions[0].pfn = g_aFunctions[1].pfn = g_aFunctions[2].pfn = g_aFunctions[3].pfn = (void *)0;
+# endif
+ g_aFunctions[4].pfn = (void *)(uintptr_t)ASMGetCS(); /* SUPR0AbsKernelCS */
+ g_aFunctions[5].pfn = (void *)(uintptr_t)ASMGetSS(); /* SUPR0AbsKernelSS */
+ g_aFunctions[6].pfn = (void *)(uintptr_t)ASMGetDS(); /* SUPR0AbsKernelDS */
+ g_aFunctions[7].pfn = (void *)(uintptr_t)ASMGetES(); /* SUPR0AbsKernelES */
+ g_aFunctions[8].pfn = (void *)(uintptr_t)ASMGetFS(); /* SUPR0AbsKernelFS */
+ g_aFunctions[9].pfn = (void *)(uintptr_t)ASMGetGS(); /* SUPR0AbsKernelGS */
+#endif /* !RT_OS_DARWIN */
+ return VINF_SUCCESS;
+ }
+
+ supdrvGipDestroy(pDevExt);
+ }
+
+#ifdef SUPDRV_USE_MUTEX_FOR_GIP
+ RTSemMutexDestroy(pDevExt->mtxGip);
+ pDevExt->mtxGip = NIL_RTSEMMUTEX;
+#else
+ RTSemFastMutexDestroy(pDevExt->mtxGip);
+ pDevExt->mtxGip = NIL_RTSEMFASTMUTEX;
+#endif
+ }
+ RTSemFastMutexDestroy(pDevExt->mtxComponentFactory);
+ pDevExt->mtxComponentFactory = NIL_RTSEMFASTMUTEX;
+ }
+ }
+
+#ifdef SUPDRV_USE_MUTEX_FOR_GIP
+ RTSemMutexDestroy(pDevExt->mtxTscDelta);
+ pDevExt->mtxTscDelta = NIL_RTSEMMUTEX;
+#else
+ RTSemFastMutexDestroy(pDevExt->mtxTscDelta);
+ pDevExt->mtxTscDelta = NIL_RTSEMFASTMUTEX;
+#endif
+#ifdef SUPDRV_USE_MUTEX_FOR_LDR
+ RTSemMutexDestroy(pDevExt->mtxLdr);
+ pDevExt->mtxLdr = NIL_RTSEMMUTEX;
+#else
+ RTSemFastMutexDestroy(pDevExt->mtxLdr);
+ pDevExt->mtxLdr = NIL_RTSEMFASTMUTEX;
+#endif
+ RTSpinlockDestroy(pDevExt->Spinlock);
+ pDevExt->Spinlock = NIL_RTSPINLOCK;
+ RTSpinlockDestroy(pDevExt->hGipSpinlock);
+ pDevExt->hGipSpinlock = NIL_RTSPINLOCK;
+ RTSpinlockDestroy(pDevExt->hSessionHashTabSpinlock);
+ pDevExt->hSessionHashTabSpinlock = NIL_RTSPINLOCK;
+
+#ifdef SUPDRV_WITH_RELEASE_LOGGER
+ RTLogDestroy(RTLogRelSetDefaultInstance(NULL));
+ RTLogDestroy(RTLogSetDefaultInstance(NULL));
+#endif
+
+ return rc;
+}
+
+
+/**
+ * Delete the device extension (e.g. cleanup members).
+ *
+ * @param pDevExt The device extension to delete.
+ */
+void VBOXCALL supdrvDeleteDevExt(PSUPDRVDEVEXT pDevExt)
+{
+ PSUPDRVOBJ pObj;
+ PSUPDRVUSAGE pUsage;
+
+ /*
+ * Kill mutexes and spinlocks.
+ */
+#ifdef SUPDRV_USE_MUTEX_FOR_GIP
+ RTSemMutexDestroy(pDevExt->mtxGip);
+ pDevExt->mtxGip = NIL_RTSEMMUTEX;
+ RTSemMutexDestroy(pDevExt->mtxTscDelta);
+ pDevExt->mtxTscDelta = NIL_RTSEMMUTEX;
+#else
+ RTSemFastMutexDestroy(pDevExt->mtxGip);
+ pDevExt->mtxGip = NIL_RTSEMFASTMUTEX;
+ RTSemFastMutexDestroy(pDevExt->mtxTscDelta);
+ pDevExt->mtxTscDelta = NIL_RTSEMFASTMUTEX;
+#endif
+#ifdef SUPDRV_USE_MUTEX_FOR_LDR
+ RTSemMutexDestroy(pDevExt->mtxLdr);
+ pDevExt->mtxLdr = NIL_RTSEMMUTEX;
+#else
+ RTSemFastMutexDestroy(pDevExt->mtxLdr);
+ pDevExt->mtxLdr = NIL_RTSEMFASTMUTEX;
+#endif
+ RTSpinlockDestroy(pDevExt->Spinlock);
+ pDevExt->Spinlock = NIL_RTSPINLOCK;
+ RTSemFastMutexDestroy(pDevExt->mtxComponentFactory);
+ pDevExt->mtxComponentFactory = NIL_RTSEMFASTMUTEX;
+ RTSpinlockDestroy(pDevExt->hSessionHashTabSpinlock);
+ pDevExt->hSessionHashTabSpinlock = NIL_RTSPINLOCK;
+
+ /*
+ * Free lists.
+ */
+ /* objects. */
+ pObj = pDevExt->pObjs;
+ Assert(!pObj); /* (can trigger on forced unloads) */
+ pDevExt->pObjs = NULL;
+ while (pObj)
+ {
+ void *pvFree = pObj;
+ pObj = pObj->pNext;
+ RTMemFree(pvFree);
+ }
+
+ /* usage records. */
+ pUsage = pDevExt->pUsageFree;
+ pDevExt->pUsageFree = NULL;
+ while (pUsage)
+ {
+ void *pvFree = pUsage;
+ pUsage = pUsage->pNext;
+ RTMemFree(pvFree);
+ }
+
+ /* kill the GIP. */
+ supdrvGipDestroy(pDevExt);
+ RTSpinlockDestroy(pDevExt->hGipSpinlock);
+ pDevExt->hGipSpinlock = NIL_RTSPINLOCK;
+
+ supdrvTracerTerm(pDevExt);
+
+#ifdef SUPDRV_WITH_RELEASE_LOGGER
+ /* destroy the loggers. */
+ RTLogDestroy(RTLogRelSetDefaultInstance(NULL));
+ RTLogDestroy(RTLogSetDefaultInstance(NULL));
+#endif
+}
+
+
+/**
+ * Create session.
+ *
+ * @returns IPRT status code.
+ * @param pDevExt Device extension.
+ * @param fUser Flag indicating whether this is a user or kernel
+ * session.
+ * @param fUnrestricted Unrestricted access (system) or restricted access
+ * (user)?
+ * @param ppSession Where to store the pointer to the session data.
+ */
+int VBOXCALL supdrvCreateSession(PSUPDRVDEVEXT pDevExt, bool fUser, bool fUnrestricted, PSUPDRVSESSION *ppSession)
+{
+ int rc;
+ PSUPDRVSESSION pSession;
+
+ if (!SUP_IS_DEVEXT_VALID(pDevExt))
+ return VERR_INVALID_PARAMETER;
+
+ /*
+ * Allocate memory for the session data.
+ */
+ pSession = *ppSession = (PSUPDRVSESSION)RTMemAllocZ(pDevExt->cbSession);
+ if (pSession)
+ {
+ /* Initialize session data. */
+ rc = RTSpinlockCreate(&pSession->Spinlock, RTSPINLOCK_FLAGS_INTERRUPT_UNSAFE, "SUPDrvSession");
+ if (!rc)
+ {
+ rc = RTHandleTableCreateEx(&pSession->hHandleTable,
+ RTHANDLETABLE_FLAGS_LOCKED_IRQ_SAFE | RTHANDLETABLE_FLAGS_CONTEXT,
+ 1 /*uBase*/, 32768 /*cMax*/, supdrvSessionObjHandleRetain, pSession);
+ if (RT_SUCCESS(rc))
+ {
+ Assert(pSession->Spinlock != NIL_RTSPINLOCK);
+ pSession->pDevExt = pDevExt;
+ pSession->u32Cookie = BIRD_INV;
+ pSession->fUnrestricted = fUnrestricted;
+ /*pSession->fInHashTable = false; */
+ pSession->cRefs = 1;
+ /*pSession->pCommonNextHash = NULL;
+ pSession->ppOsSessionPtr = NULL; */
+ if (fUser)
+ {
+ pSession->Process = RTProcSelf();
+ pSession->R0Process = RTR0ProcHandleSelf();
+ }
+ else
+ {
+ pSession->Process = NIL_RTPROCESS;
+ pSession->R0Process = NIL_RTR0PROCESS;
+ }
+ /*pSession->pLdrUsage = NULL;
+ pSession->pVM = NULL;
+ pSession->pUsage = NULL;
+ pSession->pGip = NULL;
+ pSession->fGipReferenced = false;
+ pSession->Bundle.cUsed = 0; */
+ pSession->Uid = NIL_RTUID;
+ pSession->Gid = NIL_RTGID;
+ /*pSession->uTracerData = 0;*/
+ pSession->hTracerCaller = NIL_RTNATIVETHREAD;
+ RTListInit(&pSession->TpProviders);
+ /*pSession->cTpProviders = 0;*/
+ /*pSession->cTpProbesFiring = 0;*/
+ RTListInit(&pSession->TpUmods);
+ /*RT_ZERO(pSession->apTpLookupTable);*/
+
+ VBOXDRV_SESSION_CREATE(pSession, fUser);
+ LogFlow(("Created session %p initial cookie=%#x\n", pSession, pSession->u32Cookie));
+ return VINF_SUCCESS;
+ }
+
+ RTSpinlockDestroy(pSession->Spinlock);
+ }
+ RTMemFree(pSession);
+ *ppSession = NULL;
+ Log(("Failed to create spinlock, rc=%d!\n", rc));
+ }
+ else
+ rc = VERR_NO_MEMORY;
+
+ return rc;
+}
+
+
+/**
+ * Cleans up the session in the context of the process to which it belongs, the
+ * caller will free the session and the session spinlock.
+ *
+ * This should normally occur when the session is closed or as the process
+ * exits. Careful reference counting in the OS specfic code makes sure that
+ * there cannot be any races between process/handle cleanup callbacks and
+ * threads doing I/O control calls.
+ *
+ * @param pDevExt The device extension.
+ * @param pSession Session data.
+ */
+static void supdrvCleanupSession(PSUPDRVDEVEXT pDevExt, PSUPDRVSESSION pSession)
+{
+ int rc;
+ PSUPDRVBUNDLE pBundle;
+ LogFlow(("supdrvCleanupSession: pSession=%p\n", pSession));
+
+ Assert(!pSession->fInHashTable);
+ Assert(!pSession->ppOsSessionPtr);
+ AssertLogRelMsg(pSession->R0Process == RTR0ProcHandleSelf() || pSession->R0Process == NIL_RTR0PROCESS,
+ ("R0Process=%p cur=%p; curpid=%u\n",
+ pSession->R0Process, RTR0ProcHandleSelf(), RTProcSelf()));
+
+ /*
+ * Remove logger instances related to this session.
+ */
+ RTLogSetDefaultInstanceThread(NULL, (uintptr_t)pSession);
+
+ /*
+ * Destroy the handle table.
+ */
+ rc = RTHandleTableDestroy(pSession->hHandleTable, supdrvSessionObjHandleDelete, pSession);
+ AssertRC(rc);
+ pSession->hHandleTable = NIL_RTHANDLETABLE;
+
+ /*
+ * Release object references made in this session.
+ * In theory there should be noone racing us in this session.
+ */
+ Log2(("release objects - start\n"));
+ if (pSession->pUsage)
+ {
+ PSUPDRVUSAGE pUsage;
+ RTSpinlockAcquire(pDevExt->Spinlock);
+
+ while ((pUsage = pSession->pUsage) != NULL)
+ {
+ PSUPDRVOBJ pObj = pUsage->pObj;
+ pSession->pUsage = pUsage->pNext;
+
+ AssertMsg(pUsage->cUsage >= 1 && pObj->cUsage >= pUsage->cUsage, ("glob %d; sess %d\n", pObj->cUsage, pUsage->cUsage));
+ if (pUsage->cUsage < pObj->cUsage)
+ {
+ pObj->cUsage -= pUsage->cUsage;
+ RTSpinlockRelease(pDevExt->Spinlock);
+ }
+ else
+ {
+ /* Destroy the object and free the record. */
+ if (pDevExt->pObjs == pObj)
+ pDevExt->pObjs = pObj->pNext;
+ else
+ {
+ PSUPDRVOBJ pObjPrev;
+ for (pObjPrev = pDevExt->pObjs; pObjPrev; pObjPrev = pObjPrev->pNext)
+ if (pObjPrev->pNext == pObj)
+ {
+ pObjPrev->pNext = pObj->pNext;
+ break;
+ }
+ Assert(pObjPrev);
+ }
+ RTSpinlockRelease(pDevExt->Spinlock);
+
+ Log(("supdrvCleanupSession: destroying %p/%d (%p/%p) cpid=%RTproc pid=%RTproc dtor=%p\n",
+ pObj, pObj->enmType, pObj->pvUser1, pObj->pvUser2, pObj->CreatorProcess, RTProcSelf(), pObj->pfnDestructor));
+ if (pObj->pfnDestructor)
+ pObj->pfnDestructor(pObj, pObj->pvUser1, pObj->pvUser2);
+ RTMemFree(pObj);
+ }
+
+ /* free it and continue. */
+ RTMemFree(pUsage);
+
+ RTSpinlockAcquire(pDevExt->Spinlock);
+ }
+
+ RTSpinlockRelease(pDevExt->Spinlock);
+ AssertMsg(!pSession->pUsage, ("Some buster reregistered an object during desturction!\n"));
+ }
+ Log2(("release objects - done\n"));
+
+ /*
+ * Make sure the associated VM pointers are NULL.
+ */
+ if (pSession->pSessionGVM || pSession->pSessionVM || pSession->pFastIoCtrlVM)
+ {
+ SUPR0Printf("supdrvCleanupSession: VM not disassociated! pSessionGVM=%p pSessionVM=%p pFastIoCtrlVM=%p\n",
+ pSession->pSessionGVM, pSession->pSessionVM, pSession->pFastIoCtrlVM);
+ pSession->pSessionGVM = NULL;
+ pSession->pSessionVM = NULL;
+ pSession->pFastIoCtrlVM = NULL;
+ }
+
+ /*
+ * Do tracer cleanups related to this session.
+ */
+ Log2(("release tracer stuff - start\n"));
+ supdrvTracerCleanupSession(pDevExt, pSession);
+ Log2(("release tracer stuff - end\n"));
+
+ /*
+ * Release memory allocated in the session.
+ *
+ * We do not serialize this as we assume that the application will
+ * not allocated memory while closing the file handle object.
+ */
+ Log2(("freeing memory:\n"));
+ pBundle = &pSession->Bundle;
+ while (pBundle)
+ {
+ PSUPDRVBUNDLE pToFree;
+ unsigned i;
+
+ /*
+ * Check and unlock all entries in the bundle.
+ */
+ for (i = 0; i < RT_ELEMENTS(pBundle->aMem); i++)
+ {
+ if (pBundle->aMem[i].MemObj != NIL_RTR0MEMOBJ)
+ {
+ Log2(("eType=%d pvR0=%p pvR3=%p cb=%ld\n", pBundle->aMem[i].eType, RTR0MemObjAddress(pBundle->aMem[i].MemObj),
+ (void *)RTR0MemObjAddressR3(pBundle->aMem[i].MapObjR3), (long)RTR0MemObjSize(pBundle->aMem[i].MemObj)));
+ if (pBundle->aMem[i].MapObjR3 != NIL_RTR0MEMOBJ)
+ {
+ rc = RTR0MemObjFree(pBundle->aMem[i].MapObjR3, false);
+ AssertRC(rc); /** @todo figure out how to handle this. */
+ pBundle->aMem[i].MapObjR3 = NIL_RTR0MEMOBJ;
+ }
+ rc = RTR0MemObjFree(pBundle->aMem[i].MemObj, true /* fFreeMappings */);
+ AssertRC(rc); /** @todo figure out how to handle this. */
+ pBundle->aMem[i].MemObj = NIL_RTR0MEMOBJ;
+ pBundle->aMem[i].eType = MEMREF_TYPE_UNUSED;
+ }
+ }
+
+ /*
+ * Advance and free previous bundle.
+ */
+ pToFree = pBundle;
+ pBundle = pBundle->pNext;
+
+ pToFree->pNext = NULL;
+ pToFree->cUsed = 0;
+ if (pToFree != &pSession->Bundle)
+ RTMemFree(pToFree);
+ }
+ Log2(("freeing memory - done\n"));
+
+ /*
+ * Deregister component factories.
+ */
+ RTSemFastMutexRequest(pDevExt->mtxComponentFactory);
+ Log2(("deregistering component factories:\n"));
+ if (pDevExt->pComponentFactoryHead)
+ {
+ PSUPDRVFACTORYREG pPrev = NULL;
+ PSUPDRVFACTORYREG pCur = pDevExt->pComponentFactoryHead;
+ while (pCur)
+ {
+ if (pCur->pSession == pSession)
+ {
+ /* unlink it */
+ PSUPDRVFACTORYREG pNext = pCur->pNext;
+ if (pPrev)
+ pPrev->pNext = pNext;
+ else
+ pDevExt->pComponentFactoryHead = pNext;
+
+ /* free it */
+ pCur->pNext = NULL;
+ pCur->pSession = NULL;
+ pCur->pFactory = NULL;
+ RTMemFree(pCur);
+
+ /* next */
+ pCur = pNext;
+ }
+ else
+ {
+ /* next */
+ pPrev = pCur;
+ pCur = pCur->pNext;
+ }
+ }
+ }
+ RTSemFastMutexRelease(pDevExt->mtxComponentFactory);
+ Log2(("deregistering component factories - done\n"));
+
+ /*
+ * Loaded images needs to be dereferenced and possibly freed up.
+ */
+ supdrvLdrLock(pDevExt);
+ Log2(("freeing images:\n"));
+ if (pSession->pLdrUsage)
+ {
+ PSUPDRVLDRUSAGE pUsage = pSession->pLdrUsage;
+ pSession->pLdrUsage = NULL;
+ while (pUsage)
+ {
+ void *pvFree = pUsage;
+ PSUPDRVLDRIMAGE pImage = pUsage->pImage;
+ uint32_t cUsage = pUsage->cRing0Usage + pUsage->cRing3Usage;
+ if (pImage->cImgUsage > cUsage)
+ supdrvLdrSubtractUsage(pDevExt, pImage, cUsage);
+ else
+ supdrvLdrFree(pDevExt, pImage);
+ pUsage->pImage = NULL;
+ pUsage = pUsage->pNext;
+ RTMemFree(pvFree);
+ }
+ }
+ supdrvLdrUnlock(pDevExt);
+ Log2(("freeing images - done\n"));
+
+ /*
+ * Unmap the GIP.
+ */
+ Log2(("umapping GIP:\n"));
+ if (pSession->GipMapObjR3 != NIL_RTR0MEMOBJ)
+ {
+ SUPR0GipUnmap(pSession);
+ pSession->fGipReferenced = 0;
+ }
+ Log2(("umapping GIP - done\n"));
+}
+
+
+/**
+ * Common code for freeing a session when the reference count reaches zero.
+ *
+ * @param pDevExt Device extension.
+ * @param pSession Session data.
+ * This data will be freed by this routine.
+ */
+static void supdrvDestroySession(PSUPDRVDEVEXT pDevExt, PSUPDRVSESSION pSession)
+{
+ VBOXDRV_SESSION_CLOSE(pSession);
+
+ /*
+ * Cleanup the session first.
+ */
+ supdrvCleanupSession(pDevExt, pSession);
+ supdrvOSCleanupSession(pDevExt, pSession);
+
+ /*
+ * Free the rest of the session stuff.
+ */
+ RTSpinlockDestroy(pSession->Spinlock);
+ pSession->Spinlock = NIL_RTSPINLOCK;
+ pSession->pDevExt = NULL;
+ RTMemFree(pSession);
+ LogFlow(("supdrvDestroySession: returns\n"));
+}
+
+
+/**
+ * Inserts the session into the global hash table.
+ *
+ * @retval VINF_SUCCESS on success.
+ * @retval VERR_WRONG_ORDER if the session was already inserted (asserted).
+ * @retval VERR_INVALID_PARAMETER if the session handle is invalid or a ring-0
+ * session (asserted).
+ * @retval VERR_DUPLICATE if there is already a session for that pid.
+ *
+ * @param pDevExt The device extension.
+ * @param pSession The session.
+ * @param ppOsSessionPtr Pointer to the OS session pointer, if any is
+ * available and used. This will set to point to the
+ * session while under the protection of the session
+ * hash table spinlock. It will also be kept in
+ * PSUPDRVSESSION::ppOsSessionPtr for lookup and
+ * cleanup use.
+ * @param pvUser Argument for supdrvOSSessionHashTabInserted.
+ */
+int VBOXCALL supdrvSessionHashTabInsert(PSUPDRVDEVEXT pDevExt, PSUPDRVSESSION pSession, PSUPDRVSESSION *ppOsSessionPtr,
+ void *pvUser)
+{
+ PSUPDRVSESSION pCur;
+ unsigned iHash;
+
+ /*
+ * Validate input.
+ */
+ AssertReturn(SUP_IS_SESSION_VALID(pSession), VERR_INVALID_PARAMETER);
+ AssertReturn(pSession->R0Process != NIL_RTR0PROCESS, VERR_INVALID_PARAMETER);
+
+ /*
+ * Calculate the hash table index and acquire the spinlock.
+ */
+ iHash = SUPDRV_SESSION_HASH(pSession->Process);
+
+ RTSpinlockAcquire(pDevExt->hSessionHashTabSpinlock);
+
+ /*
+ * If there are a collisions, we need to carefully check if we got a
+ * duplicate. There can only be one open session per process.
+ */
+ pCur = pDevExt->apSessionHashTab[iHash];
+ if (pCur)
+ {
+ while (pCur && pCur->Process != pSession->Process)
+ pCur = pCur->pCommonNextHash;
+
+ if (pCur)
+ {
+ RTSpinlockRelease(pDevExt->hSessionHashTabSpinlock);
+ if (pCur == pSession)
+ {
+ Assert(pSession->fInHashTable);
+ AssertFailed();
+ return VERR_WRONG_ORDER;
+ }
+ Assert(!pSession->fInHashTable);
+ if (pCur->R0Process == pSession->R0Process)
+ return VERR_RESOURCE_IN_USE;
+ return VERR_DUPLICATE;
+ }
+ }
+ Assert(!pSession->fInHashTable);
+ Assert(!pSession->ppOsSessionPtr);
+
+ /*
+ * Insert it, doing a callout to the OS specific code in case it has
+ * anything it wishes to do while we're holding the spinlock.
+ */
+ pSession->pCommonNextHash = pDevExt->apSessionHashTab[iHash];
+ pDevExt->apSessionHashTab[iHash] = pSession;
+ pSession->fInHashTable = true;
+ ASMAtomicIncS32(&pDevExt->cSessions);
+
+ pSession->ppOsSessionPtr = ppOsSessionPtr;
+ if (ppOsSessionPtr)
+ ASMAtomicWritePtr(ppOsSessionPtr, pSession);
+
+ supdrvOSSessionHashTabInserted(pDevExt, pSession, pvUser);
+
+ /*
+ * Retain a reference for the pointer in the session table.
+ */
+ ASMAtomicIncU32(&pSession->cRefs);
+
+ RTSpinlockRelease(pDevExt->hSessionHashTabSpinlock);
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * Removes the session from the global hash table.
+ *
+ * @retval VINF_SUCCESS on success.
+ * @retval VERR_NOT_FOUND if the session was already removed (asserted).
+ * @retval VERR_INVALID_PARAMETER if the session handle is invalid or a ring-0
+ * session (asserted).
+ *
+ * @param pDevExt The device extension.
+ * @param pSession The session. The caller is expected to have a reference
+ * to this so it won't croak on us when we release the hash
+ * table reference.
+ * @param pvUser OS specific context value for the
+ * supdrvOSSessionHashTabInserted callback.
+ */
+int VBOXCALL supdrvSessionHashTabRemove(PSUPDRVDEVEXT pDevExt, PSUPDRVSESSION pSession, void *pvUser)
+{
+ PSUPDRVSESSION pCur;
+ unsigned iHash;
+ int32_t cRefs;
+
+ /*
+ * Validate input.
+ */
+ AssertReturn(SUP_IS_SESSION_VALID(pSession), VERR_INVALID_PARAMETER);
+ AssertReturn(pSession->R0Process != NIL_RTR0PROCESS, VERR_INVALID_PARAMETER);
+
+ /*
+ * Calculate the hash table index and acquire the spinlock.
+ */
+ iHash = SUPDRV_SESSION_HASH(pSession->Process);
+
+ RTSpinlockAcquire(pDevExt->hSessionHashTabSpinlock);
+
+ /*
+ * Unlink it.
+ */
+ pCur = pDevExt->apSessionHashTab[iHash];
+ if (pCur == pSession)
+ pDevExt->apSessionHashTab[iHash] = pSession->pCommonNextHash;
+ else
+ {
+ PSUPDRVSESSION pPrev = pCur;
+ while (pCur && pCur != pSession)
+ {
+ pPrev = pCur;
+ pCur = pCur->pCommonNextHash;
+ }
+ if (pCur)
+ pPrev->pCommonNextHash = pCur->pCommonNextHash;
+ else
+ {
+ Assert(!pSession->fInHashTable);
+ RTSpinlockRelease(pDevExt->hSessionHashTabSpinlock);
+ return VERR_NOT_FOUND;
+ }
+ }
+
+ pSession->pCommonNextHash = NULL;
+ pSession->fInHashTable = false;
+
+ ASMAtomicDecS32(&pDevExt->cSessions);
+
+ /*
+ * Clear OS specific session pointer if available and do the OS callback.
+ */
+ if (pSession->ppOsSessionPtr)
+ {
+ ASMAtomicCmpXchgPtr(pSession->ppOsSessionPtr, NULL, pSession);
+ pSession->ppOsSessionPtr = NULL;
+ }
+
+ supdrvOSSessionHashTabRemoved(pDevExt, pSession, pvUser);
+
+ RTSpinlockRelease(pDevExt->hSessionHashTabSpinlock);
+
+ /*
+ * Drop the reference the hash table had to the session. This shouldn't
+ * be the last reference!
+ */
+ cRefs = ASMAtomicDecU32(&pSession->cRefs);
+ Assert(cRefs > 0 && cRefs < _1M);
+ if (cRefs == 0)
+ supdrvDestroySession(pDevExt, pSession);
+
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * Looks up the session for the current process in the global hash table or in
+ * OS specific pointer.
+ *
+ * @returns Pointer to the session with a reference that the caller must
+ * release. If no valid session was found, NULL is returned.
+ *
+ * @param pDevExt The device extension.
+ * @param Process The process ID.
+ * @param R0Process The ring-0 process handle.
+ * @param ppOsSessionPtr The OS session pointer if available. If not NULL,
+ * this is used instead of the hash table. For
+ * additional safety it must then be equal to the
+ * SUPDRVSESSION::ppOsSessionPtr member.
+ * This can be NULL even if the OS has a session
+ * pointer.
+ */
+PSUPDRVSESSION VBOXCALL supdrvSessionHashTabLookup(PSUPDRVDEVEXT pDevExt, RTPROCESS Process, RTR0PROCESS R0Process,
+ PSUPDRVSESSION *ppOsSessionPtr)
+{
+ PSUPDRVSESSION pCur;
+ unsigned iHash;
+
+ /*
+ * Validate input.
+ */
+ AssertReturn(R0Process != NIL_RTR0PROCESS, NULL);
+
+ /*
+ * Calculate the hash table index and acquire the spinlock.
+ */
+ iHash = SUPDRV_SESSION_HASH(Process);
+
+ RTSpinlockAcquire(pDevExt->hSessionHashTabSpinlock);
+
+ /*
+ * If an OS session pointer is provided, always use it.
+ */
+ if (ppOsSessionPtr)
+ {
+ pCur = *ppOsSessionPtr;
+ if ( pCur
+ && ( pCur->ppOsSessionPtr != ppOsSessionPtr
+ || pCur->Process != Process
+ || pCur->R0Process != R0Process) )
+ pCur = NULL;
+ }
+ else
+ {
+ /*
+ * Otherwise, do the hash table lookup.
+ */
+ pCur = pDevExt->apSessionHashTab[iHash];
+ while ( pCur
+ && ( pCur->Process != Process
+ || pCur->R0Process != R0Process) )
+ pCur = pCur->pCommonNextHash;
+ }
+
+ /*
+ * Retain the session.
+ */
+ if (pCur)
+ {
+ uint32_t cRefs = ASMAtomicIncU32(&pCur->cRefs);
+ NOREF(cRefs);
+ Assert(cRefs > 1 && cRefs < _1M);
+ }
+
+ RTSpinlockRelease(pDevExt->hSessionHashTabSpinlock);
+
+ return pCur;
+}
+
+
+/**
+ * Retain a session to make sure it doesn't go away while it is in use.
+ *
+ * @returns New reference count on success, UINT32_MAX on failure.
+ * @param pSession Session data.
+ */
+uint32_t VBOXCALL supdrvSessionRetain(PSUPDRVSESSION pSession)
+{
+ uint32_t cRefs;
+ AssertPtrReturn(pSession, UINT32_MAX);
+ AssertReturn(SUP_IS_SESSION_VALID(pSession), UINT32_MAX);
+
+ cRefs = ASMAtomicIncU32(&pSession->cRefs);
+ AssertMsg(cRefs > 1 && cRefs < _1M, ("%#x %p\n", cRefs, pSession));
+ return cRefs;
+}
+
+
+/**
+ * Releases a given session.
+ *
+ * @returns New reference count on success (0 if closed), UINT32_MAX on failure.
+ * @param pSession Session data.
+ */
+uint32_t VBOXCALL supdrvSessionRelease(PSUPDRVSESSION pSession)
+{
+ uint32_t cRefs;
+ AssertPtrReturn(pSession, UINT32_MAX);
+ AssertReturn(SUP_IS_SESSION_VALID(pSession), UINT32_MAX);
+
+ cRefs = ASMAtomicDecU32(&pSession->cRefs);
+ AssertMsg(cRefs < _1M, ("%#x %p\n", cRefs, pSession));
+ if (cRefs == 0)
+ supdrvDestroySession(pSession->pDevExt, pSession);
+ return cRefs;
+}
+
+
+/**
+ * RTHandleTableDestroy callback used by supdrvCleanupSession.
+ *
+ * @returns IPRT status code, see SUPR0ObjAddRef.
+ * @param hHandleTable The handle table handle. Ignored.
+ * @param pvObj The object pointer.
+ * @param pvCtx Context, the handle type. Ignored.
+ * @param pvUser Session pointer.
+ */
+static DECLCALLBACK(int) supdrvSessionObjHandleRetain(RTHANDLETABLE hHandleTable, void *pvObj, void *pvCtx, void *pvUser)
+{
+ NOREF(pvCtx);
+ NOREF(hHandleTable);
+ return SUPR0ObjAddRefEx(pvObj, (PSUPDRVSESSION)pvUser, true /*fNoBlocking*/);
+}
+
+
+/**
+ * RTHandleTableDestroy callback used by supdrvCleanupSession.
+ *
+ * @param hHandleTable The handle table handle. Ignored.
+ * @param h The handle value. Ignored.
+ * @param pvObj The object pointer.
+ * @param pvCtx Context, the handle type. Ignored.
+ * @param pvUser Session pointer.
+ */
+static DECLCALLBACK(void) supdrvSessionObjHandleDelete(RTHANDLETABLE hHandleTable, uint32_t h, void *pvObj, void *pvCtx, void *pvUser)
+{
+ NOREF(pvCtx);
+ NOREF(h);
+ NOREF(hHandleTable);
+ SUPR0ObjRelease(pvObj, (PSUPDRVSESSION)pvUser);
+}
+
+
+/**
+ * Fast path I/O Control worker.
+ *
+ * @returns VBox status code that should be passed down to ring-3 unchanged.
+ * @param uOperation SUP_VMMR0_DO_XXX (not the I/O control number!).
+ * @param idCpu VMCPU id.
+ * @param pDevExt Device extention.
+ * @param pSession Session data.
+ */
+int VBOXCALL supdrvIOCtlFast(uintptr_t uOperation, VMCPUID idCpu, PSUPDRVDEVEXT pDevExt, PSUPDRVSESSION pSession)
+{
+ /*
+ * Validate input and check that the VM has a session.
+ */
+ if (RT_LIKELY(RT_VALID_PTR(pSession)))
+ {
+ PVM pVM = pSession->pSessionVM;
+ PGVM pGVM = pSession->pSessionGVM;
+ if (RT_LIKELY( pGVM != NULL
+ && pVM != NULL
+ && pVM == pSession->pFastIoCtrlVM))
+ {
+ if (RT_LIKELY(pDevExt->pfnVMMR0EntryFast))
+ {
+ /*
+ * Make the call.
+ */
+ pDevExt->pfnVMMR0EntryFast(pGVM, pVM, idCpu, uOperation);
+ return VINF_SUCCESS;
+ }
+
+ SUPR0Printf("supdrvIOCtlFast: pfnVMMR0EntryFast is NULL\n");
+ }
+ else
+ SUPR0Printf("supdrvIOCtlFast: Misconfig session: pGVM=%p pVM=%p pFastIoCtrlVM=%p\n",
+ pGVM, pVM, pSession->pFastIoCtrlVM);
+ }
+ else
+ SUPR0Printf("supdrvIOCtlFast: Bad session pointer %p\n", pSession);
+ return VERR_INTERNAL_ERROR;
+}
+
+
+/**
+ * Helper for supdrvIOCtl used to validate module names passed to SUP_IOCTL_LDR_OPEN.
+ *
+ * Check if pszStr contains any character of pszChars. We would use strpbrk
+ * here if this function would be contained in the RedHat kABI white list, see
+ * http://www.kerneldrivers.org/RHEL5.
+ *
+ * @returns true if fine, false if not.
+ * @param pszName The module name to check.
+ */
+static bool supdrvIsLdrModuleNameValid(const char *pszName)
+{
+ int chCur;
+ while ((chCur = *pszName++) != '\0')
+ {
+ static const char s_szInvalidChars[] = ";:()[]{}/\\|&*%#@!~`\"'";
+ unsigned offInv = RT_ELEMENTS(s_szInvalidChars);
+ while (offInv-- > 0)
+ if (s_szInvalidChars[offInv] == chCur)
+ return false;
+ }
+ return true;
+}
+
+
+
+/**
+ * I/O Control inner worker (tracing reasons).
+ *
+ * @returns IPRT status code.
+ * @retval VERR_INVALID_PARAMETER if the request is invalid.
+ *
+ * @param uIOCtl Function number.
+ * @param pDevExt Device extention.
+ * @param pSession Session data.
+ * @param pReqHdr The request header.
+ */
+static int supdrvIOCtlInnerUnrestricted(uintptr_t uIOCtl, PSUPDRVDEVEXT pDevExt, PSUPDRVSESSION pSession, PSUPREQHDR pReqHdr)
+{
+ /*
+ * Validation macros
+ */
+#define REQ_CHECK_SIZES_EX(Name, cbInExpect, cbOutExpect) \
+ do { \
+ if (RT_UNLIKELY(pReqHdr->cbIn != (cbInExpect) || pReqHdr->cbOut != (cbOutExpect))) \
+ { \
+ OSDBGPRINT(( #Name ": Invalid input/output sizes. cbIn=%ld expected %ld. cbOut=%ld expected %ld.\n", \
+ (long)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_UNLIKELY(pReqHdr->cbIn != (cbInExpect))) \
+ { \
+ OSDBGPRINT(( #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_UNLIKELY(pReqHdr->cbOut != (cbOutExpect))) \
+ { \
+ OSDBGPRINT(( #Name ": Invalid input/output sizes. cbOut=%ld expected %ld.\n", \
+ (long)pReqHdr->cbOut, (long)(cbOutExpect))); \
+ return pReqHdr->rc = VERR_INVALID_PARAMETER; \
+ } \
+ } while (0)
+
+#define REQ_CHECK_EXPR(Name, expr) \
+ do { \
+ if (RT_UNLIKELY(!(expr))) \
+ { \
+ OSDBGPRINT(( #Name ": %s\n", #expr)); \
+ return pReqHdr->rc = VERR_INVALID_PARAMETER; \
+ } \
+ } while (0)
+
+#define REQ_CHECK_EXPR_FMT(expr, fmt) \
+ do { \
+ if (RT_UNLIKELY(!(expr))) \
+ { \
+ OSDBGPRINT( fmt ); \
+ return pReqHdr->rc = VERR_INVALID_PARAMETER; \
+ } \
+ } while (0)
+
+ /*
+ * The switch.
+ */
+ switch (SUP_CTL_CODE_NO_SIZE(uIOCtl))
+ {
+ case SUP_CTL_CODE_NO_SIZE(SUP_IOCTL_COOKIE):
+ {
+ PSUPCOOKIE pReq = (PSUPCOOKIE)pReqHdr;
+ REQ_CHECK_SIZES(SUP_IOCTL_COOKIE);
+ if (strncmp(pReq->u.In.szMagic, SUPCOOKIE_MAGIC, sizeof(pReq->u.In.szMagic)))
+ {
+ OSDBGPRINT(("SUP_IOCTL_COOKIE: invalid magic %.16s\n", pReq->u.In.szMagic));
+ pReq->Hdr.rc = VERR_INVALID_MAGIC;
+ return 0;
+ }
+
+#if 0
+ /*
+ * Call out to the OS specific code and let it do permission checks on the
+ * client process.
+ */
+ if (!supdrvOSValidateClientProcess(pDevExt, pSession))
+ {
+ pReq->u.Out.u32Cookie = 0xffffffff;
+ pReq->u.Out.u32SessionCookie = 0xffffffff;
+ pReq->u.Out.u32SessionVersion = 0xffffffff;
+ pReq->u.Out.u32DriverVersion = SUPDRV_IOC_VERSION;
+ pReq->u.Out.pSession = NULL;
+ pReq->u.Out.cFunctions = 0;
+ pReq->Hdr.rc = VERR_PERMISSION_DENIED;
+ return 0;
+ }
+#endif
+
+ /*
+ * Match the version.
+ * The current logic is very simple, match the major interface version.
+ */
+ if ( pReq->u.In.u32MinVersion > SUPDRV_IOC_VERSION
+ || (pReq->u.In.u32MinVersion & 0xffff0000) != (SUPDRV_IOC_VERSION & 0xffff0000))
+ {
+ OSDBGPRINT(("SUP_IOCTL_COOKIE: Version mismatch. Requested: %#x Min: %#x Current: %#x\n",
+ pReq->u.In.u32ReqVersion, pReq->u.In.u32MinVersion, SUPDRV_IOC_VERSION));
+ pReq->u.Out.u32Cookie = 0xffffffff;
+ pReq->u.Out.u32SessionCookie = 0xffffffff;
+ pReq->u.Out.u32SessionVersion = 0xffffffff;
+ pReq->u.Out.u32DriverVersion = SUPDRV_IOC_VERSION;
+ pReq->u.Out.pSession = NULL;
+ pReq->u.Out.cFunctions = 0;
+ pReq->Hdr.rc = VERR_VERSION_MISMATCH;
+ return 0;
+ }
+
+ /*
+ * Fill in return data and be gone.
+ * N.B. The first one to change SUPDRV_IOC_VERSION shall makes sure that
+ * u32SessionVersion <= u32ReqVersion!
+ */
+ /** @todo Somehow validate the client and negotiate a secure cookie... */
+ pReq->u.Out.u32Cookie = pDevExt->u32Cookie;
+ pReq->u.Out.u32SessionCookie = pSession->u32Cookie;
+ pReq->u.Out.u32SessionVersion = SUPDRV_IOC_VERSION;
+ pReq->u.Out.u32DriverVersion = SUPDRV_IOC_VERSION;
+ pReq->u.Out.pSession = pSession;
+ pReq->u.Out.cFunctions = sizeof(g_aFunctions) / sizeof(g_aFunctions[0]);
+ pReq->Hdr.rc = VINF_SUCCESS;
+ return 0;
+ }
+
+ case SUP_CTL_CODE_NO_SIZE(SUP_IOCTL_QUERY_FUNCS(0)):
+ {
+ /* validate */
+ PSUPQUERYFUNCS pReq = (PSUPQUERYFUNCS)pReqHdr;
+ REQ_CHECK_SIZES_EX(SUP_IOCTL_QUERY_FUNCS, SUP_IOCTL_QUERY_FUNCS_SIZE_IN, SUP_IOCTL_QUERY_FUNCS_SIZE_OUT(RT_ELEMENTS(g_aFunctions)));
+
+ /* execute */
+ pReq->u.Out.cFunctions = RT_ELEMENTS(g_aFunctions);
+ RT_BCOPY_UNFORTIFIED(&pReq->u.Out.aFunctions[0], g_aFunctions, sizeof(g_aFunctions));
+ pReq->Hdr.rc = VINF_SUCCESS;
+ return 0;
+ }
+
+ case SUP_CTL_CODE_NO_SIZE(SUP_IOCTL_PAGE_LOCK):
+ {
+ /* validate */
+ PSUPPAGELOCK pReq = (PSUPPAGELOCK)pReqHdr;
+ REQ_CHECK_SIZE_IN(SUP_IOCTL_PAGE_LOCK, SUP_IOCTL_PAGE_LOCK_SIZE_IN);
+ REQ_CHECK_SIZE_OUT(SUP_IOCTL_PAGE_LOCK, SUP_IOCTL_PAGE_LOCK_SIZE_OUT(pReq->u.In.cPages));
+ REQ_CHECK_EXPR(SUP_IOCTL_PAGE_LOCK, pReq->u.In.cPages > 0);
+ REQ_CHECK_EXPR(SUP_IOCTL_PAGE_LOCK, pReq->u.In.pvR3 >= PAGE_SIZE);
+
+ /* execute */
+ pReq->Hdr.rc = SUPR0LockMem(pSession, pReq->u.In.pvR3, pReq->u.In.cPages, &pReq->u.Out.aPages[0]);
+ if (RT_FAILURE(pReq->Hdr.rc))
+ pReq->Hdr.cbOut = sizeof(pReq->Hdr);
+ return 0;
+ }
+
+ case SUP_CTL_CODE_NO_SIZE(SUP_IOCTL_PAGE_UNLOCK):
+ {
+ /* validate */
+ PSUPPAGEUNLOCK pReq = (PSUPPAGEUNLOCK)pReqHdr;
+ REQ_CHECK_SIZES(SUP_IOCTL_PAGE_UNLOCK);
+
+ /* execute */
+ pReq->Hdr.rc = SUPR0UnlockMem(pSession, pReq->u.In.pvR3);
+ return 0;
+ }
+
+ case SUP_CTL_CODE_NO_SIZE(SUP_IOCTL_CONT_ALLOC):
+ {
+ /* validate */
+ PSUPCONTALLOC pReq = (PSUPCONTALLOC)pReqHdr;
+ REQ_CHECK_SIZES(SUP_IOCTL_CONT_ALLOC);
+
+ /* execute */
+ pReq->Hdr.rc = SUPR0ContAlloc(pSession, pReq->u.In.cPages, &pReq->u.Out.pvR0, &pReq->u.Out.pvR3, &pReq->u.Out.HCPhys);
+ if (RT_FAILURE(pReq->Hdr.rc))
+ pReq->Hdr.cbOut = sizeof(pReq->Hdr);
+ return 0;
+ }
+
+ case SUP_CTL_CODE_NO_SIZE(SUP_IOCTL_CONT_FREE):
+ {
+ /* validate */
+ PSUPCONTFREE pReq = (PSUPCONTFREE)pReqHdr;
+ REQ_CHECK_SIZES(SUP_IOCTL_CONT_FREE);
+
+ /* execute */
+ pReq->Hdr.rc = SUPR0ContFree(pSession, (RTHCUINTPTR)pReq->u.In.pvR3);
+ return 0;
+ }
+
+ case SUP_CTL_CODE_NO_SIZE(SUP_IOCTL_LDR_OPEN):
+ {
+ /* validate */
+ PSUPLDROPEN pReq = (PSUPLDROPEN)pReqHdr;
+ REQ_CHECK_SIZES(SUP_IOCTL_LDR_OPEN);
+ if ( pReq->u.In.cbImageWithEverything != 0
+ || pReq->u.In.cbImageBits != 0)
+ {
+ REQ_CHECK_EXPR(SUP_IOCTL_LDR_OPEN, pReq->u.In.cbImageWithEverything > 0);
+ REQ_CHECK_EXPR(SUP_IOCTL_LDR_OPEN, pReq->u.In.cbImageWithEverything < 16*_1M);
+ REQ_CHECK_EXPR(SUP_IOCTL_LDR_OPEN, pReq->u.In.cbImageBits > 0);
+ REQ_CHECK_EXPR(SUP_IOCTL_LDR_OPEN, pReq->u.In.cbImageBits < pReq->u.In.cbImageWithEverything);
+ }
+ REQ_CHECK_EXPR(SUP_IOCTL_LDR_OPEN, pReq->u.In.szName[0]);
+ REQ_CHECK_EXPR(SUP_IOCTL_LDR_OPEN, RTStrEnd(pReq->u.In.szName, sizeof(pReq->u.In.szName)));
+ REQ_CHECK_EXPR(SUP_IOCTL_LDR_OPEN, supdrvIsLdrModuleNameValid(pReq->u.In.szName));
+ REQ_CHECK_EXPR(SUP_IOCTL_LDR_OPEN, RTStrEnd(pReq->u.In.szFilename, sizeof(pReq->u.In.szFilename)));
+
+ /* execute */
+ pReq->Hdr.rc = supdrvIOCtl_LdrOpen(pDevExt, pSession, pReq);
+ return 0;
+ }
+
+ case SUP_CTL_CODE_NO_SIZE(SUP_IOCTL_LDR_LOAD):
+ {
+ /* validate */
+ PSUPLDRLOAD pReq = (PSUPLDRLOAD)pReqHdr;
+ REQ_CHECK_EXPR(Name, pReq->Hdr.cbIn >= SUP_IOCTL_LDR_LOAD_SIZE_IN(32));
+ REQ_CHECK_SIZES_EX(SUP_IOCTL_LDR_LOAD, SUP_IOCTL_LDR_LOAD_SIZE_IN(pReq->u.In.cbImageWithEverything), SUP_IOCTL_LDR_LOAD_SIZE_OUT);
+ REQ_CHECK_EXPR_FMT( !pReq->u.In.cSymbols
+ || ( pReq->u.In.cSymbols <= 16384
+ && pReq->u.In.offSymbols >= pReq->u.In.cbImageBits
+ && pReq->u.In.offSymbols < pReq->u.In.cbImageWithEverything
+ && pReq->u.In.offSymbols + pReq->u.In.cSymbols * sizeof(SUPLDRSYM) <= pReq->u.In.cbImageWithEverything),
+ ("SUP_IOCTL_LDR_LOAD: offSymbols=%#lx cSymbols=%#lx cbImageWithEverything=%#lx\n", (long)pReq->u.In.offSymbols,
+ (long)pReq->u.In.cSymbols, (long)pReq->u.In.cbImageWithEverything));
+ REQ_CHECK_EXPR_FMT( !pReq->u.In.cbStrTab
+ || ( pReq->u.In.offStrTab < pReq->u.In.cbImageWithEverything
+ && pReq->u.In.offStrTab >= pReq->u.In.cbImageBits
+ && pReq->u.In.offStrTab + pReq->u.In.cbStrTab <= pReq->u.In.cbImageWithEverything
+ && pReq->u.In.cbStrTab <= pReq->u.In.cbImageWithEverything),
+ ("SUP_IOCTL_LDR_LOAD: offStrTab=%#lx cbStrTab=%#lx cbImageWithEverything=%#lx\n", (long)pReq->u.In.offStrTab,
+ (long)pReq->u.In.cbStrTab, (long)pReq->u.In.cbImageWithEverything));
+ REQ_CHECK_EXPR_FMT( pReq->u.In.cSegments >= 1
+ && pReq->u.In.cSegments <= 128
+ && pReq->u.In.cSegments <= (pReq->u.In.cbImageBits + PAGE_SIZE - 1) / PAGE_SIZE
+ && pReq->u.In.offSegments >= pReq->u.In.cbImageBits
+ && pReq->u.In.offSegments < pReq->u.In.cbImageWithEverything
+ && pReq->u.In.offSegments + pReq->u.In.cSegments * sizeof(SUPLDRSEG) <= pReq->u.In.cbImageWithEverything,
+ ("SUP_IOCTL_LDR_LOAD: offSegments=%#lx cSegments=%#lx cbImageWithEverything=%#lx\n", (long)pReq->u.In.offSegments,
+ (long)pReq->u.In.cSegments, (long)pReq->u.In.cbImageWithEverything));
+
+ if (pReq->u.In.cSymbols)
+ {
+ uint32_t i;
+ PSUPLDRSYM paSyms = (PSUPLDRSYM)&pReq->u.In.abImage[pReq->u.In.offSymbols];
+ for (i = 0; i < pReq->u.In.cSymbols; i++)
+ {
+ REQ_CHECK_EXPR_FMT(paSyms[i].offSymbol < pReq->u.In.cbImageWithEverything,
+ ("SUP_IOCTL_LDR_LOAD: sym #%ld: symb off %#lx (max=%#lx)\n", (long)i, (long)paSyms[i].offSymbol, (long)pReq->u.In.cbImageWithEverything));
+ REQ_CHECK_EXPR_FMT(paSyms[i].offName < pReq->u.In.cbStrTab,
+ ("SUP_IOCTL_LDR_LOAD: sym #%ld: name off %#lx (max=%#lx)\n", (long)i, (long)paSyms[i].offName, (long)pReq->u.In.cbImageWithEverything));
+ REQ_CHECK_EXPR_FMT(RTStrEnd((char const *)&pReq->u.In.abImage[pReq->u.In.offStrTab + paSyms[i].offName],
+ pReq->u.In.cbStrTab - paSyms[i].offName),
+ ("SUP_IOCTL_LDR_LOAD: sym #%ld: unterminated name! (%#lx / %#lx)\n", (long)i, (long)paSyms[i].offName, (long)pReq->u.In.cbImageWithEverything));
+ }
+ }
+ {
+ uint32_t i;
+ uint32_t offPrevEnd = 0;
+ PSUPLDRSEG paSegs = (PSUPLDRSEG)&pReq->u.In.abImage[pReq->u.In.offSegments];
+ for (i = 0; i < pReq->u.In.cSegments; i++)
+ {
+ REQ_CHECK_EXPR_FMT(paSegs[i].off < pReq->u.In.cbImageBits && !(paSegs[i].off & PAGE_OFFSET_MASK),
+ ("SUP_IOCTL_LDR_LOAD: seg #%ld: off %#lx (max=%#lx)\n", (long)i, (long)paSegs[i].off, (long)pReq->u.In.cbImageBits));
+ REQ_CHECK_EXPR_FMT(paSegs[i].cb <= pReq->u.In.cbImageBits,
+ ("SUP_IOCTL_LDR_LOAD: seg #%ld: cb %#lx (max=%#lx)\n", (long)i, (long)paSegs[i].cb, (long)pReq->u.In.cbImageBits));
+ REQ_CHECK_EXPR_FMT(paSegs[i].off + paSegs[i].cb <= pReq->u.In.cbImageBits,
+ ("SUP_IOCTL_LDR_LOAD: seg #%ld: off %#lx + cb %#lx = %#lx (max=%#lx)\n", (long)i, (long)paSegs[i].off, (long)paSegs[i].cb, (long)(paSegs[i].off + paSegs[i].cb), (long)pReq->u.In.cbImageBits));
+ REQ_CHECK_EXPR_FMT(paSegs[i].fProt != 0,
+ ("SUP_IOCTL_LDR_LOAD: seg #%ld: off %#lx + cb %#lx\n", (long)i, (long)paSegs[i].off, (long)paSegs[i].cb));
+ REQ_CHECK_EXPR_FMT(paSegs[i].fUnused == 0, ("SUP_IOCTL_LDR_LOAD: seg #%ld: fUnused=1\n", (long)i));
+ REQ_CHECK_EXPR_FMT(offPrevEnd == paSegs[i].off,
+ ("SUP_IOCTL_LDR_LOAD: seg #%ld: off %#lx offPrevEnd %#lx\n", (long)i, (long)paSegs[i].off, (long)offPrevEnd));
+ offPrevEnd = paSegs[i].off + paSegs[i].cb;
+ }
+ REQ_CHECK_EXPR_FMT(offPrevEnd == pReq->u.In.cbImageBits,
+ ("SUP_IOCTL_LDR_LOAD: offPrevEnd %#lx cbImageBits %#lx\n", (long)i, (long)offPrevEnd, (long)pReq->u.In.cbImageBits));
+ }
+ REQ_CHECK_EXPR_FMT(!(pReq->u.In.fFlags & ~SUPLDRLOAD_F_VALID_MASK),
+ ("SUP_IOCTL_LDR_LOAD: fFlags=%#x\n", (unsigned)pReq->u.In.fFlags));
+
+ /* execute */
+ pReq->Hdr.rc = supdrvIOCtl_LdrLoad(pDevExt, pSession, pReq);
+ return 0;
+ }
+
+ case SUP_CTL_CODE_NO_SIZE(SUP_IOCTL_LDR_FREE):
+ {
+ /* validate */
+ PSUPLDRFREE pReq = (PSUPLDRFREE)pReqHdr;
+ REQ_CHECK_SIZES(SUP_IOCTL_LDR_FREE);
+
+ /* execute */
+ pReq->Hdr.rc = supdrvIOCtl_LdrFree(pDevExt, pSession, pReq);
+ return 0;
+ }
+
+ case SUP_CTL_CODE_NO_SIZE(SUP_IOCTL_LDR_LOCK_DOWN):
+ {
+ /* validate */
+ REQ_CHECK_SIZES(SUP_IOCTL_LDR_LOCK_DOWN);
+
+ /* execute */
+ pReqHdr->rc = supdrvIOCtl_LdrLockDown(pDevExt);
+ return 0;
+ }
+
+ case SUP_CTL_CODE_NO_SIZE(SUP_IOCTL_LDR_GET_SYMBOL):
+ {
+ /* validate */
+ PSUPLDRGETSYMBOL pReq = (PSUPLDRGETSYMBOL)pReqHdr;
+ REQ_CHECK_SIZES(SUP_IOCTL_LDR_GET_SYMBOL);
+ REQ_CHECK_EXPR(SUP_IOCTL_LDR_GET_SYMBOL, RTStrEnd(pReq->u.In.szSymbol, sizeof(pReq->u.In.szSymbol)));
+
+ /* execute */
+ pReq->Hdr.rc = supdrvIOCtl_LdrQuerySymbol(pDevExt, pSession, pReq);
+ return 0;
+ }
+
+ case SUP_CTL_CODE_NO_SIZE(SUP_IOCTL_CALL_VMMR0_NO_SIZE()):
+ {
+ /* validate */
+ PSUPCALLVMMR0 pReq = (PSUPCALLVMMR0)pReqHdr;
+ Log4(("SUP_IOCTL_CALL_VMMR0: op=%u in=%u arg=%RX64 p/t=%RTproc/%RTthrd\n",
+ pReq->u.In.uOperation, pReq->Hdr.cbIn, pReq->u.In.u64Arg, RTProcSelf(), RTThreadNativeSelf()));
+
+ if (pReq->Hdr.cbIn == SUP_IOCTL_CALL_VMMR0_SIZE(0))
+ {
+ REQ_CHECK_SIZES_EX(SUP_IOCTL_CALL_VMMR0, SUP_IOCTL_CALL_VMMR0_SIZE_IN(0), SUP_IOCTL_CALL_VMMR0_SIZE_OUT(0));
+
+ /* execute */
+ if (RT_LIKELY(pDevExt->pfnVMMR0EntryEx))
+ {
+ if (pReq->u.In.pVMR0 == NULL)
+ pReq->Hdr.rc = pDevExt->pfnVMMR0EntryEx(NULL, NULL, pReq->u.In.idCpu,
+ pReq->u.In.uOperation, NULL, pReq->u.In.u64Arg, pSession);
+ else if (pReq->u.In.pVMR0 == pSession->pSessionVM)
+ pReq->Hdr.rc = pDevExt->pfnVMMR0EntryEx(pSession->pSessionGVM, pSession->pSessionVM, pReq->u.In.idCpu,
+ pReq->u.In.uOperation, NULL, pReq->u.In.u64Arg, pSession);
+ else
+ pReq->Hdr.rc = VERR_INVALID_VM_HANDLE;
+ }
+ else
+ pReq->Hdr.rc = VERR_WRONG_ORDER;
+ }
+ else
+ {
+ PSUPVMMR0REQHDR pVMMReq = (PSUPVMMR0REQHDR)&pReq->abReqPkt[0];
+ REQ_CHECK_EXPR_FMT(pReq->Hdr.cbIn >= SUP_IOCTL_CALL_VMMR0_SIZE(sizeof(SUPVMMR0REQHDR)),
+ ("SUP_IOCTL_CALL_VMMR0: cbIn=%#x < %#lx\n", pReq->Hdr.cbIn, SUP_IOCTL_CALL_VMMR0_SIZE(sizeof(SUPVMMR0REQHDR))));
+ REQ_CHECK_EXPR(SUP_IOCTL_CALL_VMMR0, pVMMReq->u32Magic == SUPVMMR0REQHDR_MAGIC);
+ REQ_CHECK_SIZES_EX(SUP_IOCTL_CALL_VMMR0, SUP_IOCTL_CALL_VMMR0_SIZE_IN(pVMMReq->cbReq), SUP_IOCTL_CALL_VMMR0_SIZE_OUT(pVMMReq->cbReq));
+
+ /* execute */
+ if (RT_LIKELY(pDevExt->pfnVMMR0EntryEx))
+ {
+ if (pReq->u.In.pVMR0 == NULL)
+ pReq->Hdr.rc = pDevExt->pfnVMMR0EntryEx(NULL, NULL, pReq->u.In.idCpu,
+ pReq->u.In.uOperation, pVMMReq, pReq->u.In.u64Arg, pSession);
+ else if (pReq->u.In.pVMR0 == pSession->pSessionVM)
+ pReq->Hdr.rc = pDevExt->pfnVMMR0EntryEx(pSession->pSessionGVM, pSession->pSessionVM, pReq->u.In.idCpu,
+ pReq->u.In.uOperation, pVMMReq, pReq->u.In.u64Arg, pSession);
+ else
+ pReq->Hdr.rc = VERR_INVALID_VM_HANDLE;
+ }
+ else
+ pReq->Hdr.rc = VERR_WRONG_ORDER;
+ }
+
+ if ( RT_FAILURE(pReq->Hdr.rc)
+ && pReq->Hdr.rc != VERR_INTERRUPTED
+ && pReq->Hdr.rc != VERR_TIMEOUT)
+ Log(("SUP_IOCTL_CALL_VMMR0: rc=%Rrc op=%u out=%u arg=%RX64 p/t=%RTproc/%RTthrd\n",
+ pReq->Hdr.rc, pReq->u.In.uOperation, pReq->Hdr.cbOut, pReq->u.In.u64Arg, RTProcSelf(), RTThreadNativeSelf()));
+ else
+ Log4(("SUP_IOCTL_CALL_VMMR0: rc=%Rrc op=%u out=%u arg=%RX64 p/t=%RTproc/%RTthrd\n",
+ pReq->Hdr.rc, pReq->u.In.uOperation, pReq->Hdr.cbOut, pReq->u.In.u64Arg, RTProcSelf(), RTThreadNativeSelf()));
+ return 0;
+ }
+
+ case SUP_CTL_CODE_NO_SIZE(SUP_IOCTL_CALL_VMMR0_BIG):
+ {
+ /* validate */
+ PSUPCALLVMMR0 pReq = (PSUPCALLVMMR0)pReqHdr;
+ PSUPVMMR0REQHDR pVMMReq;
+ Log4(("SUP_IOCTL_CALL_VMMR0_BIG: op=%u in=%u arg=%RX64 p/t=%RTproc/%RTthrd\n",
+ pReq->u.In.uOperation, pReq->Hdr.cbIn, pReq->u.In.u64Arg, RTProcSelf(), RTThreadNativeSelf()));
+
+ pVMMReq = (PSUPVMMR0REQHDR)&pReq->abReqPkt[0];
+ REQ_CHECK_EXPR_FMT(pReq->Hdr.cbIn >= SUP_IOCTL_CALL_VMMR0_BIG_SIZE(sizeof(SUPVMMR0REQHDR)),
+ ("SUP_IOCTL_CALL_VMMR0_BIG: cbIn=%#x < %#lx\n", pReq->Hdr.cbIn, SUP_IOCTL_CALL_VMMR0_BIG_SIZE(sizeof(SUPVMMR0REQHDR))));
+ REQ_CHECK_EXPR(SUP_IOCTL_CALL_VMMR0_BIG, pVMMReq->u32Magic == SUPVMMR0REQHDR_MAGIC);
+ REQ_CHECK_SIZES_EX(SUP_IOCTL_CALL_VMMR0_BIG, SUP_IOCTL_CALL_VMMR0_BIG_SIZE_IN(pVMMReq->cbReq), SUP_IOCTL_CALL_VMMR0_BIG_SIZE_OUT(pVMMReq->cbReq));
+
+ /* execute */
+ if (RT_LIKELY(pDevExt->pfnVMMR0EntryEx))
+ {
+ if (pReq->u.In.pVMR0 == NULL)
+ pReq->Hdr.rc = pDevExt->pfnVMMR0EntryEx(NULL, NULL, pReq->u.In.idCpu, pReq->u.In.uOperation, pVMMReq, pReq->u.In.u64Arg, pSession);
+ else if (pReq->u.In.pVMR0 == pSession->pSessionVM)
+ pReq->Hdr.rc = pDevExt->pfnVMMR0EntryEx(pSession->pSessionGVM, pSession->pSessionVM, pReq->u.In.idCpu,
+ pReq->u.In.uOperation, pVMMReq, pReq->u.In.u64Arg, pSession);
+ else
+ pReq->Hdr.rc = VERR_INVALID_VM_HANDLE;
+ }
+ else
+ pReq->Hdr.rc = VERR_WRONG_ORDER;
+
+ if ( RT_FAILURE(pReq->Hdr.rc)
+ && pReq->Hdr.rc != VERR_INTERRUPTED
+ && pReq->Hdr.rc != VERR_TIMEOUT)
+ Log(("SUP_IOCTL_CALL_VMMR0_BIG: rc=%Rrc op=%u out=%u arg=%RX64 p/t=%RTproc/%RTthrd\n",
+ pReq->Hdr.rc, pReq->u.In.uOperation, pReq->Hdr.cbOut, pReq->u.In.u64Arg, RTProcSelf(), RTThreadNativeSelf()));
+ else
+ Log4(("SUP_IOCTL_CALL_VMMR0_BIG: rc=%Rrc op=%u out=%u arg=%RX64 p/t=%RTproc/%RTthrd\n",
+ pReq->Hdr.rc, pReq->u.In.uOperation, pReq->Hdr.cbOut, pReq->u.In.u64Arg, RTProcSelf(), RTThreadNativeSelf()));
+ return 0;
+ }
+
+ case SUP_CTL_CODE_NO_SIZE(SUP_IOCTL_GET_PAGING_MODE):
+ {
+ /* validate */
+ PSUPGETPAGINGMODE pReq = (PSUPGETPAGINGMODE)pReqHdr;
+ REQ_CHECK_SIZES(SUP_IOCTL_GET_PAGING_MODE);
+
+ /* execute */
+ pReq->Hdr.rc = VINF_SUCCESS;
+ pReq->u.Out.enmMode = SUPR0GetPagingMode();
+ return 0;
+ }
+
+ case SUP_CTL_CODE_NO_SIZE(SUP_IOCTL_LOW_ALLOC):
+ {
+ /* validate */
+ PSUPLOWALLOC pReq = (PSUPLOWALLOC)pReqHdr;
+ REQ_CHECK_EXPR(SUP_IOCTL_LOW_ALLOC, pReq->Hdr.cbIn <= SUP_IOCTL_LOW_ALLOC_SIZE_IN);
+ REQ_CHECK_SIZES_EX(SUP_IOCTL_LOW_ALLOC, SUP_IOCTL_LOW_ALLOC_SIZE_IN, SUP_IOCTL_LOW_ALLOC_SIZE_OUT(pReq->u.In.cPages));
+
+ /* execute */
+ pReq->Hdr.rc = SUPR0LowAlloc(pSession, pReq->u.In.cPages, &pReq->u.Out.pvR0, &pReq->u.Out.pvR3, &pReq->u.Out.aPages[0]);
+ if (RT_FAILURE(pReq->Hdr.rc))
+ pReq->Hdr.cbOut = sizeof(pReq->Hdr);
+ return 0;
+ }
+
+ case SUP_CTL_CODE_NO_SIZE(SUP_IOCTL_LOW_FREE):
+ {
+ /* validate */
+ PSUPLOWFREE pReq = (PSUPLOWFREE)pReqHdr;
+ REQ_CHECK_SIZES(SUP_IOCTL_LOW_FREE);
+
+ /* execute */
+ pReq->Hdr.rc = SUPR0LowFree(pSession, (RTHCUINTPTR)pReq->u.In.pvR3);
+ return 0;
+ }
+
+ case SUP_CTL_CODE_NO_SIZE(SUP_IOCTL_GIP_MAP):
+ {
+ /* validate */
+ PSUPGIPMAP pReq = (PSUPGIPMAP)pReqHdr;
+ REQ_CHECK_SIZES(SUP_IOCTL_GIP_MAP);
+
+ /* execute */
+ pReq->Hdr.rc = SUPR0GipMap(pSession, &pReq->u.Out.pGipR3, &pReq->u.Out.HCPhysGip);
+ if (RT_SUCCESS(pReq->Hdr.rc))
+ pReq->u.Out.pGipR0 = pDevExt->pGip;
+ return 0;
+ }
+
+ case SUP_CTL_CODE_NO_SIZE(SUP_IOCTL_GIP_UNMAP):
+ {
+ /* validate */
+ PSUPGIPUNMAP pReq = (PSUPGIPUNMAP)pReqHdr;
+ REQ_CHECK_SIZES(SUP_IOCTL_GIP_UNMAP);
+
+ /* execute */
+ pReq->Hdr.rc = SUPR0GipUnmap(pSession);
+ return 0;
+ }
+
+ case SUP_CTL_CODE_NO_SIZE(SUP_IOCTL_SET_VM_FOR_FAST):
+ {
+ /* validate */
+ PSUPSETVMFORFAST pReq = (PSUPSETVMFORFAST)pReqHdr;
+ REQ_CHECK_SIZES(SUP_IOCTL_SET_VM_FOR_FAST);
+ REQ_CHECK_EXPR_FMT( !pReq->u.In.pVMR0
+ || ( RT_VALID_PTR(pReq->u.In.pVMR0)
+ && !((uintptr_t)pReq->u.In.pVMR0 & (PAGE_SIZE - 1))),
+ ("SUP_IOCTL_SET_VM_FOR_FAST: pVMR0=%p!\n", pReq->u.In.pVMR0));
+
+ /* execute */
+ RTSpinlockAcquire(pDevExt->Spinlock);
+ if (pSession->pSessionVM == pReq->u.In.pVMR0)
+ {
+ if (pSession->pFastIoCtrlVM == NULL)
+ {
+ pSession->pFastIoCtrlVM = pSession->pSessionVM;
+ RTSpinlockRelease(pDevExt->Spinlock);
+ pReq->Hdr.rc = VINF_SUCCESS;
+ }
+ else
+ {
+ RTSpinlockRelease(pDevExt->Spinlock);
+ OSDBGPRINT(("SUP_IOCTL_SET_VM_FOR_FAST: pSession->pFastIoCtrlVM=%p! (pVMR0=%p)\n",
+ pSession->pFastIoCtrlVM, pReq->u.In.pVMR0));
+ pReq->Hdr.rc = VERR_ALREADY_EXISTS;
+ }
+ }
+ else
+ {
+ RTSpinlockRelease(pDevExt->Spinlock);
+ OSDBGPRINT(("SUP_IOCTL_SET_VM_FOR_FAST: pSession->pSessionVM=%p vs pVMR0=%p)\n",
+ pSession->pSessionVM, pReq->u.In.pVMR0));
+ pReq->Hdr.rc = pSession->pSessionVM ? VERR_ACCESS_DENIED : VERR_WRONG_ORDER;
+ }
+ return 0;
+ }
+
+ case SUP_CTL_CODE_NO_SIZE(SUP_IOCTL_PAGE_ALLOC_EX):
+ {
+ /* validate */
+ PSUPPAGEALLOCEX pReq = (PSUPPAGEALLOCEX)pReqHdr;
+ REQ_CHECK_EXPR(SUP_IOCTL_PAGE_ALLOC_EX, pReq->Hdr.cbIn <= SUP_IOCTL_PAGE_ALLOC_EX_SIZE_IN);
+ REQ_CHECK_SIZES_EX(SUP_IOCTL_PAGE_ALLOC_EX, SUP_IOCTL_PAGE_ALLOC_EX_SIZE_IN, SUP_IOCTL_PAGE_ALLOC_EX_SIZE_OUT(pReq->u.In.cPages));
+ REQ_CHECK_EXPR_FMT(pReq->u.In.fKernelMapping || pReq->u.In.fUserMapping,
+ ("SUP_IOCTL_PAGE_ALLOC_EX: No mapping requested!\n"));
+ REQ_CHECK_EXPR_FMT(pReq->u.In.fUserMapping,
+ ("SUP_IOCTL_PAGE_ALLOC_EX: Must have user mapping!\n"));
+ REQ_CHECK_EXPR_FMT(!pReq->u.In.fReserved0 && !pReq->u.In.fReserved1,
+ ("SUP_IOCTL_PAGE_ALLOC_EX: fReserved0=%d fReserved1=%d\n", pReq->u.In.fReserved0, pReq->u.In.fReserved1));
+
+ /* execute */
+ pReq->Hdr.rc = SUPR0PageAllocEx(pSession, pReq->u.In.cPages, 0 /* fFlags */,
+ pReq->u.In.fUserMapping ? &pReq->u.Out.pvR3 : NULL,
+ pReq->u.In.fKernelMapping ? &pReq->u.Out.pvR0 : NULL,
+ &pReq->u.Out.aPages[0]);
+ if (RT_FAILURE(pReq->Hdr.rc))
+ pReq->Hdr.cbOut = sizeof(pReq->Hdr);
+ return 0;
+ }
+
+ case SUP_CTL_CODE_NO_SIZE(SUP_IOCTL_PAGE_MAP_KERNEL):
+ {
+ /* validate */
+ PSUPPAGEMAPKERNEL pReq = (PSUPPAGEMAPKERNEL)pReqHdr;
+ REQ_CHECK_SIZES(SUP_IOCTL_PAGE_MAP_KERNEL);
+ REQ_CHECK_EXPR_FMT(!pReq->u.In.fFlags, ("SUP_IOCTL_PAGE_MAP_KERNEL: fFlags=%#x! MBZ\n", pReq->u.In.fFlags));
+ REQ_CHECK_EXPR_FMT(!(pReq->u.In.offSub & PAGE_OFFSET_MASK), ("SUP_IOCTL_PAGE_MAP_KERNEL: offSub=%#x\n", pReq->u.In.offSub));
+ REQ_CHECK_EXPR_FMT(pReq->u.In.cbSub && !(pReq->u.In.cbSub & PAGE_OFFSET_MASK),
+ ("SUP_IOCTL_PAGE_MAP_KERNEL: cbSub=%#x\n", pReq->u.In.cbSub));
+
+ /* execute */
+ pReq->Hdr.rc = SUPR0PageMapKernel(pSession, pReq->u.In.pvR3, pReq->u.In.offSub, pReq->u.In.cbSub,
+ pReq->u.In.fFlags, &pReq->u.Out.pvR0);
+ if (RT_FAILURE(pReq->Hdr.rc))
+ pReq->Hdr.cbOut = sizeof(pReq->Hdr);
+ return 0;
+ }
+
+ case SUP_CTL_CODE_NO_SIZE(SUP_IOCTL_PAGE_PROTECT):
+ {
+ /* validate */
+ PSUPPAGEPROTECT pReq = (PSUPPAGEPROTECT)pReqHdr;
+ REQ_CHECK_SIZES(SUP_IOCTL_PAGE_PROTECT);
+ REQ_CHECK_EXPR_FMT(!(pReq->u.In.fProt & ~(RTMEM_PROT_READ | RTMEM_PROT_WRITE | RTMEM_PROT_EXEC | RTMEM_PROT_NONE)),
+ ("SUP_IOCTL_PAGE_PROTECT: fProt=%#x!\n", pReq->u.In.fProt));
+ REQ_CHECK_EXPR_FMT(!(pReq->u.In.offSub & PAGE_OFFSET_MASK), ("SUP_IOCTL_PAGE_PROTECT: offSub=%#x\n", pReq->u.In.offSub));
+ REQ_CHECK_EXPR_FMT(pReq->u.In.cbSub && !(pReq->u.In.cbSub & PAGE_OFFSET_MASK),
+ ("SUP_IOCTL_PAGE_PROTECT: cbSub=%#x\n", pReq->u.In.cbSub));
+
+ /* execute */
+ pReq->Hdr.rc = SUPR0PageProtect(pSession, pReq->u.In.pvR3, pReq->u.In.pvR0, pReq->u.In.offSub, pReq->u.In.cbSub, pReq->u.In.fProt);
+ return 0;
+ }
+
+ case SUP_CTL_CODE_NO_SIZE(SUP_IOCTL_PAGE_FREE):
+ {
+ /* validate */
+ PSUPPAGEFREE pReq = (PSUPPAGEFREE)pReqHdr;
+ REQ_CHECK_SIZES(SUP_IOCTL_PAGE_FREE);
+
+ /* execute */
+ pReq->Hdr.rc = SUPR0PageFree(pSession, pReq->u.In.pvR3);
+ return 0;
+ }
+
+ case SUP_CTL_CODE_NO_SIZE(SUP_IOCTL_CALL_SERVICE_NO_SIZE()):
+ {
+ /* validate */
+ PSUPCALLSERVICE pReq = (PSUPCALLSERVICE)pReqHdr;
+ Log4(("SUP_IOCTL_CALL_SERVICE: op=%u in=%u arg=%RX64 p/t=%RTproc/%RTthrd\n",
+ pReq->u.In.uOperation, pReq->Hdr.cbIn, pReq->u.In.u64Arg, RTProcSelf(), RTThreadNativeSelf()));
+
+ if (pReq->Hdr.cbIn == SUP_IOCTL_CALL_SERVICE_SIZE(0))
+ REQ_CHECK_SIZES_EX(SUP_IOCTL_CALL_SERVICE, SUP_IOCTL_CALL_SERVICE_SIZE_IN(0), SUP_IOCTL_CALL_SERVICE_SIZE_OUT(0));
+ else
+ {
+ PSUPR0SERVICEREQHDR pSrvReq = (PSUPR0SERVICEREQHDR)&pReq->abReqPkt[0];
+ REQ_CHECK_EXPR_FMT(pReq->Hdr.cbIn >= SUP_IOCTL_CALL_SERVICE_SIZE(sizeof(SUPR0SERVICEREQHDR)),
+ ("SUP_IOCTL_CALL_SERVICE: cbIn=%#x < %#lx\n", pReq->Hdr.cbIn, SUP_IOCTL_CALL_SERVICE_SIZE(sizeof(SUPR0SERVICEREQHDR))));
+ REQ_CHECK_EXPR(SUP_IOCTL_CALL_SERVICE, pSrvReq->u32Magic == SUPR0SERVICEREQHDR_MAGIC);
+ REQ_CHECK_SIZES_EX(SUP_IOCTL_CALL_SERVICE, SUP_IOCTL_CALL_SERVICE_SIZE_IN(pSrvReq->cbReq), SUP_IOCTL_CALL_SERVICE_SIZE_OUT(pSrvReq->cbReq));
+ }
+ REQ_CHECK_EXPR(SUP_IOCTL_CALL_SERVICE, RTStrEnd(pReq->u.In.szName, sizeof(pReq->u.In.szName)));
+
+ /* execute */
+ pReq->Hdr.rc = supdrvIOCtl_CallServiceModule(pDevExt, pSession, pReq);
+ return 0;
+ }
+
+ case SUP_CTL_CODE_NO_SIZE(SUP_IOCTL_LOGGER_SETTINGS_NO_SIZE()):
+ {
+ /* validate */
+ PSUPLOGGERSETTINGS pReq = (PSUPLOGGERSETTINGS)pReqHdr;
+ size_t cbStrTab;
+ REQ_CHECK_SIZE_OUT(SUP_IOCTL_LOGGER_SETTINGS, SUP_IOCTL_LOGGER_SETTINGS_SIZE_OUT);
+ REQ_CHECK_EXPR(SUP_IOCTL_LOGGER_SETTINGS, pReq->Hdr.cbIn >= SUP_IOCTL_LOGGER_SETTINGS_SIZE_IN(1));
+ cbStrTab = pReq->Hdr.cbIn - SUP_IOCTL_LOGGER_SETTINGS_SIZE_IN(0);
+ REQ_CHECK_EXPR(SUP_IOCTL_LOGGER_SETTINGS, pReq->u.In.offGroups < cbStrTab);
+ REQ_CHECK_EXPR(SUP_IOCTL_LOGGER_SETTINGS, pReq->u.In.offFlags < cbStrTab);
+ REQ_CHECK_EXPR(SUP_IOCTL_LOGGER_SETTINGS, pReq->u.In.offDestination < cbStrTab);
+ REQ_CHECK_EXPR_FMT(pReq->u.In.szStrings[cbStrTab - 1] == '\0',
+ ("SUP_IOCTL_LOGGER_SETTINGS: cbIn=%#x cbStrTab=%#zx LastChar=%d\n",
+ pReq->Hdr.cbIn, cbStrTab, pReq->u.In.szStrings[cbStrTab - 1]));
+ REQ_CHECK_EXPR(SUP_IOCTL_LOGGER_SETTINGS, pReq->u.In.fWhich <= SUPLOGGERSETTINGS_WHICH_RELEASE);
+ REQ_CHECK_EXPR(SUP_IOCTL_LOGGER_SETTINGS, pReq->u.In.fWhat <= SUPLOGGERSETTINGS_WHAT_DESTROY);
+
+ /* execute */
+ pReq->Hdr.rc = supdrvIOCtl_LoggerSettings(pReq);
+ return 0;
+ }
+
+ case SUP_CTL_CODE_NO_SIZE(SUP_IOCTL_SEM_OP2):
+ {
+ /* validate */
+ PSUPSEMOP2 pReq = (PSUPSEMOP2)pReqHdr;
+ REQ_CHECK_SIZES_EX(SUP_IOCTL_SEM_OP2, SUP_IOCTL_SEM_OP2_SIZE_IN, SUP_IOCTL_SEM_OP2_SIZE_OUT);
+ REQ_CHECK_EXPR(SUP_IOCTL_SEM_OP2, pReq->u.In.uReserved == 0);
+
+ /* execute */
+ switch (pReq->u.In.uType)
+ {
+ case SUP_SEM_TYPE_EVENT:
+ {
+ SUPSEMEVENT hEvent = (SUPSEMEVENT)(uintptr_t)pReq->u.In.hSem;
+ switch (pReq->u.In.uOp)
+ {
+ case SUPSEMOP2_WAIT_MS_REL:
+ pReq->Hdr.rc = SUPSemEventWaitNoResume(pSession, hEvent, pReq->u.In.uArg.cRelMsTimeout);
+ break;
+ case SUPSEMOP2_WAIT_NS_ABS:
+ pReq->Hdr.rc = SUPSemEventWaitNsAbsIntr(pSession, hEvent, pReq->u.In.uArg.uAbsNsTimeout);
+ break;
+ case SUPSEMOP2_WAIT_NS_REL:
+ pReq->Hdr.rc = SUPSemEventWaitNsRelIntr(pSession, hEvent, pReq->u.In.uArg.cRelNsTimeout);
+ break;
+ case SUPSEMOP2_SIGNAL:
+ pReq->Hdr.rc = SUPSemEventSignal(pSession, hEvent);
+ break;
+ case SUPSEMOP2_CLOSE:
+ pReq->Hdr.rc = SUPSemEventClose(pSession, hEvent);
+ break;
+ case SUPSEMOP2_RESET:
+ default:
+ pReq->Hdr.rc = VERR_INVALID_FUNCTION;
+ break;
+ }
+ break;
+ }
+
+ case SUP_SEM_TYPE_EVENT_MULTI:
+ {
+ SUPSEMEVENTMULTI hEventMulti = (SUPSEMEVENTMULTI)(uintptr_t)pReq->u.In.hSem;
+ switch (pReq->u.In.uOp)
+ {
+ case SUPSEMOP2_WAIT_MS_REL:
+ pReq->Hdr.rc = SUPSemEventMultiWaitNoResume(pSession, hEventMulti, pReq->u.In.uArg.cRelMsTimeout);
+ break;
+ case SUPSEMOP2_WAIT_NS_ABS:
+ pReq->Hdr.rc = SUPSemEventMultiWaitNsAbsIntr(pSession, hEventMulti, pReq->u.In.uArg.uAbsNsTimeout);
+ break;
+ case SUPSEMOP2_WAIT_NS_REL:
+ pReq->Hdr.rc = SUPSemEventMultiWaitNsRelIntr(pSession, hEventMulti, pReq->u.In.uArg.cRelNsTimeout);
+ break;
+ case SUPSEMOP2_SIGNAL:
+ pReq->Hdr.rc = SUPSemEventMultiSignal(pSession, hEventMulti);
+ break;
+ case SUPSEMOP2_CLOSE:
+ pReq->Hdr.rc = SUPSemEventMultiClose(pSession, hEventMulti);
+ break;
+ case SUPSEMOP2_RESET:
+ pReq->Hdr.rc = SUPSemEventMultiReset(pSession, hEventMulti);
+ break;
+ default:
+ pReq->Hdr.rc = VERR_INVALID_FUNCTION;
+ break;
+ }
+ break;
+ }
+
+ default:
+ pReq->Hdr.rc = VERR_INVALID_PARAMETER;
+ break;
+ }
+ return 0;
+ }
+
+ case SUP_CTL_CODE_NO_SIZE(SUP_IOCTL_SEM_OP3):
+ {
+ /* validate */
+ PSUPSEMOP3 pReq = (PSUPSEMOP3)pReqHdr;
+ REQ_CHECK_SIZES_EX(SUP_IOCTL_SEM_OP3, SUP_IOCTL_SEM_OP3_SIZE_IN, SUP_IOCTL_SEM_OP3_SIZE_OUT);
+ REQ_CHECK_EXPR(SUP_IOCTL_SEM_OP3, pReq->u.In.u32Reserved == 0 && pReq->u.In.u64Reserved == 0);
+
+ /* execute */
+ switch (pReq->u.In.uType)
+ {
+ case SUP_SEM_TYPE_EVENT:
+ {
+ SUPSEMEVENT hEvent = (SUPSEMEVENT)(uintptr_t)pReq->u.In.hSem;
+ switch (pReq->u.In.uOp)
+ {
+ case SUPSEMOP3_CREATE:
+ REQ_CHECK_EXPR(SUP_IOCTL_SEM_OP3, hEvent == NIL_SUPSEMEVENT);
+ pReq->Hdr.rc = SUPSemEventCreate(pSession, &hEvent);
+ pReq->u.Out.hSem = (uint32_t)(uintptr_t)hEvent;
+ break;
+ case SUPSEMOP3_GET_RESOLUTION:
+ REQ_CHECK_EXPR(SUP_IOCTL_SEM_OP3, hEvent == NIL_SUPSEMEVENT);
+ pReq->Hdr.rc = VINF_SUCCESS;
+ pReq->Hdr.cbOut = sizeof(*pReq);
+ pReq->u.Out.cNsResolution = SUPSemEventGetResolution(pSession);
+ break;
+ default:
+ pReq->Hdr.rc = VERR_INVALID_FUNCTION;
+ break;
+ }
+ break;
+ }
+
+ case SUP_SEM_TYPE_EVENT_MULTI:
+ {
+ SUPSEMEVENTMULTI hEventMulti = (SUPSEMEVENTMULTI)(uintptr_t)pReq->u.In.hSem;
+ switch (pReq->u.In.uOp)
+ {
+ case SUPSEMOP3_CREATE:
+ REQ_CHECK_EXPR(SUP_IOCTL_SEM_OP3, hEventMulti == NIL_SUPSEMEVENTMULTI);
+ pReq->Hdr.rc = SUPSemEventMultiCreate(pSession, &hEventMulti);
+ pReq->u.Out.hSem = (uint32_t)(uintptr_t)hEventMulti;
+ break;
+ case SUPSEMOP3_GET_RESOLUTION:
+ REQ_CHECK_EXPR(SUP_IOCTL_SEM_OP3, hEventMulti == NIL_SUPSEMEVENTMULTI);
+ pReq->Hdr.rc = VINF_SUCCESS;
+ pReq->u.Out.cNsResolution = SUPSemEventMultiGetResolution(pSession);
+ break;
+ default:
+ pReq->Hdr.rc = VERR_INVALID_FUNCTION;
+ break;
+ }
+ break;
+ }
+
+ default:
+ pReq->Hdr.rc = VERR_INVALID_PARAMETER;
+ break;
+ }
+ return 0;
+ }
+
+ case SUP_CTL_CODE_NO_SIZE(SUP_IOCTL_VT_CAPS):
+ {
+ /* validate */
+ PSUPVTCAPS pReq = (PSUPVTCAPS)pReqHdr;
+ REQ_CHECK_SIZES(SUP_IOCTL_VT_CAPS);
+
+ /* execute */
+ pReq->Hdr.rc = SUPR0QueryVTCaps(pSession, &pReq->u.Out.fCaps);
+ if (RT_FAILURE(pReq->Hdr.rc))
+ pReq->Hdr.cbOut = sizeof(pReq->Hdr);
+ return 0;
+ }
+
+ case SUP_CTL_CODE_NO_SIZE(SUP_IOCTL_TRACER_OPEN):
+ {
+ /* validate */
+ PSUPTRACEROPEN pReq = (PSUPTRACEROPEN)pReqHdr;
+ REQ_CHECK_SIZES(SUP_IOCTL_TRACER_OPEN);
+
+ /* execute */
+ pReq->Hdr.rc = supdrvIOCtl_TracerOpen(pDevExt, pSession, pReq->u.In.uCookie, pReq->u.In.uArg);
+ return 0;
+ }
+
+ case SUP_CTL_CODE_NO_SIZE(SUP_IOCTL_TRACER_CLOSE):
+ {
+ /* validate */
+ REQ_CHECK_SIZES(SUP_IOCTL_TRACER_CLOSE);
+
+ /* execute */
+ pReqHdr->rc = supdrvIOCtl_TracerClose(pDevExt, pSession);
+ return 0;
+ }
+
+ case SUP_CTL_CODE_NO_SIZE(SUP_IOCTL_TRACER_IOCTL):
+ {
+ /* validate */
+ PSUPTRACERIOCTL pReq = (PSUPTRACERIOCTL)pReqHdr;
+ REQ_CHECK_SIZES(SUP_IOCTL_TRACER_IOCTL);
+
+ /* execute */
+ pReqHdr->rc = supdrvIOCtl_TracerIOCtl(pDevExt, pSession, pReq->u.In.uCmd, pReq->u.In.uArg, &pReq->u.Out.iRetVal);
+ return 0;
+ }
+
+ case SUP_CTL_CODE_NO_SIZE(SUP_IOCTL_TRACER_UMOD_REG):
+ {
+ /* validate */
+ PSUPTRACERUMODREG pReq = (PSUPTRACERUMODREG)pReqHdr;
+ REQ_CHECK_SIZES(SUP_IOCTL_TRACER_UMOD_REG);
+ if (!RTStrEnd(pReq->u.In.szName, sizeof(pReq->u.In.szName)))
+ return VERR_INVALID_PARAMETER;
+
+ /* execute */
+ pReqHdr->rc = supdrvIOCtl_TracerUmodRegister(pDevExt, pSession,
+ pReq->u.In.R3PtrVtgHdr, pReq->u.In.uVtgHdrAddr,
+ pReq->u.In.R3PtrStrTab, pReq->u.In.cbStrTab,
+ pReq->u.In.szName, pReq->u.In.fFlags);
+ return 0;
+ }
+
+ case SUP_CTL_CODE_NO_SIZE(SUP_IOCTL_TRACER_UMOD_DEREG):
+ {
+ /* validate */
+ PSUPTRACERUMODDEREG pReq = (PSUPTRACERUMODDEREG)pReqHdr;
+ REQ_CHECK_SIZES(SUP_IOCTL_TRACER_UMOD_DEREG);
+
+ /* execute */
+ pReqHdr->rc = supdrvIOCtl_TracerUmodDeregister(pDevExt, pSession, pReq->u.In.pVtgHdr);
+ return 0;
+ }
+
+ case SUP_CTL_CODE_NO_SIZE(SUP_IOCTL_TRACER_UMOD_FIRE_PROBE):
+ {
+ /* validate */
+ PSUPTRACERUMODFIREPROBE pReq = (PSUPTRACERUMODFIREPROBE)pReqHdr;
+ REQ_CHECK_SIZES(SUP_IOCTL_TRACER_UMOD_FIRE_PROBE);
+
+ supdrvIOCtl_TracerUmodProbeFire(pDevExt, pSession, &pReq->u.In);
+ pReqHdr->rc = VINF_SUCCESS;
+ return 0;
+ }
+
+ case SUP_CTL_CODE_NO_SIZE(SUP_IOCTL_MSR_PROBER):
+ {
+ /* validate */
+ PSUPMSRPROBER pReq = (PSUPMSRPROBER)pReqHdr;
+ REQ_CHECK_SIZES(SUP_IOCTL_MSR_PROBER);
+ REQ_CHECK_EXPR(SUP_IOCTL_MSR_PROBER,
+ pReq->u.In.enmOp > SUPMSRPROBEROP_INVALID && pReq->u.In.enmOp < SUPMSRPROBEROP_END);
+
+ pReqHdr->rc = supdrvIOCtl_MsrProber(pDevExt, pReq);
+ return 0;
+ }
+
+ case SUP_CTL_CODE_NO_SIZE(SUP_IOCTL_RESUME_SUSPENDED_KBDS):
+ {
+ /* validate */
+ REQ_CHECK_SIZES(SUP_IOCTL_RESUME_SUSPENDED_KBDS);
+
+ pReqHdr->rc = supdrvIOCtl_ResumeSuspendedKbds();
+ return 0;
+ }
+
+ case SUP_CTL_CODE_NO_SIZE(SUP_IOCTL_TSC_DELTA_MEASURE):
+ {
+ /* validate */
+ PSUPTSCDELTAMEASURE pReq = (PSUPTSCDELTAMEASURE)pReqHdr;
+ REQ_CHECK_SIZES(SUP_IOCTL_TSC_DELTA_MEASURE);
+
+ pReqHdr->rc = supdrvIOCtl_TscDeltaMeasure(pDevExt, pSession, pReq);
+ return 0;
+ }
+
+ case SUP_CTL_CODE_NO_SIZE(SUP_IOCTL_TSC_READ):
+ {
+ /* validate */
+ PSUPTSCREAD pReq = (PSUPTSCREAD)pReqHdr;
+ REQ_CHECK_SIZES(SUP_IOCTL_TSC_READ);
+
+ pReqHdr->rc = supdrvIOCtl_TscRead(pDevExt, pSession, pReq);
+ return 0;
+ }
+
+ case SUP_CTL_CODE_NO_SIZE(SUP_IOCTL_GIP_SET_FLAGS):
+ {
+ /* validate */
+ PSUPGIPSETFLAGS pReq = (PSUPGIPSETFLAGS)pReqHdr;
+ REQ_CHECK_SIZES(SUP_IOCTL_GIP_SET_FLAGS);
+
+ pReqHdr->rc = supdrvIOCtl_GipSetFlags(pDevExt, pSession, pReq->u.In.fOrMask, pReq->u.In.fAndMask);
+ return 0;
+ }
+
+ case SUP_CTL_CODE_NO_SIZE(SUP_IOCTL_UCODE_REV):
+ {
+ /* validate */
+ PSUPUCODEREV pReq = (PSUPUCODEREV)pReqHdr;
+ REQ_CHECK_SIZES(SUP_IOCTL_UCODE_REV);
+
+ /* execute */
+ pReq->Hdr.rc = SUPR0QueryUcodeRev(pSession, &pReq->u.Out.MicrocodeRev);
+ if (RT_FAILURE(pReq->Hdr.rc))
+ pReq->Hdr.cbOut = sizeof(pReq->Hdr);
+ return 0;
+ }
+
+ case SUP_CTL_CODE_NO_SIZE(SUP_IOCTL_GET_HWVIRT_MSRS):
+ {
+ /* validate */
+ PSUPGETHWVIRTMSRS pReq = (PSUPGETHWVIRTMSRS)pReqHdr;
+ REQ_CHECK_SIZES(SUP_IOCTL_GET_HWVIRT_MSRS);
+ REQ_CHECK_EXPR_FMT(!pReq->u.In.fReserved0 && !pReq->u.In.fReserved1 && !pReq->u.In.fReserved2,
+ ("SUP_IOCTL_GET_HWVIRT_MSRS: fReserved0=%d fReserved1=%d fReserved2=%d\n", pReq->u.In.fReserved0,
+ pReq->u.In.fReserved1, pReq->u.In.fReserved2));
+
+ /* execute */
+ pReq->Hdr.rc = SUPR0GetHwvirtMsrs(&pReq->u.Out.HwvirtMsrs, 0 /* fCaps */, pReq->u.In.fForce);
+ if (RT_FAILURE(pReq->Hdr.rc))
+ pReq->Hdr.cbOut = sizeof(pReq->Hdr);
+ return 0;
+ }
+
+ default:
+ Log(("Unknown IOCTL %#lx\n", (long)uIOCtl));
+ break;
+ }
+ return VERR_GENERAL_FAILURE;
+}
+
+
+/**
+ * I/O Control inner worker for the restricted operations.
+ *
+ * @returns IPRT status code.
+ * @retval VERR_INVALID_PARAMETER if the request is invalid.
+ *
+ * @param uIOCtl Function number.
+ * @param pDevExt Device extention.
+ * @param pSession Session data.
+ * @param pReqHdr The request header.
+ */
+static int supdrvIOCtlInnerRestricted(uintptr_t uIOCtl, PSUPDRVDEVEXT pDevExt, PSUPDRVSESSION pSession, PSUPREQHDR pReqHdr)
+{
+ /*
+ * The switch.
+ */
+ switch (SUP_CTL_CODE_NO_SIZE(uIOCtl))
+ {
+ case SUP_CTL_CODE_NO_SIZE(SUP_IOCTL_COOKIE):
+ {
+ PSUPCOOKIE pReq = (PSUPCOOKIE)pReqHdr;
+ REQ_CHECK_SIZES(SUP_IOCTL_COOKIE);
+ if (strncmp(pReq->u.In.szMagic, SUPCOOKIE_MAGIC, sizeof(pReq->u.In.szMagic)))
+ {
+ OSDBGPRINT(("SUP_IOCTL_COOKIE: invalid magic %.16s\n", pReq->u.In.szMagic));
+ pReq->Hdr.rc = VERR_INVALID_MAGIC;
+ return 0;
+ }
+
+ /*
+ * Match the version.
+ * The current logic is very simple, match the major interface version.
+ */
+ if ( pReq->u.In.u32MinVersion > SUPDRV_IOC_VERSION
+ || (pReq->u.In.u32MinVersion & 0xffff0000) != (SUPDRV_IOC_VERSION & 0xffff0000))
+ {
+ OSDBGPRINT(("SUP_IOCTL_COOKIE: Version mismatch. Requested: %#x Min: %#x Current: %#x\n",
+ pReq->u.In.u32ReqVersion, pReq->u.In.u32MinVersion, SUPDRV_IOC_VERSION));
+ pReq->u.Out.u32Cookie = 0xffffffff;
+ pReq->u.Out.u32SessionCookie = 0xffffffff;
+ pReq->u.Out.u32SessionVersion = 0xffffffff;
+ pReq->u.Out.u32DriverVersion = SUPDRV_IOC_VERSION;
+ pReq->u.Out.pSession = NULL;
+ pReq->u.Out.cFunctions = 0;
+ pReq->Hdr.rc = VERR_VERSION_MISMATCH;
+ return 0;
+ }
+
+ /*
+ * Fill in return data and be gone.
+ * N.B. The first one to change SUPDRV_IOC_VERSION shall makes sure that
+ * u32SessionVersion <= u32ReqVersion!
+ */
+ /** @todo Somehow validate the client and negotiate a secure cookie... */
+ pReq->u.Out.u32Cookie = pDevExt->u32Cookie;
+ pReq->u.Out.u32SessionCookie = pSession->u32Cookie;
+ pReq->u.Out.u32SessionVersion = SUPDRV_IOC_VERSION;
+ pReq->u.Out.u32DriverVersion = SUPDRV_IOC_VERSION;
+ pReq->u.Out.pSession = NULL;
+ pReq->u.Out.cFunctions = 0;
+ pReq->Hdr.rc = VINF_SUCCESS;
+ return 0;
+ }
+
+ case SUP_CTL_CODE_NO_SIZE(SUP_IOCTL_VT_CAPS):
+ {
+ /* validate */
+ PSUPVTCAPS pReq = (PSUPVTCAPS)pReqHdr;
+ REQ_CHECK_SIZES(SUP_IOCTL_VT_CAPS);
+
+ /* execute */
+ pReq->Hdr.rc = SUPR0QueryVTCaps(pSession, &pReq->u.Out.fCaps);
+ if (RT_FAILURE(pReq->Hdr.rc))
+ pReq->Hdr.cbOut = sizeof(pReq->Hdr);
+ return 0;
+ }
+
+ default:
+ Log(("Unknown IOCTL %#lx\n", (long)uIOCtl));
+ break;
+ }
+ return VERR_GENERAL_FAILURE;
+}
+
+
+/**
+ * I/O Control worker.
+ *
+ * @returns IPRT status code.
+ * @retval VERR_INVALID_PARAMETER if the request is invalid.
+ *
+ * @param uIOCtl Function number.
+ * @param pDevExt Device extention.
+ * @param pSession Session data.
+ * @param pReqHdr The request header.
+ * @param cbReq The size of the request buffer.
+ */
+int VBOXCALL supdrvIOCtl(uintptr_t uIOCtl, PSUPDRVDEVEXT pDevExt, PSUPDRVSESSION pSession, PSUPREQHDR pReqHdr, size_t cbReq)
+{
+ int rc;
+ VBOXDRV_IOCTL_ENTRY(pSession, uIOCtl, pReqHdr);
+
+ /*
+ * Validate the request.
+ */
+ if (RT_UNLIKELY(cbReq < sizeof(*pReqHdr)))
+ {
+ OSDBGPRINT(("vboxdrv: Bad ioctl request size; cbReq=%#lx\n", (long)cbReq));
+ VBOXDRV_IOCTL_RETURN(pSession, uIOCtl, pReqHdr, VERR_INVALID_PARAMETER, VINF_SUCCESS);
+ return VERR_INVALID_PARAMETER;
+ }
+ if (RT_UNLIKELY( (pReqHdr->fFlags & SUPREQHDR_FLAGS_MAGIC_MASK) != SUPREQHDR_FLAGS_MAGIC
+ || pReqHdr->cbIn < sizeof(*pReqHdr)
+ || pReqHdr->cbIn > cbReq
+ || pReqHdr->cbOut < sizeof(*pReqHdr)
+ || pReqHdr->cbOut > cbReq))
+ {
+ OSDBGPRINT(("vboxdrv: Bad ioctl request header; cbIn=%#lx cbOut=%#lx fFlags=%#lx\n",
+ (long)pReqHdr->cbIn, (long)pReqHdr->cbOut, (long)pReqHdr->fFlags));
+ VBOXDRV_IOCTL_RETURN(pSession, uIOCtl, pReqHdr, VERR_INVALID_PARAMETER, VINF_SUCCESS);
+ return VERR_INVALID_PARAMETER;
+ }
+ if (RT_UNLIKELY(!RT_VALID_PTR(pSession)))
+ {
+ OSDBGPRINT(("vboxdrv: Invalid pSession value %p (ioctl=%p)\n", pSession, (void *)uIOCtl));
+ VBOXDRV_IOCTL_RETURN(pSession, uIOCtl, pReqHdr, VERR_INVALID_PARAMETER, VINF_SUCCESS);
+ return VERR_INVALID_PARAMETER;
+ }
+ if (RT_UNLIKELY(uIOCtl == SUP_IOCTL_COOKIE))
+ {
+ if (pReqHdr->u32Cookie != SUPCOOKIE_INITIAL_COOKIE)
+ {
+ OSDBGPRINT(("SUP_IOCTL_COOKIE: bad cookie %#lx\n", (long)pReqHdr->u32Cookie));
+ VBOXDRV_IOCTL_RETURN(pSession, uIOCtl, pReqHdr, VERR_INVALID_PARAMETER, VINF_SUCCESS);
+ return VERR_INVALID_PARAMETER;
+ }
+ }
+ else if (RT_UNLIKELY( pReqHdr->u32Cookie != pDevExt->u32Cookie
+ || pReqHdr->u32SessionCookie != pSession->u32Cookie))
+ {
+ OSDBGPRINT(("vboxdrv: bad cookie %#lx / %#lx.\n", (long)pReqHdr->u32Cookie, (long)pReqHdr->u32SessionCookie));
+ VBOXDRV_IOCTL_RETURN(pSession, uIOCtl, pReqHdr, VERR_INVALID_PARAMETER, VINF_SUCCESS);
+ return VERR_INVALID_PARAMETER;
+ }
+
+ /*
+ * Hand it to an inner function to avoid lots of unnecessary return tracepoints.
+ */
+ if (pSession->fUnrestricted)
+ rc = supdrvIOCtlInnerUnrestricted(uIOCtl, pDevExt, pSession, pReqHdr);
+ else
+ rc = supdrvIOCtlInnerRestricted(uIOCtl, pDevExt, pSession, pReqHdr);
+
+ VBOXDRV_IOCTL_RETURN(pSession, uIOCtl, pReqHdr, pReqHdr->rc, rc);
+ return rc;
+}
+
+
+/**
+ * Inter-Driver Communication (IDC) worker.
+ *
+ * @returns VBox status code.
+ * @retval VINF_SUCCESS on success.
+ * @retval VERR_INVALID_PARAMETER if the request is invalid.
+ * @retval VERR_NOT_SUPPORTED if the request isn't supported.
+ *
+ * @param uReq The request (function) code.
+ * @param pDevExt Device extention.
+ * @param pSession Session data.
+ * @param pReqHdr The request header.
+ */
+int VBOXCALL supdrvIDC(uintptr_t uReq, PSUPDRVDEVEXT pDevExt, PSUPDRVSESSION pSession, PSUPDRVIDCREQHDR pReqHdr)
+{
+ /*
+ * The OS specific code has already validated the pSession
+ * pointer, and the request size being greater or equal to
+ * size of the header.
+ *
+ * So, just check that pSession is a kernel context session.
+ */
+ if (RT_UNLIKELY( pSession
+ && pSession->R0Process != NIL_RTR0PROCESS))
+ return VERR_INVALID_PARAMETER;
+
+/*
+ * Validation macro.
+ */
+#define REQ_CHECK_IDC_SIZE(Name, cbExpect) \
+ do { \
+ if (RT_UNLIKELY(pReqHdr->cb != (cbExpect))) \
+ { \
+ OSDBGPRINT(( #Name ": Invalid input/output sizes. cb=%ld expected %ld.\n", \
+ (long)pReqHdr->cb, (long)(cbExpect))); \
+ return pReqHdr->rc = VERR_INVALID_PARAMETER; \
+ } \
+ } while (0)
+
+ switch (uReq)
+ {
+ case SUPDRV_IDC_REQ_CONNECT:
+ {
+ PSUPDRVIDCREQCONNECT pReq = (PSUPDRVIDCREQCONNECT)pReqHdr;
+ REQ_CHECK_IDC_SIZE(SUPDRV_IDC_REQ_CONNECT, sizeof(*pReq));
+
+ /*
+ * Validate the cookie and other input.
+ */
+ if (pReq->Hdr.pSession != NULL)
+ {
+ OSDBGPRINT(("SUPDRV_IDC_REQ_CONNECT: Hdr.pSession=%p expected NULL!\n", pReq->Hdr.pSession));
+ return pReqHdr->rc = VERR_INVALID_PARAMETER;
+ }
+ if (pReq->u.In.u32MagicCookie != SUPDRVIDCREQ_CONNECT_MAGIC_COOKIE)
+ {
+ OSDBGPRINT(("SUPDRV_IDC_REQ_CONNECT: u32MagicCookie=%#x expected %#x!\n",
+ (unsigned)pReq->u.In.u32MagicCookie, (unsigned)SUPDRVIDCREQ_CONNECT_MAGIC_COOKIE));
+ return pReqHdr->rc = VERR_INVALID_PARAMETER;
+ }
+ if ( pReq->u.In.uMinVersion > pReq->u.In.uReqVersion
+ || (pReq->u.In.uMinVersion & UINT32_C(0xffff0000)) != (pReq->u.In.uReqVersion & UINT32_C(0xffff0000)))
+ {
+ OSDBGPRINT(("SUPDRV_IDC_REQ_CONNECT: uMinVersion=%#x uMaxVersion=%#x doesn't match!\n",
+ pReq->u.In.uMinVersion, pReq->u.In.uReqVersion));
+ return pReqHdr->rc = VERR_INVALID_PARAMETER;
+ }
+ if (pSession != NULL)
+ {
+ OSDBGPRINT(("SUPDRV_IDC_REQ_CONNECT: pSession=%p expected NULL!\n", pSession));
+ return pReqHdr->rc = VERR_INVALID_PARAMETER;
+ }
+
+ /*
+ * Match the version.
+ * The current logic is very simple, match the major interface version.
+ */
+ if ( pReq->u.In.uMinVersion > SUPDRV_IDC_VERSION
+ || (pReq->u.In.uMinVersion & 0xffff0000) != (SUPDRV_IDC_VERSION & 0xffff0000))
+ {
+ OSDBGPRINT(("SUPDRV_IDC_REQ_CONNECT: Version mismatch. Requested: %#x Min: %#x Current: %#x\n",
+ pReq->u.In.uReqVersion, pReq->u.In.uMinVersion, (unsigned)SUPDRV_IDC_VERSION));
+ pReq->u.Out.pSession = NULL;
+ pReq->u.Out.uSessionVersion = 0xffffffff;
+ pReq->u.Out.uDriverVersion = SUPDRV_IDC_VERSION;
+ pReq->u.Out.uDriverRevision = VBOX_SVN_REV;
+ pReq->Hdr.rc = VERR_VERSION_MISMATCH;
+ return VINF_SUCCESS;
+ }
+
+ pReq->u.Out.pSession = NULL;
+ pReq->u.Out.uSessionVersion = SUPDRV_IDC_VERSION;
+ pReq->u.Out.uDriverVersion = SUPDRV_IDC_VERSION;
+ pReq->u.Out.uDriverRevision = VBOX_SVN_REV;
+
+ pReq->Hdr.rc = supdrvCreateSession(pDevExt, false /* fUser */, true /*fUnrestricted*/, &pSession);
+ if (RT_FAILURE(pReq->Hdr.rc))
+ {
+ OSDBGPRINT(("SUPDRV_IDC_REQ_CONNECT: failed to create session, rc=%d\n", pReq->Hdr.rc));
+ return VINF_SUCCESS;
+ }
+
+ pReq->u.Out.pSession = pSession;
+ pReq->Hdr.pSession = pSession;
+
+ return VINF_SUCCESS;
+ }
+
+ case SUPDRV_IDC_REQ_DISCONNECT:
+ {
+ REQ_CHECK_IDC_SIZE(SUPDRV_IDC_REQ_DISCONNECT, sizeof(*pReqHdr));
+
+ supdrvSessionRelease(pSession);
+ return pReqHdr->rc = VINF_SUCCESS;
+ }
+
+ case SUPDRV_IDC_REQ_GET_SYMBOL:
+ {
+ PSUPDRVIDCREQGETSYM pReq = (PSUPDRVIDCREQGETSYM)pReqHdr;
+ REQ_CHECK_IDC_SIZE(SUPDRV_IDC_REQ_GET_SYMBOL, sizeof(*pReq));
+
+ pReq->Hdr.rc = supdrvIDC_LdrGetSymbol(pDevExt, pSession, pReq);
+ return VINF_SUCCESS;
+ }
+
+ case SUPDRV_IDC_REQ_COMPONENT_REGISTER_FACTORY:
+ {
+ PSUPDRVIDCREQCOMPREGFACTORY pReq = (PSUPDRVIDCREQCOMPREGFACTORY)pReqHdr;
+ REQ_CHECK_IDC_SIZE(SUPDRV_IDC_REQ_COMPONENT_REGISTER_FACTORY, sizeof(*pReq));
+
+ pReq->Hdr.rc = SUPR0ComponentRegisterFactory(pSession, pReq->u.In.pFactory);
+ return VINF_SUCCESS;
+ }
+
+ case SUPDRV_IDC_REQ_COMPONENT_DEREGISTER_FACTORY:
+ {
+ PSUPDRVIDCREQCOMPDEREGFACTORY pReq = (PSUPDRVIDCREQCOMPDEREGFACTORY)pReqHdr;
+ REQ_CHECK_IDC_SIZE(SUPDRV_IDC_REQ_COMPONENT_DEREGISTER_FACTORY, sizeof(*pReq));
+
+ pReq->Hdr.rc = SUPR0ComponentDeregisterFactory(pSession, pReq->u.In.pFactory);
+ return VINF_SUCCESS;
+ }
+
+ default:
+ Log(("Unknown IDC %#lx\n", (long)uReq));
+ break;
+ }
+
+#undef REQ_CHECK_IDC_SIZE
+ return VERR_NOT_SUPPORTED;
+}
+
+
+/**
+ * Register a object for reference counting.
+ * The object is registered with one reference in the specified session.
+ *
+ * @returns Unique identifier on success (pointer).
+ * All future reference must use this identifier.
+ * @returns NULL on failure.
+ * @param pSession The caller's session.
+ * @param enmType The object type.
+ * @param pfnDestructor The destructore function which will be called when the reference count reaches 0.
+ * @param pvUser1 The first user argument.
+ * @param pvUser2 The second user argument.
+ */
+SUPR0DECL(void *) SUPR0ObjRegister(PSUPDRVSESSION pSession, SUPDRVOBJTYPE enmType, PFNSUPDRVDESTRUCTOR pfnDestructor, void *pvUser1, void *pvUser2)
+{
+ PSUPDRVDEVEXT pDevExt = pSession->pDevExt;
+ PSUPDRVOBJ pObj;
+ PSUPDRVUSAGE pUsage;
+
+ /*
+ * Validate the input.
+ */
+ AssertReturn(SUP_IS_SESSION_VALID(pSession), NULL);
+ AssertReturn(enmType > SUPDRVOBJTYPE_INVALID && enmType < SUPDRVOBJTYPE_END, NULL);
+ AssertPtrReturn(pfnDestructor, NULL);
+
+ /*
+ * Allocate and initialize the object.
+ */
+ pObj = (PSUPDRVOBJ)RTMemAlloc(sizeof(*pObj));
+ if (!pObj)
+ return NULL;
+ pObj->u32Magic = SUPDRVOBJ_MAGIC;
+ pObj->enmType = enmType;
+ pObj->pNext = NULL;
+ pObj->cUsage = 1;
+ pObj->pfnDestructor = pfnDestructor;
+ pObj->pvUser1 = pvUser1;
+ pObj->pvUser2 = pvUser2;
+ pObj->CreatorUid = pSession->Uid;
+ pObj->CreatorGid = pSession->Gid;
+ pObj->CreatorProcess= pSession->Process;
+ supdrvOSObjInitCreator(pObj, pSession);
+
+ /*
+ * Allocate the usage record.
+ * (We keep freed usage records around to simplify SUPR0ObjAddRefEx().)
+ */
+ RTSpinlockAcquire(pDevExt->Spinlock);
+
+ pUsage = pDevExt->pUsageFree;
+ if (pUsage)
+ pDevExt->pUsageFree = pUsage->pNext;
+ else
+ {
+ RTSpinlockRelease(pDevExt->Spinlock);
+ pUsage = (PSUPDRVUSAGE)RTMemAlloc(sizeof(*pUsage));
+ if (!pUsage)
+ {
+ RTMemFree(pObj);
+ return NULL;
+ }
+ RTSpinlockAcquire(pDevExt->Spinlock);
+ }
+
+ /*
+ * Insert the object and create the session usage record.
+ */
+ /* The object. */
+ pObj->pNext = pDevExt->pObjs;
+ pDevExt->pObjs = pObj;
+
+ /* The session record. */
+ pUsage->cUsage = 1;
+ pUsage->pObj = pObj;
+ pUsage->pNext = pSession->pUsage;
+ /* Log2(("SUPR0ObjRegister: pUsage=%p:{.pObj=%p, .pNext=%p}\n", pUsage, pUsage->pObj, pUsage->pNext)); */
+ pSession->pUsage = pUsage;
+
+ RTSpinlockRelease(pDevExt->Spinlock);
+
+ Log(("SUPR0ObjRegister: returns %p (pvUser1=%p, pvUser=%p)\n", pObj, pvUser1, pvUser2));
+ return pObj;
+}
+SUPR0_EXPORT_SYMBOL(SUPR0ObjRegister);
+
+
+/**
+ * Increment the reference counter for the object associating the reference
+ * with the specified session.
+ *
+ * @returns IPRT status code.
+ * @param pvObj The identifier returned by SUPR0ObjRegister().
+ * @param pSession The session which is referencing the object.
+ *
+ * @remarks The caller should not own any spinlocks and must carefully protect
+ * itself against potential race with the destructor so freed memory
+ * isn't accessed here.
+ */
+SUPR0DECL(int) SUPR0ObjAddRef(void *pvObj, PSUPDRVSESSION pSession)
+{
+ return SUPR0ObjAddRefEx(pvObj, pSession, false /* fNoBlocking */);
+}
+SUPR0_EXPORT_SYMBOL(SUPR0ObjAddRef);
+
+
+/**
+ * Increment the reference counter for the object associating the reference
+ * with the specified session.
+ *
+ * @returns IPRT status code.
+ * @retval VERR_TRY_AGAIN if fNoBlocking was set and a new usage record
+ * couldn't be allocated. (If you see this you're not doing the right
+ * thing and it won't ever work reliably.)
+ *
+ * @param pvObj The identifier returned by SUPR0ObjRegister().
+ * @param pSession The session which is referencing the object.
+ * @param fNoBlocking Set if it's not OK to block. Never try to make the
+ * first reference to an object in a session with this
+ * argument set.
+ *
+ * @remarks The caller should not own any spinlocks and must carefully protect
+ * itself against potential race with the destructor so freed memory
+ * isn't accessed here.
+ */
+SUPR0DECL(int) SUPR0ObjAddRefEx(void *pvObj, PSUPDRVSESSION pSession, bool fNoBlocking)
+{
+ PSUPDRVDEVEXT pDevExt = pSession->pDevExt;
+ PSUPDRVOBJ pObj = (PSUPDRVOBJ)pvObj;
+ int rc = VINF_SUCCESS;
+ PSUPDRVUSAGE pUsagePre;
+ PSUPDRVUSAGE pUsage;
+
+ /*
+ * Validate the input.
+ * Be ready for the destruction race (someone might be stuck in the
+ * destructor waiting a lock we own).
+ */
+ AssertReturn(SUP_IS_SESSION_VALID(pSession), VERR_INVALID_PARAMETER);
+ AssertPtrReturn(pObj, VERR_INVALID_POINTER);
+ AssertMsgReturn(pObj->u32Magic == SUPDRVOBJ_MAGIC || pObj->u32Magic == SUPDRVOBJ_MAGIC_DEAD,
+ ("Invalid pvObj=%p magic=%#x (expected %#x or %#x)\n", pvObj, pObj->u32Magic, SUPDRVOBJ_MAGIC, SUPDRVOBJ_MAGIC_DEAD),
+ VERR_INVALID_PARAMETER);
+
+ RTSpinlockAcquire(pDevExt->Spinlock);
+
+ if (RT_UNLIKELY(pObj->u32Magic != SUPDRVOBJ_MAGIC))
+ {
+ RTSpinlockRelease(pDevExt->Spinlock);
+
+ AssertMsgFailed(("pvObj=%p magic=%#x\n", pvObj, pObj->u32Magic));
+ return VERR_WRONG_ORDER;
+ }
+
+ /*
+ * Preallocate the usage record if we can.
+ */
+ pUsagePre = pDevExt->pUsageFree;
+ if (pUsagePre)
+ pDevExt->pUsageFree = pUsagePre->pNext;
+ else if (!fNoBlocking)
+ {
+ RTSpinlockRelease(pDevExt->Spinlock);
+ pUsagePre = (PSUPDRVUSAGE)RTMemAlloc(sizeof(*pUsagePre));
+ if (!pUsagePre)
+ return VERR_NO_MEMORY;
+
+ RTSpinlockAcquire(pDevExt->Spinlock);
+ if (RT_UNLIKELY(pObj->u32Magic != SUPDRVOBJ_MAGIC))
+ {
+ RTSpinlockRelease(pDevExt->Spinlock);
+
+ AssertMsgFailed(("pvObj=%p magic=%#x\n", pvObj, pObj->u32Magic));
+ return VERR_WRONG_ORDER;
+ }
+ }
+
+ /*
+ * Reference the object.
+ */
+ pObj->cUsage++;
+
+ /*
+ * Look for the session record.
+ */
+ for (pUsage = pSession->pUsage; pUsage; pUsage = pUsage->pNext)
+ {
+ /*Log(("SUPR0AddRef: pUsage=%p:{.pObj=%p, .pNext=%p}\n", pUsage, pUsage->pObj, pUsage->pNext));*/
+ if (pUsage->pObj == pObj)
+ break;
+ }
+ if (pUsage)
+ pUsage->cUsage++;
+ else if (pUsagePre)
+ {
+ /* create a new session record. */
+ pUsagePre->cUsage = 1;
+ pUsagePre->pObj = pObj;
+ pUsagePre->pNext = pSession->pUsage;
+ pSession->pUsage = pUsagePre;
+ /*Log(("SUPR0AddRef: pUsagePre=%p:{.pObj=%p, .pNext=%p}\n", pUsagePre, pUsagePre->pObj, pUsagePre->pNext));*/
+
+ pUsagePre = NULL;
+ }
+ else
+ {
+ pObj->cUsage--;
+ rc = VERR_TRY_AGAIN;
+ }
+
+ /*
+ * Put any unused usage record into the free list..
+ */
+ if (pUsagePre)
+ {
+ pUsagePre->pNext = pDevExt->pUsageFree;
+ pDevExt->pUsageFree = pUsagePre;
+ }
+
+ RTSpinlockRelease(pDevExt->Spinlock);
+
+ return rc;
+}
+SUPR0_EXPORT_SYMBOL(SUPR0ObjAddRefEx);
+
+
+/**
+ * Decrement / destroy a reference counter record for an object.
+ *
+ * The object is uniquely identified by pfnDestructor+pvUser1+pvUser2.
+ *
+ * @returns IPRT status code.
+ * @retval VINF_SUCCESS if not destroyed.
+ * @retval VINF_OBJECT_DESTROYED if it's destroyed by this release call.
+ * @retval VERR_INVALID_PARAMETER if the object isn't valid. Will assert in
+ * string builds.
+ *
+ * @param pvObj The identifier returned by SUPR0ObjRegister().
+ * @param pSession The session which is referencing the object.
+ */
+SUPR0DECL(int) SUPR0ObjRelease(void *pvObj, PSUPDRVSESSION pSession)
+{
+ PSUPDRVDEVEXT pDevExt = pSession->pDevExt;
+ PSUPDRVOBJ pObj = (PSUPDRVOBJ)pvObj;
+ int rc = VERR_INVALID_PARAMETER;
+ PSUPDRVUSAGE pUsage;
+ PSUPDRVUSAGE pUsagePrev;
+
+ /*
+ * Validate the input.
+ */
+ AssertReturn(SUP_IS_SESSION_VALID(pSession), VERR_INVALID_PARAMETER);
+ AssertMsgReturn(RT_VALID_PTR(pObj) && pObj->u32Magic == SUPDRVOBJ_MAGIC,
+ ("Invalid pvObj=%p magic=%#x (expected %#x)\n", pvObj, pObj ? pObj->u32Magic : 0, SUPDRVOBJ_MAGIC),
+ VERR_INVALID_PARAMETER);
+
+ /*
+ * Acquire the spinlock and look for the usage record.
+ */
+ RTSpinlockAcquire(pDevExt->Spinlock);
+
+ for (pUsagePrev = NULL, pUsage = pSession->pUsage;
+ pUsage;
+ pUsagePrev = pUsage, pUsage = pUsage->pNext)
+ {
+ /*Log2(("SUPR0ObjRelease: pUsage=%p:{.pObj=%p, .pNext=%p}\n", pUsage, pUsage->pObj, pUsage->pNext));*/
+ if (pUsage->pObj == pObj)
+ {
+ rc = VINF_SUCCESS;
+ AssertMsg(pUsage->cUsage >= 1 && pObj->cUsage >= pUsage->cUsage, ("glob %d; sess %d\n", pObj->cUsage, pUsage->cUsage));
+ if (pUsage->cUsage > 1)
+ {
+ pObj->cUsage--;
+ pUsage->cUsage--;
+ }
+ else
+ {
+ /*
+ * Free the session record.
+ */
+ if (pUsagePrev)
+ pUsagePrev->pNext = pUsage->pNext;
+ else
+ pSession->pUsage = pUsage->pNext;
+ pUsage->pNext = pDevExt->pUsageFree;
+ pDevExt->pUsageFree = pUsage;
+
+ /* What about the object? */
+ if (pObj->cUsage > 1)
+ pObj->cUsage--;
+ else
+ {
+ /*
+ * Object is to be destroyed, unlink it.
+ */
+ pObj->u32Magic = SUPDRVOBJ_MAGIC_DEAD;
+ rc = VINF_OBJECT_DESTROYED;
+ if (pDevExt->pObjs == pObj)
+ pDevExt->pObjs = pObj->pNext;
+ else
+ {
+ PSUPDRVOBJ pObjPrev;
+ for (pObjPrev = pDevExt->pObjs; pObjPrev; pObjPrev = pObjPrev->pNext)
+ if (pObjPrev->pNext == pObj)
+ {
+ pObjPrev->pNext = pObj->pNext;
+ break;
+ }
+ Assert(pObjPrev);
+ }
+ }
+ }
+ break;
+ }
+ }
+
+ RTSpinlockRelease(pDevExt->Spinlock);
+
+ /*
+ * Call the destructor and free the object if required.
+ */
+ if (rc == VINF_OBJECT_DESTROYED)
+ {
+ Log(("SUPR0ObjRelease: destroying %p/%d (%p/%p) cpid=%RTproc pid=%RTproc dtor=%p\n",
+ pObj, pObj->enmType, pObj->pvUser1, pObj->pvUser2, pObj->CreatorProcess, RTProcSelf(), pObj->pfnDestructor));
+ if (pObj->pfnDestructor)
+ pObj->pfnDestructor(pObj, pObj->pvUser1, pObj->pvUser2);
+ RTMemFree(pObj);
+ }
+
+ AssertMsg(pUsage, ("pvObj=%p\n", pvObj));
+ return rc;
+}
+SUPR0_EXPORT_SYMBOL(SUPR0ObjRelease);
+
+
+/**
+ * Verifies that the current process can access the specified object.
+ *
+ * @returns The following IPRT status code:
+ * @retval VINF_SUCCESS if access was granted.
+ * @retval VERR_PERMISSION_DENIED if denied access.
+ * @retval VERR_INVALID_PARAMETER if invalid parameter.
+ *
+ * @param pvObj The identifier returned by SUPR0ObjRegister().
+ * @param pSession The session which wishes to access the object.
+ * @param pszObjName Object string name. This is optional and depends on the object type.
+ *
+ * @remark The caller is responsible for making sure the object isn't removed while
+ * we're inside this function. If uncertain about this, just call AddRef before calling us.
+ */
+SUPR0DECL(int) SUPR0ObjVerifyAccess(void *pvObj, PSUPDRVSESSION pSession, const char *pszObjName)
+{
+ PSUPDRVOBJ pObj = (PSUPDRVOBJ)pvObj;
+ int rc;
+
+ /*
+ * Validate the input.
+ */
+ AssertReturn(SUP_IS_SESSION_VALID(pSession), VERR_INVALID_PARAMETER);
+ AssertMsgReturn(RT_VALID_PTR(pObj) && pObj->u32Magic == SUPDRVOBJ_MAGIC,
+ ("Invalid pvObj=%p magic=%#x (exepcted %#x)\n", pvObj, pObj ? pObj->u32Magic : 0, SUPDRVOBJ_MAGIC),
+ VERR_INVALID_PARAMETER);
+
+ /*
+ * Check access. (returns true if a decision has been made.)
+ */
+ rc = VERR_INTERNAL_ERROR;
+ if (supdrvOSObjCanAccess(pObj, pSession, pszObjName, &rc))
+ return rc;
+
+ /*
+ * Default policy is to allow the user to access his own
+ * stuff but nothing else.
+ */
+ if (pObj->CreatorUid == pSession->Uid)
+ return VINF_SUCCESS;
+ return VERR_PERMISSION_DENIED;
+}
+SUPR0_EXPORT_SYMBOL(SUPR0ObjVerifyAccess);
+
+
+/**
+ * API for the VMMR0 module to get the SUPDRVSESSION::pSessionVM member.
+ *
+ * @returns The associated VM pointer.
+ * @param pSession The session of the current thread.
+ */
+SUPR0DECL(PVM) SUPR0GetSessionVM(PSUPDRVSESSION pSession)
+{
+ AssertReturn(SUP_IS_SESSION_VALID(pSession), NULL);
+ return pSession->pSessionVM;
+}
+SUPR0_EXPORT_SYMBOL(SUPR0GetSessionVM);
+
+
+/**
+ * API for the VMMR0 module to get the SUPDRVSESSION::pSessionGVM member.
+ *
+ * @returns The associated GVM pointer.
+ * @param pSession The session of the current thread.
+ */
+SUPR0DECL(PGVM) SUPR0GetSessionGVM(PSUPDRVSESSION pSession)
+{
+ AssertReturn(SUP_IS_SESSION_VALID(pSession), NULL);
+ return pSession->pSessionGVM;
+}
+SUPR0_EXPORT_SYMBOL(SUPR0GetSessionGVM);
+
+
+/**
+ * API for the VMMR0 module to work the SUPDRVSESSION::pSessionVM member.
+ *
+ * This will fail if there is already a VM associated with the session and pVM
+ * isn't NULL.
+ *
+ * @retval VINF_SUCCESS
+ * @retval VERR_ALREADY_EXISTS if there already is a VM associated with the
+ * session.
+ * @retval VERR_INVALID_PARAMETER if only one of the parameters are NULL or if
+ * the session is invalid.
+ *
+ * @param pSession The session of the current thread.
+ * @param pGVM The GVM to associate with the session. Pass NULL to
+ * dissassociate.
+ * @param pVM The VM to associate with the session. Pass NULL to
+ * dissassociate.
+ */
+SUPR0DECL(int) SUPR0SetSessionVM(PSUPDRVSESSION pSession, PGVM pGVM, PVM pVM)
+{
+ AssertReturn(SUP_IS_SESSION_VALID(pSession), VERR_INVALID_PARAMETER);
+ AssertReturn((pGVM != NULL) == (pVM != NULL), VERR_INVALID_PARAMETER);
+
+ RTSpinlockAcquire(pSession->pDevExt->Spinlock);
+ if (pGVM)
+ {
+ if (!pSession->pSessionGVM)
+ {
+ pSession->pSessionGVM = pGVM;
+ pSession->pSessionVM = pVM;
+ pSession->pFastIoCtrlVM = NULL;
+ }
+ else
+ {
+ RTSpinlockRelease(pSession->pDevExt->Spinlock);
+ SUPR0Printf("SUPR0SetSessionVM: Unable to associated GVM/VM %p/%p with session %p as it has %p/%p already!\n",
+ pGVM, pVM, pSession, pSession->pSessionGVM, pSession->pSessionVM);
+ return VERR_ALREADY_EXISTS;
+ }
+ }
+ else
+ {
+ pSession->pSessionGVM = NULL;
+ pSession->pSessionVM = NULL;
+ pSession->pFastIoCtrlVM = NULL;
+ }
+ RTSpinlockRelease(pSession->pDevExt->Spinlock);
+ return VINF_SUCCESS;
+}
+SUPR0_EXPORT_SYMBOL(SUPR0SetSessionVM);
+
+
+/**
+ * For getting SUPDRVSESSION::Uid.
+ *
+ * @returns The session UID. NIL_RTUID if invalid pointer or not successfully
+ * set by the host code.
+ * @param pSession The session of the current thread.
+ */
+SUPR0DECL(RTUID) SUPR0GetSessionUid(PSUPDRVSESSION pSession)
+{
+ AssertReturn(SUP_IS_SESSION_VALID(pSession), NIL_RTUID);
+ return pSession->Uid;
+}
+SUPR0_EXPORT_SYMBOL(SUPR0GetSessionUid);
+
+
+/** @copydoc RTLogDefaultInstanceEx
+ * @remarks To allow overriding RTLogDefaultInstanceEx locally. */
+SUPR0DECL(struct RTLOGGER *) SUPR0DefaultLogInstanceEx(uint32_t fFlagsAndGroup)
+{
+ return RTLogDefaultInstanceEx(fFlagsAndGroup);
+}
+SUPR0_EXPORT_SYMBOL(SUPR0DefaultLogInstanceEx);
+
+
+/** @copydoc RTLogGetDefaultInstanceEx
+ * @remarks To allow overriding RTLogGetDefaultInstanceEx locally. */
+SUPR0DECL(struct RTLOGGER *) SUPR0GetDefaultLogInstanceEx(uint32_t fFlagsAndGroup)
+{
+ return RTLogGetDefaultInstanceEx(fFlagsAndGroup);
+}
+SUPR0_EXPORT_SYMBOL(SUPR0GetDefaultLogInstanceEx);
+
+
+/** @copydoc RTLogRelGetDefaultInstanceEx
+ * @remarks To allow overriding RTLogRelGetDefaultInstanceEx locally. */
+SUPR0DECL(struct RTLOGGER *) SUPR0GetDefaultLogRelInstanceEx(uint32_t fFlagsAndGroup)
+{
+ return RTLogRelGetDefaultInstanceEx(fFlagsAndGroup);
+}
+SUPR0_EXPORT_SYMBOL(SUPR0GetDefaultLogRelInstanceEx);
+
+
+/**
+ * Lock pages.
+ *
+ * @returns IPRT status code.
+ * @param pSession Session to which the locked memory should be associated.
+ * @param pvR3 Start of the memory range to lock.
+ * This must be page aligned.
+ * @param cPages Number of pages to lock.
+ * @param paPages Where to put the physical addresses of locked memory.
+ */
+SUPR0DECL(int) SUPR0LockMem(PSUPDRVSESSION pSession, RTR3PTR pvR3, uint32_t cPages, PRTHCPHYS paPages)
+{
+ int rc;
+ SUPDRVMEMREF Mem = { NIL_RTR0MEMOBJ, NIL_RTR0MEMOBJ, MEMREF_TYPE_UNUSED };
+ const size_t cb = (size_t)cPages << PAGE_SHIFT;
+ LogFlow(("SUPR0LockMem: pSession=%p pvR3=%p cPages=%d paPages=%p\n", pSession, (void *)pvR3, cPages, paPages));
+
+ /*
+ * Verify input.
+ */
+ AssertReturn(SUP_IS_SESSION_VALID(pSession), VERR_INVALID_PARAMETER);
+ AssertPtrReturn(paPages, VERR_INVALID_PARAMETER);
+ if ( RT_ALIGN_R3PT(pvR3, PAGE_SIZE, RTR3PTR) != pvR3
+ || !pvR3)
+ {
+ Log(("pvR3 (%p) must be page aligned and not NULL!\n", (void *)pvR3));
+ return VERR_INVALID_PARAMETER;
+ }
+
+ /*
+ * Let IPRT do the job.
+ */
+ Mem.eType = MEMREF_TYPE_LOCKED;
+ rc = RTR0MemObjLockUser(&Mem.MemObj, pvR3, cb, RTMEM_PROT_READ | RTMEM_PROT_WRITE, NIL_RTR0PROCESS);
+ if (RT_SUCCESS(rc))
+ {
+ uint32_t iPage = cPages;
+ AssertMsg(RTR0MemObjAddressR3(Mem.MemObj) == pvR3, ("%p == %p\n", RTR0MemObjAddressR3(Mem.MemObj), pvR3));
+ AssertMsg(RTR0MemObjSize(Mem.MemObj) == cb, ("%x == %x\n", RTR0MemObjSize(Mem.MemObj), cb));
+
+ while (iPage-- > 0)
+ {
+ paPages[iPage] = RTR0MemObjGetPagePhysAddr(Mem.MemObj, iPage);
+ if (RT_UNLIKELY(paPages[iPage] == NIL_RTCCPHYS))
+ {
+ AssertMsgFailed(("iPage=%d\n", iPage));
+ rc = VERR_INTERNAL_ERROR;
+ break;
+ }
+ }
+ if (RT_SUCCESS(rc))
+ rc = supdrvMemAdd(&Mem, pSession);
+ if (RT_FAILURE(rc))
+ {
+ int rc2 = RTR0MemObjFree(Mem.MemObj, false);
+ AssertRC(rc2);
+ }
+ }
+
+ return rc;
+}
+SUPR0_EXPORT_SYMBOL(SUPR0LockMem);
+
+
+/**
+ * Unlocks the memory pointed to by pv.
+ *
+ * @returns IPRT status code.
+ * @param pSession Session to which the memory was locked.
+ * @param pvR3 Memory to unlock.
+ */
+SUPR0DECL(int) SUPR0UnlockMem(PSUPDRVSESSION pSession, RTR3PTR pvR3)
+{
+ LogFlow(("SUPR0UnlockMem: pSession=%p pvR3=%p\n", pSession, (void *)pvR3));
+ AssertReturn(SUP_IS_SESSION_VALID(pSession), VERR_INVALID_PARAMETER);
+ return supdrvMemRelease(pSession, (RTHCUINTPTR)pvR3, MEMREF_TYPE_LOCKED);
+}
+SUPR0_EXPORT_SYMBOL(SUPR0UnlockMem);
+
+
+/**
+ * Allocates a chunk of page aligned memory with contiguous and fixed physical
+ * backing.
+ *
+ * @returns IPRT status code.
+ * @param pSession Session data.
+ * @param cPages Number of pages to allocate.
+ * @param ppvR0 Where to put the address of Ring-0 mapping the allocated memory.
+ * @param ppvR3 Where to put the address of Ring-3 mapping the allocated memory.
+ * @param pHCPhys Where to put the physical address of allocated memory.
+ */
+SUPR0DECL(int) SUPR0ContAlloc(PSUPDRVSESSION pSession, uint32_t cPages, PRTR0PTR ppvR0, PRTR3PTR ppvR3, PRTHCPHYS pHCPhys)
+{
+ int rc;
+ SUPDRVMEMREF Mem = { NIL_RTR0MEMOBJ, NIL_RTR0MEMOBJ, MEMREF_TYPE_UNUSED };
+ LogFlow(("SUPR0ContAlloc: pSession=%p cPages=%d ppvR0=%p ppvR3=%p pHCPhys=%p\n", pSession, cPages, ppvR0, ppvR3, pHCPhys));
+
+ /*
+ * Validate input.
+ */
+ AssertReturn(SUP_IS_SESSION_VALID(pSession), VERR_INVALID_PARAMETER);
+ if (!ppvR3 || !ppvR0 || !pHCPhys)
+ {
+ Log(("Null pointer. All of these should be set: pSession=%p ppvR0=%p ppvR3=%p pHCPhys=%p\n",
+ pSession, ppvR0, ppvR3, pHCPhys));
+ return VERR_INVALID_PARAMETER;
+
+ }
+ if (cPages < 1 || cPages >= 256)
+ {
+ Log(("Illegal request cPages=%d, must be greater than 0 and smaller than 256.\n", cPages));
+ return VERR_PAGE_COUNT_OUT_OF_RANGE;
+ }
+
+ /*
+ * Let IPRT do the job.
+ */
+ rc = RTR0MemObjAllocCont(&Mem.MemObj, cPages << PAGE_SHIFT, true /* executable R0 mapping */);
+ if (RT_SUCCESS(rc))
+ {
+ int rc2;
+ rc = RTR0MemObjMapUser(&Mem.MapObjR3, Mem.MemObj, (RTR3PTR)-1, 0,
+ RTMEM_PROT_EXEC | RTMEM_PROT_WRITE | RTMEM_PROT_READ, NIL_RTR0PROCESS);
+ if (RT_SUCCESS(rc))
+ {
+ Mem.eType = MEMREF_TYPE_CONT;
+ rc = supdrvMemAdd(&Mem, pSession);
+ if (!rc)
+ {
+ *ppvR0 = RTR0MemObjAddress(Mem.MemObj);
+ *ppvR3 = RTR0MemObjAddressR3(Mem.MapObjR3);
+ *pHCPhys = RTR0MemObjGetPagePhysAddr(Mem.MemObj, 0);
+ return 0;
+ }
+
+ rc2 = RTR0MemObjFree(Mem.MapObjR3, false);
+ AssertRC(rc2);
+ }
+ rc2 = RTR0MemObjFree(Mem.MemObj, false);
+ AssertRC(rc2);
+ }
+
+ return rc;
+}
+SUPR0_EXPORT_SYMBOL(SUPR0ContAlloc);
+
+
+/**
+ * Frees memory allocated using SUPR0ContAlloc().
+ *
+ * @returns IPRT status code.
+ * @param pSession The session to which the memory was allocated.
+ * @param uPtr Pointer to the memory (ring-3 or ring-0).
+ */
+SUPR0DECL(int) SUPR0ContFree(PSUPDRVSESSION pSession, RTHCUINTPTR uPtr)
+{
+ LogFlow(("SUPR0ContFree: pSession=%p uPtr=%p\n", pSession, (void *)uPtr));
+ AssertReturn(SUP_IS_SESSION_VALID(pSession), VERR_INVALID_PARAMETER);
+ return supdrvMemRelease(pSession, uPtr, MEMREF_TYPE_CONT);
+}
+SUPR0_EXPORT_SYMBOL(SUPR0ContFree);
+
+
+/**
+ * Allocates a chunk of page aligned memory with fixed physical backing below 4GB.
+ *
+ * The memory isn't zeroed.
+ *
+ * @returns IPRT status code.
+ * @param pSession Session data.
+ * @param cPages Number of pages to allocate.
+ * @param ppvR0 Where to put the address of Ring-0 mapping of the allocated memory.
+ * @param ppvR3 Where to put the address of Ring-3 mapping of the allocated memory.
+ * @param paPages Where to put the physical addresses of allocated memory.
+ */
+SUPR0DECL(int) SUPR0LowAlloc(PSUPDRVSESSION pSession, uint32_t cPages, PRTR0PTR ppvR0, PRTR3PTR ppvR3, PRTHCPHYS paPages)
+{
+ unsigned iPage;
+ int rc;
+ SUPDRVMEMREF Mem = { NIL_RTR0MEMOBJ, NIL_RTR0MEMOBJ, MEMREF_TYPE_UNUSED };
+ LogFlow(("SUPR0LowAlloc: pSession=%p cPages=%d ppvR3=%p ppvR0=%p paPages=%p\n", pSession, cPages, ppvR3, ppvR0, paPages));
+
+ /*
+ * Validate input.
+ */
+ AssertReturn(SUP_IS_SESSION_VALID(pSession), VERR_INVALID_PARAMETER);
+ if (!ppvR3 || !ppvR0 || !paPages)
+ {
+ Log(("Null pointer. All of these should be set: pSession=%p ppvR3=%p ppvR0=%p paPages=%p\n",
+ pSession, ppvR3, ppvR0, paPages));
+ return VERR_INVALID_PARAMETER;
+
+ }
+ if (cPages < 1 || cPages >= 256)
+ {
+ Log(("Illegal request cPages=%d, must be greater than 0 and smaller than 256.\n", cPages));
+ return VERR_PAGE_COUNT_OUT_OF_RANGE;
+ }
+
+ /*
+ * Let IPRT do the work.
+ */
+ rc = RTR0MemObjAllocLow(&Mem.MemObj, cPages << PAGE_SHIFT, true /* executable ring-0 mapping */);
+ if (RT_SUCCESS(rc))
+ {
+ int rc2;
+ rc = RTR0MemObjMapUser(&Mem.MapObjR3, Mem.MemObj, (RTR3PTR)-1, 0,
+ RTMEM_PROT_EXEC | RTMEM_PROT_WRITE | RTMEM_PROT_READ, NIL_RTR0PROCESS);
+ if (RT_SUCCESS(rc))
+ {
+ Mem.eType = MEMREF_TYPE_LOW;
+ rc = supdrvMemAdd(&Mem, pSession);
+ if (!rc)
+ {
+ for (iPage = 0; iPage < cPages; iPage++)
+ {
+ paPages[iPage] = RTR0MemObjGetPagePhysAddr(Mem.MemObj, iPage);
+ AssertMsg(!(paPages[iPage] & (PAGE_SIZE - 1)), ("iPage=%d Phys=%RHp\n", paPages[iPage]));
+ }
+ *ppvR0 = RTR0MemObjAddress(Mem.MemObj);
+ *ppvR3 = RTR0MemObjAddressR3(Mem.MapObjR3);
+ return 0;
+ }
+
+ rc2 = RTR0MemObjFree(Mem.MapObjR3, false);
+ AssertRC(rc2);
+ }
+
+ rc2 = RTR0MemObjFree(Mem.MemObj, false);
+ AssertRC(rc2);
+ }
+
+ return rc;
+}
+SUPR0_EXPORT_SYMBOL(SUPR0LowAlloc);
+
+
+/**
+ * Frees memory allocated using SUPR0LowAlloc().
+ *
+ * @returns IPRT status code.
+ * @param pSession The session to which the memory was allocated.
+ * @param uPtr Pointer to the memory (ring-3 or ring-0).
+ */
+SUPR0DECL(int) SUPR0LowFree(PSUPDRVSESSION pSession, RTHCUINTPTR uPtr)
+{
+ LogFlow(("SUPR0LowFree: pSession=%p uPtr=%p\n", pSession, (void *)uPtr));
+ AssertReturn(SUP_IS_SESSION_VALID(pSession), VERR_INVALID_PARAMETER);
+ return supdrvMemRelease(pSession, uPtr, MEMREF_TYPE_LOW);
+}
+SUPR0_EXPORT_SYMBOL(SUPR0LowFree);
+
+
+
+/**
+ * Allocates a chunk of memory with both R0 and R3 mappings.
+ * The memory is fixed and it's possible to query the physical addresses using SUPR0MemGetPhys().
+ *
+ * @returns IPRT status code.
+ * @param pSession The session to associated the allocation with.
+ * @param cb Number of bytes to allocate.
+ * @param ppvR0 Where to store the address of the Ring-0 mapping.
+ * @param ppvR3 Where to store the address of the Ring-3 mapping.
+ */
+SUPR0DECL(int) SUPR0MemAlloc(PSUPDRVSESSION pSession, uint32_t cb, PRTR0PTR ppvR0, PRTR3PTR ppvR3)
+{
+ int rc;
+ SUPDRVMEMREF Mem = { NIL_RTR0MEMOBJ, NIL_RTR0MEMOBJ, MEMREF_TYPE_UNUSED };
+ LogFlow(("SUPR0MemAlloc: pSession=%p cb=%d ppvR0=%p ppvR3=%p\n", pSession, cb, ppvR0, ppvR3));
+
+ /*
+ * Validate input.
+ */
+ AssertReturn(SUP_IS_SESSION_VALID(pSession), VERR_INVALID_PARAMETER);
+ AssertPtrReturn(ppvR0, VERR_INVALID_POINTER);
+ AssertPtrReturn(ppvR3, VERR_INVALID_POINTER);
+ if (cb < 1 || cb >= _4M)
+ {
+ Log(("Illegal request cb=%u; must be greater than 0 and smaller than 4MB.\n", cb));
+ return VERR_INVALID_PARAMETER;
+ }
+
+ /*
+ * Let IPRT do the work.
+ */
+ rc = RTR0MemObjAllocPage(&Mem.MemObj, cb, true /* executable ring-0 mapping */);
+ if (RT_SUCCESS(rc))
+ {
+ int rc2;
+ rc = RTR0MemObjMapUser(&Mem.MapObjR3, Mem.MemObj, (RTR3PTR)-1, 0,
+ RTMEM_PROT_EXEC | RTMEM_PROT_WRITE | RTMEM_PROT_READ, NIL_RTR0PROCESS);
+ if (RT_SUCCESS(rc))
+ {
+ Mem.eType = MEMREF_TYPE_MEM;
+ rc = supdrvMemAdd(&Mem, pSession);
+ if (!rc)
+ {
+ *ppvR0 = RTR0MemObjAddress(Mem.MemObj);
+ *ppvR3 = RTR0MemObjAddressR3(Mem.MapObjR3);
+ return VINF_SUCCESS;
+ }
+
+ rc2 = RTR0MemObjFree(Mem.MapObjR3, false);
+ AssertRC(rc2);
+ }
+
+ rc2 = RTR0MemObjFree(Mem.MemObj, false);
+ AssertRC(rc2);
+ }
+
+ return rc;
+}
+SUPR0_EXPORT_SYMBOL(SUPR0MemAlloc);
+
+
+/**
+ * Get the physical addresses of memory allocated using SUPR0MemAlloc().
+ *
+ * @returns IPRT status code.
+ * @param pSession The session to which the memory was allocated.
+ * @param uPtr The Ring-0 or Ring-3 address returned by SUPR0MemAlloc().
+ * @param paPages Where to store the physical addresses.
+ */
+SUPR0DECL(int) SUPR0MemGetPhys(PSUPDRVSESSION pSession, RTHCUINTPTR uPtr, PSUPPAGE paPages) /** @todo switch this bugger to RTHCPHYS */
+{
+ PSUPDRVBUNDLE pBundle;
+ LogFlow(("SUPR0MemGetPhys: pSession=%p uPtr=%p paPages=%p\n", pSession, (void *)uPtr, paPages));
+
+ /*
+ * Validate input.
+ */
+ AssertReturn(SUP_IS_SESSION_VALID(pSession), VERR_INVALID_PARAMETER);
+ AssertPtrReturn(paPages, VERR_INVALID_POINTER);
+ AssertReturn(uPtr, VERR_INVALID_PARAMETER);
+
+ /*
+ * Search for the address.
+ */
+ RTSpinlockAcquire(pSession->Spinlock);
+ for (pBundle = &pSession->Bundle; pBundle; pBundle = pBundle->pNext)
+ {
+ if (pBundle->cUsed > 0)
+ {
+ unsigned i;
+ for (i = 0; i < RT_ELEMENTS(pBundle->aMem); i++)
+ {
+ if ( pBundle->aMem[i].eType == MEMREF_TYPE_MEM
+ && pBundle->aMem[i].MemObj != NIL_RTR0MEMOBJ
+ && ( (RTHCUINTPTR)RTR0MemObjAddress(pBundle->aMem[i].MemObj) == uPtr
+ || ( pBundle->aMem[i].MapObjR3 != NIL_RTR0MEMOBJ
+ && RTR0MemObjAddressR3(pBundle->aMem[i].MapObjR3) == uPtr)
+ )
+ )
+ {
+ const size_t cPages = RTR0MemObjSize(pBundle->aMem[i].MemObj) >> PAGE_SHIFT;
+ size_t iPage;
+ for (iPage = 0; iPage < cPages; iPage++)
+ {
+ paPages[iPage].Phys = RTR0MemObjGetPagePhysAddr(pBundle->aMem[i].MemObj, iPage);
+ paPages[iPage].uReserved = 0;
+ }
+ RTSpinlockRelease(pSession->Spinlock);
+ return VINF_SUCCESS;
+ }
+ }
+ }
+ }
+ RTSpinlockRelease(pSession->Spinlock);
+ Log(("Failed to find %p!!!\n", (void *)uPtr));
+ return VERR_INVALID_PARAMETER;
+}
+SUPR0_EXPORT_SYMBOL(SUPR0MemGetPhys);
+
+
+/**
+ * Free memory allocated by SUPR0MemAlloc().
+ *
+ * @returns IPRT status code.
+ * @param pSession The session owning the allocation.
+ * @param uPtr The Ring-0 or Ring-3 address returned by SUPR0MemAlloc().
+ */
+SUPR0DECL(int) SUPR0MemFree(PSUPDRVSESSION pSession, RTHCUINTPTR uPtr)
+{
+ LogFlow(("SUPR0MemFree: pSession=%p uPtr=%p\n", pSession, (void *)uPtr));
+ AssertReturn(SUP_IS_SESSION_VALID(pSession), VERR_INVALID_PARAMETER);
+ return supdrvMemRelease(pSession, uPtr, MEMREF_TYPE_MEM);
+}
+SUPR0_EXPORT_SYMBOL(SUPR0MemFree);
+
+
+/**
+ * Allocates a chunk of memory with a kernel or/and a user mode mapping.
+ *
+ * The memory is fixed and it's possible to query the physical addresses using
+ * SUPR0MemGetPhys().
+ *
+ * @returns IPRT status code.
+ * @param pSession The session to associated the allocation with.
+ * @param cPages The number of pages to allocate.
+ * @param fFlags Flags, reserved for the future. Must be zero.
+ * @param ppvR3 Where to store the address of the Ring-3 mapping.
+ * NULL if no ring-3 mapping.
+ * @param ppvR0 Where to store the address of the Ring-0 mapping.
+ * NULL if no ring-0 mapping.
+ * @param paPages Where to store the addresses of the pages. Optional.
+ */
+SUPR0DECL(int) SUPR0PageAllocEx(PSUPDRVSESSION pSession, uint32_t cPages, uint32_t fFlags, PRTR3PTR ppvR3, PRTR0PTR ppvR0, PRTHCPHYS paPages)
+{
+ int rc;
+ SUPDRVMEMREF Mem = { NIL_RTR0MEMOBJ, NIL_RTR0MEMOBJ, MEMREF_TYPE_UNUSED };
+ LogFlow(("SUPR0PageAlloc: pSession=%p cb=%d ppvR3=%p\n", pSession, cPages, ppvR3));
+
+ /*
+ * Validate input. The allowed allocation size must be at least equal to the maximum guest VRAM size.
+ */
+ AssertReturn(SUP_IS_SESSION_VALID(pSession), VERR_INVALID_PARAMETER);
+ AssertPtrNullReturn(ppvR3, VERR_INVALID_POINTER);
+ AssertPtrNullReturn(ppvR0, VERR_INVALID_POINTER);
+ AssertReturn(ppvR3 || ppvR0, VERR_INVALID_PARAMETER);
+ AssertReturn(!fFlags, VERR_INVALID_PARAMETER);
+ if (cPages < 1 || cPages > VBOX_MAX_ALLOC_PAGE_COUNT)
+ {
+ Log(("SUPR0PageAlloc: Illegal request cb=%u; must be greater than 0 and smaller than %uMB (VBOX_MAX_ALLOC_PAGE_COUNT pages).\n", cPages, VBOX_MAX_ALLOC_PAGE_COUNT * (_1M / _4K)));
+ return VERR_PAGE_COUNT_OUT_OF_RANGE;
+ }
+
+ /*
+ * Let IPRT do the work.
+ */
+ if (ppvR0)
+ rc = RTR0MemObjAllocPage(&Mem.MemObj, (size_t)cPages * PAGE_SIZE, false /*fExecutable*/);
+ else
+ rc = RTR0MemObjAllocPhysNC(&Mem.MemObj, (size_t)cPages * PAGE_SIZE, NIL_RTHCPHYS);
+ if (RT_SUCCESS(rc))
+ {
+ int rc2;
+ if (ppvR3)
+ {
+ /* Make sure memory mapped into ring-3 is zero initialized if we can: */
+ if ( ppvR0
+ && !RTR0MemObjWasZeroInitialized(Mem.MemObj))
+ {
+ void *pv = RTR0MemObjAddress(Mem.MemObj);
+ Assert(pv || !ppvR0);
+ if (pv)
+ RT_BZERO(pv, (size_t)cPages * PAGE_SIZE);
+ }
+
+ rc = RTR0MemObjMapUser(&Mem.MapObjR3, Mem.MemObj, (RTR3PTR)-1, 0, RTMEM_PROT_WRITE | RTMEM_PROT_READ, NIL_RTR0PROCESS);
+ }
+ else
+ Mem.MapObjR3 = NIL_RTR0MEMOBJ;
+ if (RT_SUCCESS(rc))
+ {
+ Mem.eType = MEMREF_TYPE_PAGE;
+ rc = supdrvMemAdd(&Mem, pSession);
+ if (!rc)
+ {
+ if (ppvR3)
+ *ppvR3 = RTR0MemObjAddressR3(Mem.MapObjR3);
+ if (ppvR0)
+ *ppvR0 = RTR0MemObjAddress(Mem.MemObj);
+ if (paPages)
+ {
+ uint32_t iPage = cPages;
+ while (iPage-- > 0)
+ {
+ paPages[iPage] = RTR0MemObjGetPagePhysAddr(Mem.MapObjR3, iPage);
+ Assert(paPages[iPage] != NIL_RTHCPHYS);
+ }
+ }
+ return VINF_SUCCESS;
+ }
+
+ rc2 = RTR0MemObjFree(Mem.MapObjR3, false);
+ AssertRC(rc2);
+ }
+
+ rc2 = RTR0MemObjFree(Mem.MemObj, false);
+ AssertRC(rc2);
+ }
+ return rc;
+}
+SUPR0_EXPORT_SYMBOL(SUPR0PageAllocEx);
+
+
+/**
+ * Maps a chunk of memory previously allocated by SUPR0PageAllocEx into kernel
+ * space.
+ *
+ * @returns IPRT status code.
+ * @param pSession The session to associated the allocation with.
+ * @param pvR3 The ring-3 address returned by SUPR0PageAllocEx.
+ * @param offSub Where to start mapping. Must be page aligned.
+ * @param cbSub How much to map. Must be page aligned.
+ * @param fFlags Flags, MBZ.
+ * @param ppvR0 Where to return the address of the ring-0 mapping on
+ * success.
+ */
+SUPR0DECL(int) SUPR0PageMapKernel(PSUPDRVSESSION pSession, RTR3PTR pvR3, uint32_t offSub, uint32_t cbSub,
+ uint32_t fFlags, PRTR0PTR ppvR0)
+{
+ int rc;
+ PSUPDRVBUNDLE pBundle;
+ RTR0MEMOBJ hMemObj = NIL_RTR0MEMOBJ;
+ LogFlow(("SUPR0PageMapKernel: pSession=%p pvR3=%p offSub=%#x cbSub=%#x\n", pSession, pvR3, offSub, cbSub));
+
+ /*
+ * Validate input. The allowed allocation size must be at least equal to the maximum guest VRAM size.
+ */
+ AssertReturn(SUP_IS_SESSION_VALID(pSession), VERR_INVALID_PARAMETER);
+ AssertPtrNullReturn(ppvR0, VERR_INVALID_POINTER);
+ AssertReturn(!fFlags, VERR_INVALID_PARAMETER);
+ AssertReturn(!(offSub & PAGE_OFFSET_MASK), VERR_INVALID_PARAMETER);
+ AssertReturn(!(cbSub & PAGE_OFFSET_MASK), VERR_INVALID_PARAMETER);
+ AssertReturn(cbSub, VERR_INVALID_PARAMETER);
+
+ /*
+ * Find the memory object.
+ */
+ RTSpinlockAcquire(pSession->Spinlock);
+ for (pBundle = &pSession->Bundle; pBundle; pBundle = pBundle->pNext)
+ {
+ if (pBundle->cUsed > 0)
+ {
+ unsigned i;
+ for (i = 0; i < RT_ELEMENTS(pBundle->aMem); i++)
+ {
+ if ( ( pBundle->aMem[i].eType == MEMREF_TYPE_PAGE
+ && pBundle->aMem[i].MemObj != NIL_RTR0MEMOBJ
+ && pBundle->aMem[i].MapObjR3 != NIL_RTR0MEMOBJ
+ && RTR0MemObjAddressR3(pBundle->aMem[i].MapObjR3) == pvR3)
+ || ( pBundle->aMem[i].eType == MEMREF_TYPE_LOCKED
+ && pBundle->aMem[i].MemObj != NIL_RTR0MEMOBJ
+ && pBundle->aMem[i].MapObjR3 == NIL_RTR0MEMOBJ
+ && RTR0MemObjAddressR3(pBundle->aMem[i].MemObj) == pvR3))
+ {
+ hMemObj = pBundle->aMem[i].MemObj;
+ break;
+ }
+ }
+ }
+ }
+ RTSpinlockRelease(pSession->Spinlock);
+
+ rc = VERR_INVALID_PARAMETER;
+ if (hMemObj != NIL_RTR0MEMOBJ)
+ {
+ /*
+ * Do some further input validations before calling IPRT.
+ * (Cleanup is done indirectly by telling RTR0MemObjFree to include mappings.)
+ */
+ size_t cbMemObj = RTR0MemObjSize(hMemObj);
+ if ( offSub < cbMemObj
+ && cbSub <= cbMemObj
+ && offSub + cbSub <= cbMemObj)
+ {
+ RTR0MEMOBJ hMapObj;
+ rc = RTR0MemObjMapKernelEx(&hMapObj, hMemObj, (void *)-1, 0,
+ RTMEM_PROT_READ | RTMEM_PROT_WRITE, offSub, cbSub);
+ if (RT_SUCCESS(rc))
+ *ppvR0 = RTR0MemObjAddress(hMapObj);
+ }
+ else
+ SUPR0Printf("SUPR0PageMapKernel: cbMemObj=%#x offSub=%#x cbSub=%#x\n", cbMemObj, offSub, cbSub);
+
+ }
+ return rc;
+}
+SUPR0_EXPORT_SYMBOL(SUPR0PageMapKernel);
+
+
+/**
+ * Changes the page level protection of one or more pages previously allocated
+ * by SUPR0PageAllocEx.
+ *
+ * @returns IPRT status code.
+ * @param pSession The session to associated the allocation with.
+ * @param pvR3 The ring-3 address returned by SUPR0PageAllocEx.
+ * NIL_RTR3PTR if the ring-3 mapping should be unaffected.
+ * @param pvR0 The ring-0 address returned by SUPR0PageAllocEx.
+ * NIL_RTR0PTR if the ring-0 mapping should be unaffected.
+ * @param offSub Where to start changing. Must be page aligned.
+ * @param cbSub How much to change. Must be page aligned.
+ * @param fProt The new page level protection, see RTMEM_PROT_*.
+ */
+SUPR0DECL(int) SUPR0PageProtect(PSUPDRVSESSION pSession, RTR3PTR pvR3, RTR0PTR pvR0, uint32_t offSub, uint32_t cbSub, uint32_t fProt)
+{
+ int rc;
+ PSUPDRVBUNDLE pBundle;
+ RTR0MEMOBJ hMemObjR0 = NIL_RTR0MEMOBJ;
+ RTR0MEMOBJ hMemObjR3 = NIL_RTR0MEMOBJ;
+ LogFlow(("SUPR0PageProtect: pSession=%p pvR3=%p pvR0=%p offSub=%#x cbSub=%#x fProt-%#x\n", pSession, pvR3, pvR0, offSub, cbSub, fProt));
+
+ /*
+ * Validate input. The allowed allocation size must be at least equal to the maximum guest VRAM size.
+ */
+ AssertReturn(SUP_IS_SESSION_VALID(pSession), VERR_INVALID_PARAMETER);
+ AssertReturn(!(fProt & ~(RTMEM_PROT_READ | RTMEM_PROT_WRITE | RTMEM_PROT_EXEC | RTMEM_PROT_NONE)), VERR_INVALID_PARAMETER);
+ AssertReturn(!(offSub & PAGE_OFFSET_MASK), VERR_INVALID_PARAMETER);
+ AssertReturn(!(cbSub & PAGE_OFFSET_MASK), VERR_INVALID_PARAMETER);
+ AssertReturn(cbSub, VERR_INVALID_PARAMETER);
+
+ /*
+ * Find the memory object.
+ */
+ RTSpinlockAcquire(pSession->Spinlock);
+ for (pBundle = &pSession->Bundle; pBundle; pBundle = pBundle->pNext)
+ {
+ if (pBundle->cUsed > 0)
+ {
+ unsigned i;
+ for (i = 0; i < RT_ELEMENTS(pBundle->aMem); i++)
+ {
+ if ( pBundle->aMem[i].eType == MEMREF_TYPE_PAGE
+ && pBundle->aMem[i].MemObj != NIL_RTR0MEMOBJ
+ && ( pBundle->aMem[i].MapObjR3 != NIL_RTR0MEMOBJ
+ || pvR3 == NIL_RTR3PTR)
+ && ( pvR0 == NIL_RTR0PTR
+ || RTR0MemObjAddress(pBundle->aMem[i].MemObj) == pvR0)
+ && ( pvR3 == NIL_RTR3PTR
+ || RTR0MemObjAddressR3(pBundle->aMem[i].MapObjR3) == pvR3))
+ {
+ if (pvR0 != NIL_RTR0PTR)
+ hMemObjR0 = pBundle->aMem[i].MemObj;
+ if (pvR3 != NIL_RTR3PTR)
+ hMemObjR3 = pBundle->aMem[i].MapObjR3;
+ break;
+ }
+ }
+ }
+ }
+ RTSpinlockRelease(pSession->Spinlock);
+
+ rc = VERR_INVALID_PARAMETER;
+ if ( hMemObjR0 != NIL_RTR0MEMOBJ
+ || hMemObjR3 != NIL_RTR0MEMOBJ)
+ {
+ /*
+ * Do some further input validations before calling IPRT.
+ */
+ size_t cbMemObj = hMemObjR0 != NIL_RTR0PTR ? RTR0MemObjSize(hMemObjR0) : RTR0MemObjSize(hMemObjR3);
+ if ( offSub < cbMemObj
+ && cbSub <= cbMemObj
+ && offSub + cbSub <= cbMemObj)
+ {
+ rc = VINF_SUCCESS;
+ if (hMemObjR3 != NIL_RTR0PTR)
+ rc = RTR0MemObjProtect(hMemObjR3, offSub, cbSub, fProt);
+ if (hMemObjR0 != NIL_RTR0PTR && RT_SUCCESS(rc))
+ rc = RTR0MemObjProtect(hMemObjR0, offSub, cbSub, fProt);
+ }
+ else
+ SUPR0Printf("SUPR0PageMapKernel: cbMemObj=%#x offSub=%#x cbSub=%#x\n", cbMemObj, offSub, cbSub);
+
+ }
+ return rc;
+
+}
+SUPR0_EXPORT_SYMBOL(SUPR0PageProtect);
+
+
+/**
+ * Free memory allocated by SUPR0PageAlloc() and SUPR0PageAllocEx().
+ *
+ * @returns IPRT status code.
+ * @param pSession The session owning the allocation.
+ * @param pvR3 The Ring-3 address returned by SUPR0PageAlloc() or
+ * SUPR0PageAllocEx().
+ */
+SUPR0DECL(int) SUPR0PageFree(PSUPDRVSESSION pSession, RTR3PTR pvR3)
+{
+ LogFlow(("SUPR0PageFree: pSession=%p pvR3=%p\n", pSession, (void *)pvR3));
+ AssertReturn(SUP_IS_SESSION_VALID(pSession), VERR_INVALID_PARAMETER);
+ return supdrvMemRelease(pSession, (RTHCUINTPTR)pvR3, MEMREF_TYPE_PAGE);
+}
+SUPR0_EXPORT_SYMBOL(SUPR0PageFree);
+
+
+/**
+ * Reports a bad context, currenctly that means EFLAGS.AC is 0 instead of 1.
+ *
+ * @param pDevExt The device extension.
+ * @param pszFile The source file where the caller detected the bad
+ * context.
+ * @param uLine The line number in @a pszFile.
+ * @param pszExtra Optional additional message to give further hints.
+ */
+void VBOXCALL supdrvBadContext(PSUPDRVDEVEXT pDevExt, const char *pszFile, uint32_t uLine, const char *pszExtra)
+{
+ uint32_t cCalls;
+
+ /*
+ * Shorten the filename before displaying the message.
+ */
+ for (;;)
+ {
+ const char *pszTmp = strchr(pszFile, '/');
+ if (!pszTmp)
+ pszTmp = strchr(pszFile, '\\');
+ if (!pszTmp)
+ break;
+ pszFile = pszTmp + 1;
+ }
+ if (RT_VALID_PTR(pszExtra) && *pszExtra)
+ SUPR0Printf("vboxdrv: Bad CPU context error at line %u in %s: %s\n", uLine, pszFile, pszExtra);
+ else
+ SUPR0Printf("vboxdrv: Bad CPU context error at line %u in %s!\n", uLine, pszFile);
+
+ /*
+ * Record the incident so that we stand a chance of blocking I/O controls
+ * before panicing the system.
+ */
+ cCalls = ASMAtomicIncU32(&pDevExt->cBadContextCalls);
+ if (cCalls > UINT32_MAX - _1K)
+ ASMAtomicWriteU32(&pDevExt->cBadContextCalls, UINT32_MAX - _1K);
+}
+
+
+/**
+ * Reports a bad context, currenctly that means EFLAGS.AC is 0 instead of 1.
+ *
+ * @param pSession The session of the caller.
+ * @param pszFile The source file where the caller detected the bad
+ * context.
+ * @param uLine The line number in @a pszFile.
+ * @param pszExtra Optional additional message to give further hints.
+ */
+SUPR0DECL(void) SUPR0BadContext(PSUPDRVSESSION pSession, const char *pszFile, uint32_t uLine, const char *pszExtra)
+{
+ PSUPDRVDEVEXT pDevExt;
+
+ AssertReturnVoid(SUP_IS_SESSION_VALID(pSession));
+ pDevExt = pSession->pDevExt;
+
+ supdrvBadContext(pDevExt, pszFile, uLine, pszExtra);
+}
+SUPR0_EXPORT_SYMBOL(SUPR0BadContext);
+
+
+/**
+ * Gets the paging mode of the current CPU.
+ *
+ * @returns Paging mode, SUPPAGEINGMODE_INVALID on error.
+ */
+SUPR0DECL(SUPPAGINGMODE) SUPR0GetPagingMode(void)
+{
+ SUPPAGINGMODE enmMode;
+
+ RTR0UINTREG cr0 = ASMGetCR0();
+ if ((cr0 & (X86_CR0_PG | X86_CR0_PE)) != (X86_CR0_PG | X86_CR0_PE))
+ enmMode = SUPPAGINGMODE_INVALID;
+ else
+ {
+ RTR0UINTREG cr4 = ASMGetCR4();
+ uint32_t fNXEPlusLMA = 0;
+ if (cr4 & X86_CR4_PAE)
+ {
+ uint32_t fExtFeatures = ASMCpuId_EDX(0x80000001);
+ if (fExtFeatures & (X86_CPUID_EXT_FEATURE_EDX_NX | X86_CPUID_EXT_FEATURE_EDX_LONG_MODE))
+ {
+ uint64_t efer = ASMRdMsr(MSR_K6_EFER);
+ if ((fExtFeatures & X86_CPUID_EXT_FEATURE_EDX_NX) && (efer & MSR_K6_EFER_NXE))
+ fNXEPlusLMA |= RT_BIT(0);
+ if ((fExtFeatures & X86_CPUID_EXT_FEATURE_EDX_LONG_MODE) && (efer & MSR_K6_EFER_LMA))
+ fNXEPlusLMA |= RT_BIT(1);
+ }
+ }
+
+ switch ((cr4 & (X86_CR4_PAE | X86_CR4_PGE)) | fNXEPlusLMA)
+ {
+ case 0:
+ enmMode = SUPPAGINGMODE_32_BIT;
+ break;
+
+ case X86_CR4_PGE:
+ enmMode = SUPPAGINGMODE_32_BIT_GLOBAL;
+ break;
+
+ case X86_CR4_PAE:
+ enmMode = SUPPAGINGMODE_PAE;
+ break;
+
+ case X86_CR4_PAE | RT_BIT(0):
+ enmMode = SUPPAGINGMODE_PAE_NX;
+ break;
+
+ case X86_CR4_PAE | X86_CR4_PGE:
+ enmMode = SUPPAGINGMODE_PAE_GLOBAL;
+ break;
+
+ case X86_CR4_PAE | X86_CR4_PGE | RT_BIT(0):
+ enmMode = SUPPAGINGMODE_PAE_GLOBAL;
+ break;
+
+ case RT_BIT(1) | X86_CR4_PAE:
+ enmMode = SUPPAGINGMODE_AMD64;
+ break;
+
+ case RT_BIT(1) | X86_CR4_PAE | RT_BIT(0):
+ enmMode = SUPPAGINGMODE_AMD64_NX;
+ break;
+
+ case RT_BIT(1) | X86_CR4_PAE | X86_CR4_PGE:
+ enmMode = SUPPAGINGMODE_AMD64_GLOBAL;
+ break;
+
+ case RT_BIT(1) | X86_CR4_PAE | X86_CR4_PGE | RT_BIT(0):
+ enmMode = SUPPAGINGMODE_AMD64_GLOBAL_NX;
+ break;
+
+ default:
+ AssertMsgFailed(("Cannot happen! cr4=%#x fNXEPlusLMA=%d\n", cr4, fNXEPlusLMA));
+ enmMode = SUPPAGINGMODE_INVALID;
+ break;
+ }
+ }
+ return enmMode;
+}
+SUPR0_EXPORT_SYMBOL(SUPR0GetPagingMode);
+
+
+/**
+ * Change CR4 and take care of the kernel CR4 shadow if applicable.
+ *
+ * CR4 shadow handling is required for Linux >= 4.0. Calling this function
+ * instead of ASMSetCR4() is only necessary for semi-permanent CR4 changes
+ * for code with interrupts enabled.
+ *
+ * @returns the old CR4 value.
+ *
+ * @param fOrMask bits to be set in CR4.
+ * @param fAndMask bits to be cleard in CR4.
+ *
+ * @remarks Must be called with preemption/interrupts disabled.
+ */
+SUPR0DECL(RTCCUINTREG) SUPR0ChangeCR4(RTCCUINTREG fOrMask, RTCCUINTREG fAndMask)
+{
+#ifdef RT_OS_LINUX
+ return supdrvOSChangeCR4(fOrMask, fAndMask);
+#else
+ RTCCUINTREG uOld = ASMGetCR4();
+ RTCCUINTREG uNew = (uOld & fAndMask) | fOrMask;
+ if (uNew != uOld)
+ ASMSetCR4(uNew);
+ return uOld;
+#endif
+}
+SUPR0_EXPORT_SYMBOL(SUPR0ChangeCR4);
+
+
+/**
+ * Enables or disabled hardware virtualization extensions using native OS APIs.
+ *
+ * @returns VBox status code.
+ * @retval VINF_SUCCESS on success.
+ * @retval VERR_NOT_SUPPORTED if not supported by the native OS.
+ *
+ * @param fEnable Whether to enable or disable.
+ */
+SUPR0DECL(int) SUPR0EnableVTx(bool fEnable)
+{
+#ifdef RT_OS_DARWIN
+ return supdrvOSEnableVTx(fEnable);
+#else
+ RT_NOREF1(fEnable);
+ return VERR_NOT_SUPPORTED;
+#endif
+}
+SUPR0_EXPORT_SYMBOL(SUPR0EnableVTx);
+
+
+/**
+ * Suspends hardware virtualization extensions using the native OS API.
+ *
+ * This is called prior to entering raw-mode context.
+ *
+ * @returns @c true if suspended, @c false if not.
+ */
+SUPR0DECL(bool) SUPR0SuspendVTxOnCpu(void)
+{
+#ifdef RT_OS_DARWIN
+ return supdrvOSSuspendVTxOnCpu();
+#else
+ return false;
+#endif
+}
+SUPR0_EXPORT_SYMBOL(SUPR0SuspendVTxOnCpu);
+
+
+/**
+ * Resumes hardware virtualization extensions using the native OS API.
+ *
+ * This is called after to entering raw-mode context.
+ *
+ * @param fSuspended The return value of SUPR0SuspendVTxOnCpu.
+ */
+SUPR0DECL(void) SUPR0ResumeVTxOnCpu(bool fSuspended)
+{
+#ifdef RT_OS_DARWIN
+ supdrvOSResumeVTxOnCpu(fSuspended);
+#else
+ RT_NOREF1(fSuspended);
+ Assert(!fSuspended);
+#endif
+}
+SUPR0_EXPORT_SYMBOL(SUPR0ResumeVTxOnCpu);
+
+
+SUPR0DECL(int) SUPR0GetCurrentGdtRw(RTHCUINTPTR *pGdtRw)
+{
+#ifdef RT_OS_LINUX
+ return supdrvOSGetCurrentGdtRw(pGdtRw);
+#else
+ NOREF(pGdtRw);
+ return VERR_NOT_IMPLEMENTED;
+#endif
+}
+SUPR0_EXPORT_SYMBOL(SUPR0GetCurrentGdtRw);
+
+
+/**
+ * Gets AMD-V and VT-x support for the calling CPU.
+ *
+ * @returns VBox status code.
+ * @param pfCaps Where to store whether VT-x (SUPVTCAPS_VT_X) or AMD-V
+ * (SUPVTCAPS_AMD_V) is supported.
+ */
+SUPR0DECL(int) SUPR0GetVTSupport(uint32_t *pfCaps)
+{
+ Assert(pfCaps);
+ *pfCaps = 0;
+
+ /* Check if the CPU even supports CPUID (extremely ancient CPUs). */
+ if (ASMHasCpuId())
+ {
+ /* Check the range of standard CPUID leafs. */
+ uint32_t uMaxLeaf, uVendorEbx, uVendorEcx, uVendorEdx;
+ ASMCpuId(0, &uMaxLeaf, &uVendorEbx, &uVendorEcx, &uVendorEdx);
+ if (RTX86IsValidStdRange(uMaxLeaf))
+ {
+ /* Query the standard CPUID leaf. */
+ uint32_t fFeatEcx, fFeatEdx, uDummy;
+ ASMCpuId(1, &uDummy, &uDummy, &fFeatEcx, &fFeatEdx);
+
+ /* Check if the vendor is Intel (or compatible). */
+ if ( RTX86IsIntelCpu(uVendorEbx, uVendorEcx, uVendorEdx)
+ || RTX86IsViaCentaurCpu(uVendorEbx, uVendorEcx, uVendorEdx)
+ || RTX86IsShanghaiCpu(uVendorEbx, uVendorEcx, uVendorEdx))
+ {
+ /* Check VT-x support. In addition, VirtualBox requires MSR and FXSAVE/FXRSTOR to function. */
+ if ( (fFeatEcx & X86_CPUID_FEATURE_ECX_VMX)
+ && (fFeatEdx & X86_CPUID_FEATURE_EDX_MSR)
+ && (fFeatEdx & X86_CPUID_FEATURE_EDX_FXSR))
+ {
+ *pfCaps = SUPVTCAPS_VT_X;
+ return VINF_SUCCESS;
+ }
+ return VERR_VMX_NO_VMX;
+ }
+
+ /* Check if the vendor is AMD (or compatible). */
+ if ( RTX86IsAmdCpu(uVendorEbx, uVendorEcx, uVendorEdx)
+ || RTX86IsHygonCpu(uVendorEbx, uVendorEcx, uVendorEdx))
+ {
+ uint32_t fExtFeatEcx, uExtMaxId;
+ ASMCpuId(0x80000000, &uExtMaxId, &uDummy, &uDummy, &uDummy);
+ ASMCpuId(0x80000001, &uDummy, &uDummy, &fExtFeatEcx, &uDummy);
+
+ /* Check AMD-V support. In addition, VirtualBox requires MSR and FXSAVE/FXRSTOR to function. */
+ if ( RTX86IsValidExtRange(uExtMaxId)
+ && uExtMaxId >= 0x8000000a
+ && (fExtFeatEcx & X86_CPUID_AMD_FEATURE_ECX_SVM)
+ && (fFeatEdx & X86_CPUID_FEATURE_EDX_MSR)
+ && (fFeatEdx & X86_CPUID_FEATURE_EDX_FXSR))
+ {
+ *pfCaps = SUPVTCAPS_AMD_V;
+ return VINF_SUCCESS;
+ }
+ return VERR_SVM_NO_SVM;
+ }
+ }
+ }
+ return VERR_UNSUPPORTED_CPU;
+}
+SUPR0_EXPORT_SYMBOL(SUPR0GetVTSupport);
+
+
+/**
+ * Checks if Intel VT-x feature is usable on this CPU.
+ *
+ * @returns VBox status code.
+ * @param pfIsSmxModeAmbiguous Where to return whether the SMX mode causes
+ * ambiguity that makes us unsure whether we
+ * really can use VT-x or not.
+ *
+ * @remarks Must be called with preemption disabled.
+ * The caller is also expected to check that the CPU is an Intel (or
+ * VIA/Shanghai) CPU -and- that it supports VT-x. Otherwise, this
+ * function might throw a \#GP fault as it tries to read/write MSRs
+ * that may not be present!
+ */
+SUPR0DECL(int) SUPR0GetVmxUsability(bool *pfIsSmxModeAmbiguous)
+{
+ uint64_t fFeatMsr;
+ bool fMaybeSmxMode;
+ bool fMsrLocked;
+ bool fSmxVmxAllowed;
+ bool fVmxAllowed;
+ bool fIsSmxModeAmbiguous;
+ int rc;
+
+ Assert(!RTThreadPreemptIsEnabled(NIL_RTTHREAD));
+
+ fFeatMsr = ASMRdMsr(MSR_IA32_FEATURE_CONTROL);
+ fMaybeSmxMode = RT_BOOL(ASMGetCR4() & X86_CR4_SMXE);
+ fMsrLocked = RT_BOOL(fFeatMsr & MSR_IA32_FEATURE_CONTROL_LOCK);
+ fSmxVmxAllowed = RT_BOOL(fFeatMsr & MSR_IA32_FEATURE_CONTROL_SMX_VMXON);
+ fVmxAllowed = RT_BOOL(fFeatMsr & MSR_IA32_FEATURE_CONTROL_VMXON);
+ fIsSmxModeAmbiguous = false;
+ rc = VERR_INTERNAL_ERROR_5;
+
+ /* Check if the LOCK bit is set but excludes the required VMXON bit. */
+ if (fMsrLocked)
+ {
+ if (fVmxAllowed && fSmxVmxAllowed)
+ rc = VINF_SUCCESS;
+ else if (!fVmxAllowed && !fSmxVmxAllowed)
+ rc = VERR_VMX_MSR_ALL_VMX_DISABLED;
+ else if (!fMaybeSmxMode)
+ {
+ if (fVmxAllowed)
+ rc = VINF_SUCCESS;
+ else
+ rc = VERR_VMX_MSR_VMX_DISABLED;
+ }
+ else
+ {
+ /*
+ * CR4.SMXE is set but this doesn't mean the CPU is necessarily in SMX mode. We shall assume
+ * that it is -not- and that it is a stupid BIOS/OS setting CR4.SMXE for no good reason.
+ * See @bugref{6873}.
+ */
+ Assert(fMaybeSmxMode == true);
+ fIsSmxModeAmbiguous = true;
+ rc = VINF_SUCCESS;
+ }
+ }
+ else
+ {
+ /*
+ * MSR is not yet locked; we can change it ourselves here. Once the lock bit is set,
+ * this MSR can no longer be modified.
+ *
+ * Set both the VMX and SMX_VMX bits (if supported) as we can't determine SMX mode
+ * accurately. See @bugref{6873}.
+ *
+ * We need to check for SMX hardware support here, before writing the MSR as
+ * otherwise we will #GP fault on CPUs that do not support it. Callers do not check
+ * for it.
+ */
+ uint32_t fFeaturesECX, uDummy;
+#ifdef VBOX_STRICT
+ /* Callers should have verified these at some point. */
+ uint32_t uMaxId, uVendorEBX, uVendorECX, uVendorEDX;
+ ASMCpuId(0, &uMaxId, &uVendorEBX, &uVendorECX, &uVendorEDX);
+ Assert(RTX86IsValidStdRange(uMaxId));
+ Assert( RTX86IsIntelCpu( uVendorEBX, uVendorECX, uVendorEDX)
+ || RTX86IsViaCentaurCpu(uVendorEBX, uVendorECX, uVendorEDX)
+ || RTX86IsShanghaiCpu( uVendorEBX, uVendorECX, uVendorEDX));
+#endif
+ ASMCpuId(1, &uDummy, &uDummy, &fFeaturesECX, &uDummy);
+ bool fSmxVmxHwSupport = false;
+ if ( (fFeaturesECX & X86_CPUID_FEATURE_ECX_VMX)
+ && (fFeaturesECX & X86_CPUID_FEATURE_ECX_SMX))
+ fSmxVmxHwSupport = true;
+
+ fFeatMsr |= MSR_IA32_FEATURE_CONTROL_LOCK
+ | MSR_IA32_FEATURE_CONTROL_VMXON;
+ if (fSmxVmxHwSupport)
+ fFeatMsr |= MSR_IA32_FEATURE_CONTROL_SMX_VMXON;
+
+ /*
+ * Commit.
+ */
+ ASMWrMsr(MSR_IA32_FEATURE_CONTROL, fFeatMsr);
+
+ /*
+ * Verify.
+ */
+ fFeatMsr = ASMRdMsr(MSR_IA32_FEATURE_CONTROL);
+ fMsrLocked = RT_BOOL(fFeatMsr & MSR_IA32_FEATURE_CONTROL_LOCK);
+ if (fMsrLocked)
+ {
+ fSmxVmxAllowed = RT_BOOL(fFeatMsr & MSR_IA32_FEATURE_CONTROL_SMX_VMXON);
+ fVmxAllowed = RT_BOOL(fFeatMsr & MSR_IA32_FEATURE_CONTROL_VMXON);
+ if ( fVmxAllowed
+ && ( !fSmxVmxHwSupport
+ || fSmxVmxAllowed))
+ rc = VINF_SUCCESS;
+ else
+ rc = !fSmxVmxHwSupport ? VERR_VMX_MSR_VMX_ENABLE_FAILED : VERR_VMX_MSR_SMX_VMX_ENABLE_FAILED;
+ }
+ else
+ rc = VERR_VMX_MSR_LOCKING_FAILED;
+ }
+
+ if (pfIsSmxModeAmbiguous)
+ *pfIsSmxModeAmbiguous = fIsSmxModeAmbiguous;
+
+ return rc;
+}
+SUPR0_EXPORT_SYMBOL(SUPR0GetVmxUsability);
+
+
+/**
+ * Checks if AMD-V SVM feature is usable on this CPU.
+ *
+ * @returns VBox status code.
+ * @param fInitSvm If usable, try to initialize SVM on this CPU.
+ *
+ * @remarks Must be called with preemption disabled.
+ */
+SUPR0DECL(int) SUPR0GetSvmUsability(bool fInitSvm)
+{
+ int rc;
+ uint64_t fVmCr;
+ uint64_t fEfer;
+
+ Assert(!RTThreadPreemptIsEnabled(NIL_RTTHREAD));
+ fVmCr = ASMRdMsr(MSR_K8_VM_CR);
+ if (!(fVmCr & MSR_K8_VM_CR_SVM_DISABLE))
+ {
+ rc = VINF_SUCCESS;
+ if (fInitSvm)
+ {
+ /* Turn on SVM in the EFER MSR. */
+ fEfer = ASMRdMsr(MSR_K6_EFER);
+ if (fEfer & MSR_K6_EFER_SVME)
+ rc = VERR_SVM_IN_USE;
+ else
+ {
+ ASMWrMsr(MSR_K6_EFER, fEfer | MSR_K6_EFER_SVME);
+
+ /* Paranoia. */
+ fEfer = ASMRdMsr(MSR_K6_EFER);
+ if (fEfer & MSR_K6_EFER_SVME)
+ {
+ /* Restore previous value. */
+ ASMWrMsr(MSR_K6_EFER, fEfer & ~MSR_K6_EFER_SVME);
+ }
+ else
+ rc = VERR_SVM_ILLEGAL_EFER_MSR;
+ }
+ }
+ }
+ else
+ rc = VERR_SVM_DISABLED;
+ return rc;
+}
+SUPR0_EXPORT_SYMBOL(SUPR0GetSvmUsability);
+
+
+/**
+ * Queries the AMD-V and VT-x capabilities of the calling CPU.
+ *
+ * @returns VBox status code.
+ * @retval VERR_VMX_NO_VMX
+ * @retval VERR_VMX_MSR_ALL_VMX_DISABLED
+ * @retval VERR_VMX_MSR_VMX_DISABLED
+ * @retval VERR_VMX_MSR_LOCKING_FAILED
+ * @retval VERR_VMX_MSR_VMX_ENABLE_FAILED
+ * @retval VERR_VMX_MSR_SMX_VMX_ENABLE_FAILED
+ * @retval VERR_SVM_NO_SVM
+ * @retval VERR_SVM_DISABLED
+ * @retval VERR_UNSUPPORTED_CPU if not identifiable as an AMD, Intel or VIA
+ * (centaur)/Shanghai CPU.
+ *
+ * @param pfCaps Where to store the capabilities.
+ */
+int VBOXCALL supdrvQueryVTCapsInternal(uint32_t *pfCaps)
+{
+ int rc = VERR_UNSUPPORTED_CPU;
+ bool fIsSmxModeAmbiguous = false;
+ RTTHREADPREEMPTSTATE PreemptState = RTTHREADPREEMPTSTATE_INITIALIZER;
+
+ /*
+ * Input validation.
+ */
+ AssertPtrReturn(pfCaps, VERR_INVALID_POINTER);
+ *pfCaps = 0;
+
+ /* We may modify MSRs and re-read them, disable preemption so we make sure we don't migrate CPUs. */
+ RTThreadPreemptDisable(&PreemptState);
+
+ /* Check if VT-x/AMD-V is supported. */
+ rc = SUPR0GetVTSupport(pfCaps);
+ if (RT_SUCCESS(rc))
+ {
+ /* Check if VT-x is supported. */
+ if (*pfCaps & SUPVTCAPS_VT_X)
+ {
+ /* Check if VT-x is usable. */
+ rc = SUPR0GetVmxUsability(&fIsSmxModeAmbiguous);
+ if (RT_SUCCESS(rc))
+ {
+ /* Query some basic VT-x capabilities (mainly required by our GUI). */
+ VMXCTLSMSR vtCaps;
+ vtCaps.u = ASMRdMsr(MSR_IA32_VMX_PROCBASED_CTLS);
+ if (vtCaps.n.allowed1 & VMX_PROC_CTLS_USE_SECONDARY_CTLS)
+ {
+ vtCaps.u = ASMRdMsr(MSR_IA32_VMX_PROCBASED_CTLS2);
+ if (vtCaps.n.allowed1 & VMX_PROC_CTLS2_EPT)
+ *pfCaps |= SUPVTCAPS_NESTED_PAGING;
+ if (vtCaps.n.allowed1 & VMX_PROC_CTLS2_UNRESTRICTED_GUEST)
+ *pfCaps |= SUPVTCAPS_VTX_UNRESTRICTED_GUEST;
+ if (vtCaps.n.allowed1 & VMX_PROC_CTLS2_VMCS_SHADOWING)
+ *pfCaps |= SUPVTCAPS_VTX_VMCS_SHADOWING;
+ }
+ }
+ }
+ /* Check if AMD-V is supported. */
+ else if (*pfCaps & SUPVTCAPS_AMD_V)
+ {
+ /* Check is SVM is usable. */
+ rc = SUPR0GetSvmUsability(false /* fInitSvm */);
+ if (RT_SUCCESS(rc))
+ {
+ /* Query some basic AMD-V capabilities (mainly required by our GUI). */
+ uint32_t uDummy, fSvmFeatures;
+ ASMCpuId(0x8000000a, &uDummy, &uDummy, &uDummy, &fSvmFeatures);
+ if (fSvmFeatures & X86_CPUID_SVM_FEATURE_EDX_NESTED_PAGING)
+ *pfCaps |= SUPVTCAPS_NESTED_PAGING;
+ if (fSvmFeatures & X86_CPUID_SVM_FEATURE_EDX_VIRT_VMSAVE_VMLOAD)
+ *pfCaps |= SUPVTCAPS_AMDV_VIRT_VMSAVE_VMLOAD;
+ }
+ }
+ }
+
+ /* Restore preemption. */
+ RTThreadPreemptRestore(&PreemptState);
+
+ /* After restoring preemption, if we may be in SMX mode, print a warning as it's difficult to debug such problems. */
+ if (fIsSmxModeAmbiguous)
+ SUPR0Printf(("WARNING! CR4 hints SMX mode but your CPU is too secretive. Proceeding anyway... We wish you good luck!\n"));
+
+ return rc;
+}
+
+
+/**
+ * Queries the AMD-V and VT-x capabilities of the calling CPU.
+ *
+ * @returns VBox status code.
+ * @retval VERR_VMX_NO_VMX
+ * @retval VERR_VMX_MSR_ALL_VMX_DISABLED
+ * @retval VERR_VMX_MSR_VMX_DISABLED
+ * @retval VERR_VMX_MSR_LOCKING_FAILED
+ * @retval VERR_VMX_MSR_VMX_ENABLE_FAILED
+ * @retval VERR_VMX_MSR_SMX_VMX_ENABLE_FAILED
+ * @retval VERR_SVM_NO_SVM
+ * @retval VERR_SVM_DISABLED
+ * @retval VERR_UNSUPPORTED_CPU if not identifiable as an AMD, Intel or VIA
+ * (centaur)/Shanghai CPU.
+ *
+ * @param pSession The session handle.
+ * @param pfCaps Where to store the capabilities.
+ */
+SUPR0DECL(int) SUPR0QueryVTCaps(PSUPDRVSESSION pSession, uint32_t *pfCaps)
+{
+ /*
+ * Input validation.
+ */
+ AssertReturn(SUP_IS_SESSION_VALID(pSession), VERR_INVALID_PARAMETER);
+ AssertPtrReturn(pfCaps, VERR_INVALID_POINTER);
+
+ /*
+ * Call common worker.
+ */
+ return supdrvQueryVTCapsInternal(pfCaps);
+}
+SUPR0_EXPORT_SYMBOL(SUPR0QueryVTCaps);
+
+
+/**
+ * Queries the CPU microcode revision.
+ *
+ * @returns VBox status code.
+ * @retval VERR_UNSUPPORTED_CPU if not identifiable as a processor with
+ * readable microcode rev.
+ *
+ * @param puRevision Where to store the microcode revision.
+ */
+static int VBOXCALL supdrvQueryUcodeRev(uint32_t *puRevision)
+{
+ int rc = VERR_UNSUPPORTED_CPU;
+ RTTHREADPREEMPTSTATE PreemptState = RTTHREADPREEMPTSTATE_INITIALIZER;
+
+ /*
+ * Input validation.
+ */
+ AssertPtrReturn(puRevision, VERR_INVALID_POINTER);
+
+ *puRevision = 0;
+
+ /* Disable preemption so we make sure we don't migrate CPUs, just in case. */
+ /* NB: We assume that there aren't mismatched microcode revs in the system. */
+ RTThreadPreemptDisable(&PreemptState);
+
+ if (ASMHasCpuId())
+ {
+ uint32_t uDummy, uTFMSEAX;
+ uint32_t uMaxId, uVendorEBX, uVendorECX, uVendorEDX;
+
+ ASMCpuId(0, &uMaxId, &uVendorEBX, &uVendorECX, &uVendorEDX);
+ ASMCpuId(1, &uTFMSEAX, &uDummy, &uDummy, &uDummy);
+
+ if (RTX86IsValidStdRange(uMaxId))
+ {
+ uint64_t uRevMsr;
+ if (RTX86IsIntelCpu(uVendorEBX, uVendorECX, uVendorEDX))
+ {
+ /* Architectural MSR available on Pentium Pro and later. */
+ if (RTX86GetCpuFamily(uTFMSEAX) >= 6)
+ {
+ /* Revision is in the high dword. */
+ uRevMsr = ASMRdMsr(MSR_IA32_BIOS_SIGN_ID);
+ *puRevision = RT_HIDWORD(uRevMsr);
+ rc = VINF_SUCCESS;
+ }
+ }
+ else if ( RTX86IsAmdCpu(uVendorEBX, uVendorECX, uVendorEDX)
+ || RTX86IsHygonCpu(uVendorEBX, uVendorECX, uVendorEDX))
+ {
+ /* Not well documented, but at least all AMD64 CPUs support this. */
+ if (RTX86GetCpuFamily(uTFMSEAX) >= 15)
+ {
+ /* Revision is in the low dword. */
+ uRevMsr = ASMRdMsr(MSR_IA32_BIOS_SIGN_ID); /* Same MSR as Intel. */
+ *puRevision = RT_LODWORD(uRevMsr);
+ rc = VINF_SUCCESS;
+ }
+ }
+ }
+ }
+
+ RTThreadPreemptRestore(&PreemptState);
+
+ return rc;
+}
+
+
+/**
+ * Queries the CPU microcode revision.
+ *
+ * @returns VBox status code.
+ * @retval VERR_UNSUPPORTED_CPU if not identifiable as a processor with
+ * readable microcode rev.
+ *
+ * @param pSession The session handle.
+ * @param puRevision Where to store the microcode revision.
+ */
+SUPR0DECL(int) SUPR0QueryUcodeRev(PSUPDRVSESSION pSession, uint32_t *puRevision)
+{
+ /*
+ * Input validation.
+ */
+ AssertReturn(SUP_IS_SESSION_VALID(pSession), VERR_INVALID_PARAMETER);
+ AssertPtrReturn(puRevision, VERR_INVALID_POINTER);
+
+ /*
+ * Call common worker.
+ */
+ return supdrvQueryUcodeRev(puRevision);
+}
+SUPR0_EXPORT_SYMBOL(SUPR0QueryUcodeRev);
+
+
+/**
+ * Gets hardware-virtualization MSRs of the calling CPU.
+ *
+ * @returns VBox status code.
+ * @param pMsrs Where to store the hardware-virtualization MSRs.
+ * @param fCaps Hardware virtualization capabilities (SUPVTCAPS_XXX). Pass 0
+ * to explicitly check for the presence of VT-x/AMD-V before
+ * querying MSRs.
+ * @param fForce Force querying of MSRs from the hardware.
+ */
+SUPR0DECL(int) SUPR0GetHwvirtMsrs(PSUPHWVIRTMSRS pMsrs, uint32_t fCaps, bool fForce)
+{
+ NOREF(fForce);
+
+ int rc;
+ RTTHREADPREEMPTSTATE PreemptState = RTTHREADPREEMPTSTATE_INITIALIZER;
+
+ /*
+ * Input validation.
+ */
+ AssertPtrReturn(pMsrs, VERR_INVALID_POINTER);
+
+ /*
+ * Disable preemption so we make sure we don't migrate CPUs and because
+ * we access global data.
+ */
+ RTThreadPreemptDisable(&PreemptState);
+
+ /*
+ * Query the MSRs from the hardware.
+ */
+ SUPHWVIRTMSRS Msrs;
+ RT_ZERO(Msrs);
+
+ /* If the caller claims VT-x/AMD-V is supported, don't need to recheck it. */
+ if (!(fCaps & (SUPVTCAPS_VT_X | SUPVTCAPS_AMD_V)))
+ rc = SUPR0GetVTSupport(&fCaps);
+ else
+ rc = VINF_SUCCESS;
+ if (RT_SUCCESS(rc))
+ {
+ if (fCaps & SUPVTCAPS_VT_X)
+ {
+ Msrs.u.vmx.u64FeatCtrl = ASMRdMsr(MSR_IA32_FEATURE_CONTROL);
+ Msrs.u.vmx.u64Basic = ASMRdMsr(MSR_IA32_VMX_BASIC);
+ Msrs.u.vmx.PinCtls.u = ASMRdMsr(MSR_IA32_VMX_PINBASED_CTLS);
+ Msrs.u.vmx.ProcCtls.u = ASMRdMsr(MSR_IA32_VMX_PROCBASED_CTLS);
+ Msrs.u.vmx.ExitCtls.u = ASMRdMsr(MSR_IA32_VMX_EXIT_CTLS);
+ Msrs.u.vmx.EntryCtls.u = ASMRdMsr(MSR_IA32_VMX_ENTRY_CTLS);
+ Msrs.u.vmx.u64Misc = ASMRdMsr(MSR_IA32_VMX_MISC);
+ Msrs.u.vmx.u64Cr0Fixed0 = ASMRdMsr(MSR_IA32_VMX_CR0_FIXED0);
+ Msrs.u.vmx.u64Cr0Fixed1 = ASMRdMsr(MSR_IA32_VMX_CR0_FIXED1);
+ Msrs.u.vmx.u64Cr4Fixed0 = ASMRdMsr(MSR_IA32_VMX_CR4_FIXED0);
+ Msrs.u.vmx.u64Cr4Fixed1 = ASMRdMsr(MSR_IA32_VMX_CR4_FIXED1);
+ Msrs.u.vmx.u64VmcsEnum = ASMRdMsr(MSR_IA32_VMX_VMCS_ENUM);
+
+ if (RT_BF_GET(Msrs.u.vmx.u64Basic, VMX_BF_BASIC_TRUE_CTLS))
+ {
+ Msrs.u.vmx.TruePinCtls.u = ASMRdMsr(MSR_IA32_VMX_TRUE_PINBASED_CTLS);
+ Msrs.u.vmx.TrueProcCtls.u = ASMRdMsr(MSR_IA32_VMX_TRUE_PROCBASED_CTLS);
+ Msrs.u.vmx.TrueEntryCtls.u = ASMRdMsr(MSR_IA32_VMX_TRUE_ENTRY_CTLS);
+ Msrs.u.vmx.TrueExitCtls.u = ASMRdMsr(MSR_IA32_VMX_TRUE_EXIT_CTLS);
+ }
+
+ if (Msrs.u.vmx.ProcCtls.n.allowed1 & VMX_PROC_CTLS_USE_SECONDARY_CTLS)
+ {
+ Msrs.u.vmx.ProcCtls2.u = ASMRdMsr(MSR_IA32_VMX_PROCBASED_CTLS2);
+
+ if (Msrs.u.vmx.ProcCtls2.n.allowed1 & (VMX_PROC_CTLS2_EPT | VMX_PROC_CTLS2_VPID))
+ Msrs.u.vmx.u64EptVpidCaps = ASMRdMsr(MSR_IA32_VMX_EPT_VPID_CAP);
+
+ if (Msrs.u.vmx.ProcCtls2.n.allowed1 & VMX_PROC_CTLS2_VMFUNC)
+ Msrs.u.vmx.u64VmFunc = ASMRdMsr(MSR_IA32_VMX_VMFUNC);
+ }
+
+ if (Msrs.u.vmx.ProcCtls.n.allowed1 & VMX_PROC_CTLS_USE_TERTIARY_CTLS)
+ Msrs.u.vmx.u64ProcCtls3 = ASMRdMsr(MSR_IA32_VMX_PROCBASED_CTLS3);
+
+ if (Msrs.u.vmx.ExitCtls.n.allowed1 & VMX_EXIT_CTLS_USE_SECONDARY_CTLS)
+ Msrs.u.vmx.u64ExitCtls2 = ASMRdMsr(MSR_IA32_VMX_EXIT_CTLS2);
+ }
+ else if (fCaps & SUPVTCAPS_AMD_V)
+ {
+ Msrs.u.svm.u64MsrHwcr = ASMRdMsr(MSR_K8_HWCR);
+ Msrs.u.svm.u64MsrSmmAddr = ASMRdMsr(MSR_K7_SMM_ADDR);
+ Msrs.u.svm.u64MsrSmmMask = ASMRdMsr(MSR_K7_SMM_MASK);
+ }
+ else
+ {
+ RTThreadPreemptRestore(&PreemptState);
+ AssertMsgFailedReturn(("SUPR0GetVTSupport returns success but neither VT-x nor AMD-V reported!\n"),
+ VERR_INTERNAL_ERROR_2);
+ }
+
+ /*
+ * Copy the MSRs out.
+ */
+ memcpy(pMsrs, &Msrs, sizeof(*pMsrs));
+ }
+
+ RTThreadPreemptRestore(&PreemptState);
+
+ return rc;
+}
+SUPR0_EXPORT_SYMBOL(SUPR0GetHwvirtMsrs);
+
+
+/**
+ * Register a component factory with the support driver.
+ *
+ * This is currently restricted to kernel sessions only.
+ *
+ * @returns VBox status code.
+ * @retval VINF_SUCCESS on success.
+ * @retval VERR_NO_MEMORY if we're out of memory.
+ * @retval VERR_ALREADY_EXISTS if the factory has already been registered.
+ * @retval VERR_ACCESS_DENIED if it isn't a kernel session.
+ * @retval VERR_INVALID_PARAMETER on invalid parameter.
+ * @retval VERR_INVALID_POINTER on invalid pointer parameter.
+ *
+ * @param pSession The SUPDRV session (must be a ring-0 session).
+ * @param pFactory Pointer to the component factory registration structure.
+ *
+ * @remarks This interface is also available via SUPR0IdcComponentRegisterFactory.
+ */
+SUPR0DECL(int) SUPR0ComponentRegisterFactory(PSUPDRVSESSION pSession, PCSUPDRVFACTORY pFactory)
+{
+ PSUPDRVFACTORYREG pNewReg;
+ const char *psz;
+ int rc;
+
+ /*
+ * Validate parameters.
+ */
+ AssertReturn(SUP_IS_SESSION_VALID(pSession), VERR_INVALID_PARAMETER);
+ AssertReturn(pSession->R0Process == NIL_RTR0PROCESS, VERR_ACCESS_DENIED);
+ AssertPtrReturn(pFactory, VERR_INVALID_POINTER);
+ AssertPtrReturn(pFactory->pfnQueryFactoryInterface, VERR_INVALID_POINTER);
+ psz = RTStrEnd(pFactory->szName, sizeof(pFactory->szName));
+ AssertReturn(psz, VERR_INVALID_PARAMETER);
+
+ /*
+ * Allocate and initialize a new registration structure.
+ */
+ pNewReg = (PSUPDRVFACTORYREG)RTMemAlloc(sizeof(SUPDRVFACTORYREG));
+ if (pNewReg)
+ {
+ pNewReg->pNext = NULL;
+ pNewReg->pFactory = pFactory;
+ pNewReg->pSession = pSession;
+ pNewReg->cchName = psz - &pFactory->szName[0];
+
+ /*
+ * Add it to the tail of the list after checking for prior registration.
+ */
+ rc = RTSemFastMutexRequest(pSession->pDevExt->mtxComponentFactory);
+ if (RT_SUCCESS(rc))
+ {
+ PSUPDRVFACTORYREG pPrev = NULL;
+ PSUPDRVFACTORYREG pCur = pSession->pDevExt->pComponentFactoryHead;
+ while (pCur && pCur->pFactory != pFactory)
+ {
+ pPrev = pCur;
+ pCur = pCur->pNext;
+ }
+ if (!pCur)
+ {
+ if (pPrev)
+ pPrev->pNext = pNewReg;
+ else
+ pSession->pDevExt->pComponentFactoryHead = pNewReg;
+ rc = VINF_SUCCESS;
+ }
+ else
+ rc = VERR_ALREADY_EXISTS;
+
+ RTSemFastMutexRelease(pSession->pDevExt->mtxComponentFactory);
+ }
+
+ if (RT_FAILURE(rc))
+ RTMemFree(pNewReg);
+ }
+ else
+ rc = VERR_NO_MEMORY;
+ return rc;
+}
+SUPR0_EXPORT_SYMBOL(SUPR0ComponentRegisterFactory);
+
+
+/**
+ * Deregister a component factory.
+ *
+ * @returns VBox status code.
+ * @retval VINF_SUCCESS on success.
+ * @retval VERR_NOT_FOUND if the factory wasn't registered.
+ * @retval VERR_ACCESS_DENIED if it isn't a kernel session.
+ * @retval VERR_INVALID_PARAMETER on invalid parameter.
+ * @retval VERR_INVALID_POINTER on invalid pointer parameter.
+ *
+ * @param pSession The SUPDRV session (must be a ring-0 session).
+ * @param pFactory Pointer to the component factory registration structure
+ * previously passed SUPR0ComponentRegisterFactory().
+ *
+ * @remarks This interface is also available via SUPR0IdcComponentDeregisterFactory.
+ */
+SUPR0DECL(int) SUPR0ComponentDeregisterFactory(PSUPDRVSESSION pSession, PCSUPDRVFACTORY pFactory)
+{
+ int rc;
+
+ /*
+ * Validate parameters.
+ */
+ AssertReturn(SUP_IS_SESSION_VALID(pSession), VERR_INVALID_PARAMETER);
+ AssertReturn(pSession->R0Process == NIL_RTR0PROCESS, VERR_ACCESS_DENIED);
+ AssertPtrReturn(pFactory, VERR_INVALID_POINTER);
+
+ /*
+ * Take the lock and look for the registration record.
+ */
+ rc = RTSemFastMutexRequest(pSession->pDevExt->mtxComponentFactory);
+ if (RT_SUCCESS(rc))
+ {
+ PSUPDRVFACTORYREG pPrev = NULL;
+ PSUPDRVFACTORYREG pCur = pSession->pDevExt->pComponentFactoryHead;
+ while (pCur && pCur->pFactory != pFactory)
+ {
+ pPrev = pCur;
+ pCur = pCur->pNext;
+ }
+ if (pCur)
+ {
+ if (!pPrev)
+ pSession->pDevExt->pComponentFactoryHead = pCur->pNext;
+ else
+ pPrev->pNext = pCur->pNext;
+
+ pCur->pNext = NULL;
+ pCur->pFactory = NULL;
+ pCur->pSession = NULL;
+ rc = VINF_SUCCESS;
+ }
+ else
+ rc = VERR_NOT_FOUND;
+
+ RTSemFastMutexRelease(pSession->pDevExt->mtxComponentFactory);
+
+ RTMemFree(pCur);
+ }
+ return rc;
+}
+SUPR0_EXPORT_SYMBOL(SUPR0ComponentDeregisterFactory);
+
+
+/**
+ * Queries a component factory.
+ *
+ * @returns VBox status code.
+ * @retval VERR_INVALID_PARAMETER on invalid parameter.
+ * @retval VERR_INVALID_POINTER on invalid pointer parameter.
+ * @retval VERR_SUPDRV_COMPONENT_NOT_FOUND if the component factory wasn't found.
+ * @retval VERR_SUPDRV_INTERFACE_NOT_SUPPORTED if the interface wasn't supported.
+ *
+ * @param pSession The SUPDRV session.
+ * @param pszName The name of the component factory.
+ * @param pszInterfaceUuid The UUID of the factory interface (stringified).
+ * @param ppvFactoryIf Where to store the factory interface.
+ */
+SUPR0DECL(int) SUPR0ComponentQueryFactory(PSUPDRVSESSION pSession, const char *pszName, const char *pszInterfaceUuid, void **ppvFactoryIf)
+{
+ const char *pszEnd;
+ size_t cchName;
+ int rc;
+
+ /*
+ * Validate parameters.
+ */
+ AssertReturn(SUP_IS_SESSION_VALID(pSession), VERR_INVALID_PARAMETER);
+
+ AssertPtrReturn(pszName, VERR_INVALID_POINTER);
+ pszEnd = RTStrEnd(pszName, RT_SIZEOFMEMB(SUPDRVFACTORY, szName));
+ AssertReturn(pszEnd, VERR_INVALID_PARAMETER);
+ cchName = pszEnd - pszName;
+
+ AssertPtrReturn(pszInterfaceUuid, VERR_INVALID_POINTER);
+ pszEnd = RTStrEnd(pszInterfaceUuid, RTUUID_STR_LENGTH);
+ AssertReturn(pszEnd, VERR_INVALID_PARAMETER);
+
+ AssertPtrReturn(ppvFactoryIf, VERR_INVALID_POINTER);
+ *ppvFactoryIf = NULL;
+
+ /*
+ * Take the lock and try all factories by this name.
+ */
+ rc = RTSemFastMutexRequest(pSession->pDevExt->mtxComponentFactory);
+ if (RT_SUCCESS(rc))
+ {
+ PSUPDRVFACTORYREG pCur = pSession->pDevExt->pComponentFactoryHead;
+ rc = VERR_SUPDRV_COMPONENT_NOT_FOUND;
+ while (pCur)
+ {
+ if ( pCur->cchName == cchName
+ && !memcmp(pCur->pFactory->szName, pszName, cchName))
+ {
+ void *pvFactory = pCur->pFactory->pfnQueryFactoryInterface(pCur->pFactory, pSession, pszInterfaceUuid);
+ if (pvFactory)
+ {
+ *ppvFactoryIf = pvFactory;
+ rc = VINF_SUCCESS;
+ break;
+ }
+ rc = VERR_SUPDRV_INTERFACE_NOT_SUPPORTED;
+ }
+
+ /* next */
+ pCur = pCur->pNext;
+ }
+
+ RTSemFastMutexRelease(pSession->pDevExt->mtxComponentFactory);
+ }
+ return rc;
+}
+SUPR0_EXPORT_SYMBOL(SUPR0ComponentQueryFactory);
+
+
+/**
+ * Adds a memory object to the session.
+ *
+ * @returns IPRT status code.
+ * @param pMem Memory tracking structure containing the
+ * information to track.
+ * @param pSession The session.
+ */
+static int supdrvMemAdd(PSUPDRVMEMREF pMem, PSUPDRVSESSION pSession)
+{
+ PSUPDRVBUNDLE pBundle;
+
+ /*
+ * Find free entry and record the allocation.
+ */
+ RTSpinlockAcquire(pSession->Spinlock);
+ for (pBundle = &pSession->Bundle; pBundle; pBundle = pBundle->pNext)
+ {
+ if (pBundle->cUsed < RT_ELEMENTS(pBundle->aMem))
+ {
+ unsigned i;
+ for (i = 0; i < RT_ELEMENTS(pBundle->aMem); i++)
+ {
+ if (pBundle->aMem[i].MemObj == NIL_RTR0MEMOBJ)
+ {
+ pBundle->cUsed++;
+ pBundle->aMem[i] = *pMem;
+ RTSpinlockRelease(pSession->Spinlock);
+ return VINF_SUCCESS;
+ }
+ }
+ AssertFailed(); /* !!this can't be happening!!! */
+ }
+ }
+ RTSpinlockRelease(pSession->Spinlock);
+
+ /*
+ * Need to allocate a new bundle.
+ * Insert into the last entry in the bundle.
+ */
+ pBundle = (PSUPDRVBUNDLE)RTMemAllocZ(sizeof(*pBundle));
+ if (!pBundle)
+ return VERR_NO_MEMORY;
+
+ /* take last entry. */
+ pBundle->cUsed++;
+ pBundle->aMem[RT_ELEMENTS(pBundle->aMem) - 1] = *pMem;
+
+ /* insert into list. */
+ RTSpinlockAcquire(pSession->Spinlock);
+ pBundle->pNext = pSession->Bundle.pNext;
+ pSession->Bundle.pNext = pBundle;
+ RTSpinlockRelease(pSession->Spinlock);
+
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * Releases a memory object referenced by pointer and type.
+ *
+ * @returns IPRT status code.
+ * @param pSession Session data.
+ * @param uPtr Pointer to memory. This is matched against both the R0 and R3 addresses.
+ * @param eType Memory type.
+ */
+static int supdrvMemRelease(PSUPDRVSESSION pSession, RTHCUINTPTR uPtr, SUPDRVMEMREFTYPE eType)
+{
+ PSUPDRVBUNDLE pBundle;
+
+ /*
+ * Validate input.
+ */
+ if (!uPtr)
+ {
+ Log(("Illegal address %p\n", (void *)uPtr));
+ return VERR_INVALID_PARAMETER;
+ }
+
+ /*
+ * Search for the address.
+ */
+ RTSpinlockAcquire(pSession->Spinlock);
+ for (pBundle = &pSession->Bundle; pBundle; pBundle = pBundle->pNext)
+ {
+ if (pBundle->cUsed > 0)
+ {
+ unsigned i;
+ for (i = 0; i < RT_ELEMENTS(pBundle->aMem); i++)
+ {
+ if ( pBundle->aMem[i].eType == eType
+ && pBundle->aMem[i].MemObj != NIL_RTR0MEMOBJ
+ && ( (RTHCUINTPTR)RTR0MemObjAddress(pBundle->aMem[i].MemObj) == uPtr
+ || ( pBundle->aMem[i].MapObjR3 != NIL_RTR0MEMOBJ
+ && RTR0MemObjAddressR3(pBundle->aMem[i].MapObjR3) == uPtr))
+ )
+ {
+ /* Make a copy of it and release it outside the spinlock. */
+ SUPDRVMEMREF Mem = pBundle->aMem[i];
+ pBundle->aMem[i].eType = MEMREF_TYPE_UNUSED;
+ pBundle->aMem[i].MemObj = NIL_RTR0MEMOBJ;
+ pBundle->aMem[i].MapObjR3 = NIL_RTR0MEMOBJ;
+ RTSpinlockRelease(pSession->Spinlock);
+
+ if (Mem.MapObjR3 != NIL_RTR0MEMOBJ)
+ {
+ int rc = RTR0MemObjFree(Mem.MapObjR3, false);
+ AssertRC(rc); /** @todo figure out how to handle this. */
+ }
+ if (Mem.MemObj != NIL_RTR0MEMOBJ)
+ {
+ int rc = RTR0MemObjFree(Mem.MemObj, true /* fFreeMappings */);
+ AssertRC(rc); /** @todo figure out how to handle this. */
+ }
+ return VINF_SUCCESS;
+ }
+ }
+ }
+ }
+ RTSpinlockRelease(pSession->Spinlock);
+ Log(("Failed to find %p!!! (eType=%d)\n", (void *)uPtr, eType));
+ return VERR_INVALID_PARAMETER;
+}
+
+
+/**
+ * Opens an image. If it's the first time it's opened the call must upload
+ * the bits using the supdrvIOCtl_LdrLoad() / SUPDRV_IOCTL_LDR_LOAD function.
+ *
+ * This is the 1st step of the loading.
+ *
+ * @returns IPRT status code.
+ * @param pDevExt Device globals.
+ * @param pSession Session data.
+ * @param pReq The open request.
+ */
+static int supdrvIOCtl_LdrOpen(PSUPDRVDEVEXT pDevExt, PSUPDRVSESSION pSession, PSUPLDROPEN pReq)
+{
+ int rc;
+ PSUPDRVLDRIMAGE pImage;
+ void *pv;
+ size_t cchName = strlen(pReq->u.In.szName); /* (caller checked < 32). */
+ SUPDRV_CHECK_SMAP_SETUP();
+ SUPDRV_CHECK_SMAP_CHECK(pDevExt, RT_NOTHING);
+ LogFlow(("supdrvIOCtl_LdrOpen: szName=%s cbImageWithEverything=%d\n", pReq->u.In.szName, pReq->u.In.cbImageWithEverything));
+
+ /*
+ * Check if we got an instance of the image already.
+ */
+ supdrvLdrLock(pDevExt);
+ SUPDRV_CHECK_SMAP_CHECK(pDevExt, RT_NOTHING);
+ for (pImage = pDevExt->pLdrImages; pImage; pImage = pImage->pNext)
+ {
+ if ( pImage->szName[cchName] == '\0'
+ && !memcmp(pImage->szName, pReq->u.In.szName, cchName))
+ {
+ /** @todo Add an _1M (or something) per session reference. */
+ if (RT_LIKELY(pImage->cImgUsage < UINT32_MAX / 2U))
+ {
+ /** @todo check cbImageBits and cbImageWithEverything here, if they differs
+ * that indicates that the images are different. */
+ pReq->u.Out.pvImageBase = pImage->pvImage;
+ pReq->u.Out.fNeedsLoading = pImage->uState == SUP_IOCTL_LDR_OPEN;
+ pReq->u.Out.fNativeLoader = pImage->fNative;
+ supdrvLdrAddUsage(pDevExt, pSession, pImage, true /*fRing3Usage*/);
+ supdrvLdrUnlock(pDevExt);
+ SUPDRV_CHECK_SMAP_CHECK(pDevExt, RT_NOTHING);
+ return VINF_SUCCESS;
+ }
+ supdrvLdrUnlock(pDevExt);
+ Log(("supdrvIOCtl_LdrOpen: Too many existing references to '%s'!\n", pReq->u.In.szName));
+ return VERR_TOO_MANY_REFERENCES;
+ }
+ }
+ /* (not found - add it!) */
+
+ /* If the loader interface is locked down, make userland fail early */
+ if (pDevExt->fLdrLockedDown)
+ {
+ supdrvLdrUnlock(pDevExt);
+ Log(("supdrvIOCtl_LdrOpen: Not adding '%s' to image list, loader interface is locked down!\n", pReq->u.In.szName));
+ return VERR_PERMISSION_DENIED;
+ }
+
+ /* Stop if caller doesn't wish to prepare loading things. */
+ if (!pReq->u.In.cbImageBits)
+ {
+ supdrvLdrUnlock(pDevExt);
+ Log(("supdrvIOCtl_LdrOpen: Returning VERR_MODULE_NOT_FOUND for '%s'!\n", pReq->u.In.szName));
+ return VERR_MODULE_NOT_FOUND;
+ }
+
+ /*
+ * Allocate memory.
+ */
+ Assert(cchName < sizeof(pImage->szName));
+ pv = RTMemAllocZ(sizeof(SUPDRVLDRIMAGE));
+ if (!pv)
+ {
+ supdrvLdrUnlock(pDevExt);
+ Log(("supdrvIOCtl_LdrOpen: RTMemAllocZ() failed\n"));
+ return VERR_NO_MEMORY;
+ }
+ SUPDRV_CHECK_SMAP_CHECK(pDevExt, RT_NOTHING);
+
+ /*
+ * Setup and link in the LDR stuff.
+ */
+ pImage = (PSUPDRVLDRIMAGE)pv;
+ pImage->pvImage = NULL;
+ pImage->hMemObjImage = NIL_RTR0MEMOBJ;
+ pImage->cbImageWithEverything = pReq->u.In.cbImageWithEverything;
+ pImage->cbImageBits = pReq->u.In.cbImageBits;
+ pImage->cSymbols = 0;
+ pImage->paSymbols = NULL;
+ pImage->pachStrTab = NULL;
+ pImage->cbStrTab = 0;
+ pImage->cSegments = 0;
+ pImage->paSegments = NULL;
+ pImage->pfnModuleInit = NULL;
+ pImage->pfnModuleTerm = NULL;
+ pImage->pfnServiceReqHandler = NULL;
+ pImage->uState = SUP_IOCTL_LDR_OPEN;
+ pImage->cImgUsage = 0; /* Increased by supdrvLdrAddUsage later */
+ pImage->pDevExt = pDevExt;
+ pImage->pImageImport = NULL;
+ pImage->uMagic = SUPDRVLDRIMAGE_MAGIC;
+ pImage->pWrappedModInfo = NULL;
+ memcpy(pImage->szName, pReq->u.In.szName, cchName + 1);
+
+ /*
+ * Try load it using the native loader, if that isn't supported, fall back
+ * on the older method.
+ */
+ pImage->fNative = true;
+ rc = supdrvOSLdrOpen(pDevExt, pImage, pReq->u.In.szFilename);
+ if (rc == VERR_NOT_SUPPORTED)
+ {
+ rc = RTR0MemObjAllocPage(&pImage->hMemObjImage, pImage->cbImageBits, true /*fExecutable*/);
+ if (RT_SUCCESS(rc))
+ {
+ pImage->pvImage = RTR0MemObjAddress(pImage->hMemObjImage);
+ pImage->fNative = false;
+ }
+ SUPDRV_CHECK_SMAP_CHECK(pDevExt, RT_NOTHING);
+ }
+ if (RT_SUCCESS(rc))
+ rc = supdrvLdrAddUsage(pDevExt, pSession, pImage, true /*fRing3Usage*/);
+ if (RT_FAILURE(rc))
+ {
+ supdrvLdrUnlock(pDevExt);
+ pImage->uMagic = SUPDRVLDRIMAGE_MAGIC_DEAD;
+ RTMemFree(pImage);
+ Log(("supdrvIOCtl_LdrOpen(%s): failed - %Rrc\n", pReq->u.In.szName, rc));
+ return rc;
+ }
+ Assert(RT_VALID_PTR(pImage->pvImage) || RT_FAILURE(rc));
+
+ /*
+ * Link it.
+ */
+ pImage->pNext = pDevExt->pLdrImages;
+ pDevExt->pLdrImages = pImage;
+
+ pReq->u.Out.pvImageBase = pImage->pvImage;
+ pReq->u.Out.fNeedsLoading = true;
+ pReq->u.Out.fNativeLoader = pImage->fNative;
+ supdrvOSLdrNotifyOpened(pDevExt, pImage, pReq->u.In.szFilename);
+
+ supdrvLdrUnlock(pDevExt);
+ SUPDRV_CHECK_SMAP_CHECK(pDevExt, RT_NOTHING);
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * Formats a load error message.
+ *
+ * @returns @a rc
+ * @param rc Return code.
+ * @param pReq The request.
+ * @param pszFormat The error message format string.
+ * @param ... Argument to the format string.
+ */
+int VBOXCALL supdrvLdrLoadError(int rc, PSUPLDRLOAD pReq, const char *pszFormat, ...)
+{
+ va_list va;
+ va_start(va, pszFormat);
+ pReq->u.Out.uErrorMagic = SUPLDRLOAD_ERROR_MAGIC;
+ RTStrPrintfV(pReq->u.Out.szError, sizeof(pReq->u.Out.szError), pszFormat, va);
+ va_end(va);
+ Log(("SUP_IOCTL_LDR_LOAD: %s [rc=%Rrc]\n", pReq->u.Out.szError, rc));
+ return rc;
+}
+
+
+/**
+ * Worker that validates a pointer to an image entrypoint.
+ *
+ * Calls supdrvLdrLoadError on error.
+ *
+ * @returns IPRT status code.
+ * @param pDevExt The device globals.
+ * @param pImage The loader image.
+ * @param pv The pointer into the image.
+ * @param fMayBeNull Whether it may be NULL.
+ * @param pszSymbol The entrypoint name or log name. If the symbol is
+ * capitalized it signifies a specific symbol, otherwise it
+ * for logging.
+ * @param pbImageBits The image bits prepared by ring-3.
+ * @param pReq The request for passing to supdrvLdrLoadError.
+ *
+ * @note Will leave the loader lock on failure!
+ */
+static int supdrvLdrValidatePointer(PSUPDRVDEVEXT pDevExt, PSUPDRVLDRIMAGE pImage, void *pv, bool fMayBeNull,
+ const uint8_t *pbImageBits, const char *pszSymbol, PSUPLDRLOAD pReq)
+{
+ if (!fMayBeNull || pv)
+ {
+ uint32_t iSeg;
+
+ /* Must be within the image bits: */
+ uintptr_t const uRva = (uintptr_t)pv - (uintptr_t)pImage->pvImage;
+ if (uRva >= pImage->cbImageBits)
+ {
+ supdrvLdrUnlock(pDevExt);
+ return supdrvLdrLoadError(VERR_INVALID_PARAMETER, pReq,
+ "Invalid entry point address %p given for %s: RVA %#zx, image size %#zx",
+ pv, pszSymbol, uRva, pImage->cbImageBits);
+ }
+
+ /* Must be in an executable segment: */
+ for (iSeg = 0; iSeg < pImage->cSegments; iSeg++)
+ if (uRva - pImage->paSegments[iSeg].off < (uintptr_t)pImage->paSegments[iSeg].cb)
+ {
+ if (pImage->paSegments[iSeg].fProt & SUPLDR_PROT_EXEC)
+ break;
+ supdrvLdrUnlock(pDevExt);
+ return supdrvLdrLoadError(VERR_INVALID_PARAMETER, pReq,
+ "Bad entry point %p given for %s: not executable (seg #%u: %#RX32 LB %#RX32 prot %#x)",
+ pv, pszSymbol, iSeg, pImage->paSegments[iSeg].off, pImage->paSegments[iSeg].cb,
+ pImage->paSegments[iSeg].fProt);
+ }
+ if (iSeg >= pImage->cSegments)
+ {
+ supdrvLdrUnlock(pDevExt);
+ return supdrvLdrLoadError(VERR_INVALID_PARAMETER, pReq,
+ "Bad entry point %p given for %s: no matching segment found (RVA %#zx)!",
+ pv, pszSymbol, uRva);
+ }
+
+ if (pImage->fNative)
+ {
+ /** @todo pass pReq along to the native code. */
+ int rc = supdrvOSLdrValidatePointer(pDevExt, pImage, pv, pbImageBits, pszSymbol);
+ if (RT_FAILURE(rc))
+ {
+ supdrvLdrUnlock(pDevExt);
+ return supdrvLdrLoadError(VERR_INVALID_PARAMETER, pReq,
+ "Bad entry point address %p for %s: rc=%Rrc\n", pv, pszSymbol, rc);
+ }
+ }
+ }
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * Loads the image bits.
+ *
+ * This is the 2nd step of the loading.
+ *
+ * @returns IPRT status code.
+ * @param pDevExt Device globals.
+ * @param pSession Session data.
+ * @param pReq The request.
+ */
+static int supdrvIOCtl_LdrLoad(PSUPDRVDEVEXT pDevExt, PSUPDRVSESSION pSession, PSUPLDRLOAD pReq)
+{
+ PSUPDRVLDRUSAGE pUsage;
+ PSUPDRVLDRIMAGE pImage;
+ PSUPDRVLDRIMAGE pImageImport;
+ int rc;
+ SUPDRV_CHECK_SMAP_SETUP();
+ LogFlow(("supdrvIOCtl_LdrLoad: pvImageBase=%p cbImageWithEverything=%d\n", pReq->u.In.pvImageBase, pReq->u.In.cbImageWithEverything));
+ SUPDRV_CHECK_SMAP_CHECK(pDevExt, RT_NOTHING);
+
+ /*
+ * Find the ldr image.
+ */
+ supdrvLdrLock(pDevExt);
+ SUPDRV_CHECK_SMAP_CHECK(pDevExt, RT_NOTHING);
+
+ pUsage = pSession->pLdrUsage;
+ while (pUsage && pUsage->pImage->pvImage != pReq->u.In.pvImageBase)
+ pUsage = pUsage->pNext;
+ if (!pUsage)
+ {
+ supdrvLdrUnlock(pDevExt);
+ return supdrvLdrLoadError(VERR_INVALID_HANDLE, pReq, "Image not found");
+ }
+ pImage = pUsage->pImage;
+
+ /*
+ * Validate input.
+ */
+ if ( pImage->cbImageWithEverything != pReq->u.In.cbImageWithEverything
+ || pImage->cbImageBits != pReq->u.In.cbImageBits)
+ {
+ supdrvLdrUnlock(pDevExt);
+ return supdrvLdrLoadError(VERR_INVALID_HANDLE, pReq, "Image size mismatch found: %u(prep) != %u(load) or %u != %u",
+ pImage->cbImageWithEverything, pReq->u.In.cbImageWithEverything, pImage->cbImageBits, pReq->u.In.cbImageBits);
+ }
+
+ if (pImage->uState != SUP_IOCTL_LDR_OPEN)
+ {
+ unsigned uState = pImage->uState;
+ supdrvLdrUnlock(pDevExt);
+ if (uState != SUP_IOCTL_LDR_LOAD)
+ AssertMsgFailed(("SUP_IOCTL_LDR_LOAD: invalid image state %d (%#x)!\n", uState, uState));
+ pReq->u.Out.uErrorMagic = 0;
+ return VERR_ALREADY_LOADED;
+ }
+
+ /* If the loader interface is locked down, don't load new images */
+ if (pDevExt->fLdrLockedDown)
+ {
+ supdrvLdrUnlock(pDevExt);
+ return supdrvLdrLoadError(VERR_PERMISSION_DENIED, pReq, "Loader is locked down");
+ }
+
+ /*
+ * If the new image is a dependant of VMMR0.r0, resolve it via the
+ * caller's usage list and make sure it's in ready state.
+ */
+ pImageImport = NULL;
+ if (pReq->u.In.fFlags & SUPLDRLOAD_F_DEP_VMMR0)
+ {
+ PSUPDRVLDRUSAGE pUsageDependency = pSession->pLdrUsage;
+ while (pUsageDependency && pUsageDependency->pImage->pvImage != pDevExt->pvVMMR0)
+ pUsageDependency = pUsageDependency->pNext;
+ if (!pUsageDependency || !pDevExt->pvVMMR0)
+ {
+ supdrvLdrUnlock(pDevExt);
+ return supdrvLdrLoadError(VERR_MODULE_NOT_FOUND, pReq, "VMMR0.r0 not loaded by session");
+ }
+ pImageImport = pUsageDependency->pImage;
+ if (pImageImport->uState != SUP_IOCTL_LDR_LOAD)
+ {
+ supdrvLdrUnlock(pDevExt);
+ return supdrvLdrLoadError(VERR_MODULE_NOT_FOUND, pReq, "VMMR0.r0 is not ready (state %#x)", pImageImport->uState);
+ }
+ }
+
+ /*
+ * Copy the segments before we start using supdrvLdrValidatePointer for entrypoint validation.
+ */
+ pImage->cSegments = pReq->u.In.cSegments;
+ {
+ size_t cbSegments = pImage->cSegments * sizeof(SUPLDRSEG);
+ pImage->paSegments = (PSUPLDRSEG)RTMemDup(&pReq->u.In.abImage[pReq->u.In.offSegments], cbSegments);
+ if (pImage->paSegments) /* Align the last segment size to avoid upsetting RTR0MemObjProtect. */ /** @todo relax RTR0MemObjProtect */
+ pImage->paSegments[pImage->cSegments - 1].cb = RT_ALIGN_32(pImage->paSegments[pImage->cSegments - 1].cb, PAGE_SIZE);
+ else
+ {
+ supdrvLdrUnlock(pDevExt);
+ return supdrvLdrLoadError(VERR_NO_MEMORY, pReq, "Out of memory for segment table: %#x", cbSegments);
+ }
+ SUPDRV_CHECK_SMAP_CHECK(pDevExt, RT_NOTHING);
+ }
+
+ /*
+ * Validate entrypoints.
+ */
+ switch (pReq->u.In.eEPType)
+ {
+ case SUPLDRLOADEP_NOTHING:
+ break;
+
+ case SUPLDRLOADEP_VMMR0:
+ rc = supdrvLdrValidatePointer(pDevExt, pImage, pReq->u.In.EP.VMMR0.pvVMMR0EntryFast, false, pReq->u.In.abImage, "VMMR0EntryFast", pReq);
+ if (RT_FAILURE(rc))
+ return rc;
+ rc = supdrvLdrValidatePointer(pDevExt, pImage, pReq->u.In.EP.VMMR0.pvVMMR0EntryEx, false, pReq->u.In.abImage, "VMMR0EntryEx", pReq);
+ if (RT_FAILURE(rc))
+ return rc;
+
+ /* Fail here if there is already a VMMR0 module. */
+ if (pDevExt->pvVMMR0 != NULL)
+ {
+ supdrvLdrUnlock(pDevExt);
+ return supdrvLdrLoadError(VERR_INVALID_PARAMETER, pReq, "There is already a VMMR0 module loaded (%p)", pDevExt->pvVMMR0);
+ }
+ break;
+
+ case SUPLDRLOADEP_SERVICE:
+ rc = supdrvLdrValidatePointer(pDevExt, pImage, pReq->u.In.EP.Service.pfnServiceReq, false, pReq->u.In.abImage, "pfnServiceReq", pReq);
+ if (RT_FAILURE(rc))
+ return rc;
+ if ( pReq->u.In.EP.Service.apvReserved[0] != NIL_RTR0PTR
+ || pReq->u.In.EP.Service.apvReserved[1] != NIL_RTR0PTR
+ || pReq->u.In.EP.Service.apvReserved[2] != NIL_RTR0PTR)
+ {
+ supdrvLdrUnlock(pDevExt);
+ return supdrvLdrLoadError(VERR_INVALID_PARAMETER, pReq, "apvReserved={%p,%p,%p} MBZ!",
+ pReq->u.In.EP.Service.apvReserved[0], pReq->u.In.EP.Service.apvReserved[1],
+ pReq->u.In.EP.Service.apvReserved[2]);
+ }
+ break;
+
+ default:
+ supdrvLdrUnlock(pDevExt);
+ return supdrvLdrLoadError(VERR_INVALID_PARAMETER, pReq, "Invalid eEPType=%d", pReq->u.In.eEPType);
+ }
+
+ rc = supdrvLdrValidatePointer(pDevExt, pImage, pReq->u.In.pfnModuleInit, true, pReq->u.In.abImage, "ModuleInit", pReq);
+ if (RT_FAILURE(rc))
+ return rc;
+ rc = supdrvLdrValidatePointer(pDevExt, pImage, pReq->u.In.pfnModuleTerm, true, pReq->u.In.abImage, "ModuleTerm", pReq);
+ if (RT_FAILURE(rc))
+ return rc;
+ SUPDRV_CHECK_SMAP_CHECK(pDevExt, RT_NOTHING);
+
+ /*
+ * Allocate and copy the tables if non-native.
+ * (No need to do try/except as this is a buffered request.)
+ */
+ if (!pImage->fNative)
+ {
+ pImage->cbStrTab = pReq->u.In.cbStrTab;
+ if (pImage->cbStrTab)
+ {
+ pImage->pachStrTab = (char *)RTMemDup(&pReq->u.In.abImage[pReq->u.In.offStrTab], pImage->cbStrTab);
+ if (!pImage->pachStrTab)
+ rc = supdrvLdrLoadError(VERR_NO_MEMORY, pReq, "Out of memory for string table: %#x", pImage->cbStrTab);
+ SUPDRV_CHECK_SMAP_CHECK(pDevExt, RT_NOTHING);
+ }
+
+ pImage->cSymbols = pReq->u.In.cSymbols;
+ if (RT_SUCCESS(rc) && pImage->cSymbols)
+ {
+ size_t cbSymbols = pImage->cSymbols * sizeof(SUPLDRSYM);
+ pImage->paSymbols = (PSUPLDRSYM)RTMemDup(&pReq->u.In.abImage[pReq->u.In.offSymbols], cbSymbols);
+ if (!pImage->paSymbols)
+ rc = supdrvLdrLoadError(VERR_NO_MEMORY, pReq, "Out of memory for symbol table: %#x", cbSymbols);
+ SUPDRV_CHECK_SMAP_CHECK(pDevExt, RT_NOTHING);
+ }
+ }
+
+ /*
+ * Copy the bits and apply permissions / complete native loading.
+ */
+ if (RT_SUCCESS(rc))
+ {
+ pImage->uState = SUP_IOCTL_LDR_LOAD;
+ pImage->pfnModuleInit = (PFNR0MODULEINIT)(uintptr_t)pReq->u.In.pfnModuleInit;
+ pImage->pfnModuleTerm = (PFNR0MODULETERM)(uintptr_t)pReq->u.In.pfnModuleTerm;
+
+ if (pImage->fNative)
+ rc = supdrvOSLdrLoad(pDevExt, pImage, pReq->u.In.abImage, pReq);
+ else
+ {
+ uint32_t i;
+ memcpy(pImage->pvImage, &pReq->u.In.abImage[0], pImage->cbImageBits);
+
+ for (i = 0; i < pImage->cSegments; i++)
+ {
+ rc = RTR0MemObjProtect(pImage->hMemObjImage, pImage->paSegments[i].off, pImage->paSegments[i].cb,
+ pImage->paSegments[i].fProt);
+ if (RT_SUCCESS(rc))
+ continue;
+ if (rc == VERR_NOT_SUPPORTED)
+ rc = VINF_SUCCESS;
+ else
+ rc = supdrvLdrLoadError(rc, pReq, "RTR0MemObjProtect failed on seg#%u %#RX32 LB %#RX32 fProt=%#x",
+ i, pImage->paSegments[i].off, pImage->paSegments[i].cb, pImage->paSegments[i].fProt);
+ break;
+ }
+ Log(("vboxdrv: Loaded '%s' at %p\n", pImage->szName, pImage->pvImage));
+ }
+ SUPDRV_CHECK_SMAP_CHECK(pDevExt, RT_NOTHING);
+ }
+
+ /*
+ * On success call the module initialization.
+ */
+ LogFlow(("supdrvIOCtl_LdrLoad: pfnModuleInit=%p\n", pImage->pfnModuleInit));
+ if (RT_SUCCESS(rc) && pImage->pfnModuleInit)
+ {
+ Log(("supdrvIOCtl_LdrLoad: calling pfnModuleInit=%p\n", pImage->pfnModuleInit));
+ pDevExt->pLdrInitImage = pImage;
+ pDevExt->hLdrInitThread = RTThreadNativeSelf();
+ SUPDRV_CHECK_SMAP_CHECK(pDevExt, RT_NOTHING);
+ rc = pImage->pfnModuleInit(pImage);
+ SUPDRV_CHECK_SMAP_CHECK(pDevExt, RT_NOTHING);
+ pDevExt->pLdrInitImage = NULL;
+ pDevExt->hLdrInitThread = NIL_RTNATIVETHREAD;
+ if (RT_FAILURE(rc))
+ supdrvLdrLoadError(rc, pReq, "ModuleInit failed: %Rrc", rc);
+ }
+ if (RT_SUCCESS(rc))
+ {
+ /*
+ * Publish any standard entry points.
+ */
+ switch (pReq->u.In.eEPType)
+ {
+ case SUPLDRLOADEP_VMMR0:
+ Assert(!pDevExt->pvVMMR0);
+ Assert(!pDevExt->pfnVMMR0EntryFast);
+ Assert(!pDevExt->pfnVMMR0EntryEx);
+ ASMAtomicWritePtrVoid(&pDevExt->pvVMMR0, pImage->pvImage);
+ ASMAtomicWritePtrVoid((void * volatile *)(uintptr_t)&pDevExt->pfnVMMR0EntryFast,
+ (void *)(uintptr_t) pReq->u.In.EP.VMMR0.pvVMMR0EntryFast);
+ ASMAtomicWritePtrVoid((void * volatile *)(uintptr_t)&pDevExt->pfnVMMR0EntryEx,
+ (void *)(uintptr_t) pReq->u.In.EP.VMMR0.pvVMMR0EntryEx);
+ break;
+ case SUPLDRLOADEP_SERVICE:
+ pImage->pfnServiceReqHandler = (PFNSUPR0SERVICEREQHANDLER)(uintptr_t)pReq->u.In.EP.Service.pfnServiceReq;
+ break;
+ default:
+ break;
+ }
+
+ /*
+ * Increase the usage counter of any imported image.
+ */
+ if (pImageImport)
+ {
+ pImageImport->cImgUsage++;
+ if (pImageImport->cImgUsage == 2 && pImageImport->pWrappedModInfo)
+ supdrvOSLdrRetainWrapperModule(pDevExt, pImageImport);
+ pImage->pImageImport = pImageImport;
+ }
+
+ /*
+ * Done!
+ */
+ SUPR0Printf("vboxdrv: %RKv %s\n", pImage->pvImage, pImage->szName);
+ pReq->u.Out.uErrorMagic = 0;
+ pReq->u.Out.szError[0] = '\0';
+ }
+ else
+ {
+ /* Inform the tracing component in case ModuleInit registered TPs. */
+ supdrvTracerModuleUnloading(pDevExt, pImage);
+
+ pImage->uState = SUP_IOCTL_LDR_OPEN;
+ pImage->pfnModuleInit = NULL;
+ pImage->pfnModuleTerm = NULL;
+ pImage->pfnServiceReqHandler= NULL;
+ pImage->cbStrTab = 0;
+ RTMemFree(pImage->pachStrTab);
+ pImage->pachStrTab = NULL;
+ RTMemFree(pImage->paSymbols);
+ pImage->paSymbols = NULL;
+ pImage->cSymbols = 0;
+ }
+
+ supdrvLdrUnlock(pDevExt);
+ SUPDRV_CHECK_SMAP_CHECK(pDevExt, RT_NOTHING);
+ return rc;
+}
+
+
+/**
+ * Registers a .r0 module wrapped in a native one and manually loaded.
+ *
+ * @returns VINF_SUCCESS or error code (no info statuses).
+ * @param pDevExt Device globals.
+ * @param pWrappedModInfo The wrapped module info.
+ * @param pvNative OS specific information.
+ * @param phMod Where to store the module handle.
+ */
+int VBOXCALL supdrvLdrRegisterWrappedModule(PSUPDRVDEVEXT pDevExt, PCSUPLDRWRAPPEDMODULE pWrappedModInfo,
+ void *pvNative, void **phMod)
+{
+ size_t cchName;
+ PSUPDRVLDRIMAGE pImage;
+ PCSUPLDRWRAPMODSYMBOL paSymbols;
+ uint16_t idx;
+ const char *pszPrevSymbol;
+ int rc;
+ SUPDRV_CHECK_SMAP_SETUP();
+ SUPDRV_CHECK_SMAP_CHECK(pDevExt, RT_NOTHING);
+
+ /*
+ * Validate input.
+ */
+ AssertPtrReturn(phMod, VERR_INVALID_POINTER);
+ *phMod = NULL;
+ AssertPtrReturn(pDevExt, VERR_INTERNAL_ERROR_2);
+
+ AssertPtrReturn(pWrappedModInfo, VERR_INVALID_POINTER);
+ AssertMsgReturn(pWrappedModInfo->uMagic == SUPLDRWRAPPEDMODULE_MAGIC,
+ ("uMagic=%#x, expected %#x\n", pWrappedModInfo->uMagic, SUPLDRWRAPPEDMODULE_MAGIC),
+ VERR_INVALID_MAGIC);
+ AssertMsgReturn(pWrappedModInfo->uVersion == SUPLDRWRAPPEDMODULE_VERSION,
+ ("Unsupported uVersion=%#x, current version %#x\n", pWrappedModInfo->uVersion, SUPLDRWRAPPEDMODULE_VERSION),
+ VERR_VERSION_MISMATCH);
+ AssertMsgReturn(pWrappedModInfo->uEndMagic == SUPLDRWRAPPEDMODULE_MAGIC,
+ ("uEndMagic=%#x, expected %#x\n", pWrappedModInfo->uEndMagic, SUPLDRWRAPPEDMODULE_MAGIC),
+ VERR_INVALID_MAGIC);
+ AssertMsgReturn(pWrappedModInfo->fFlags <= SUPLDRWRAPPEDMODULE_F_VMMR0, ("Unknown flags in: %#x\n", pWrappedModInfo->fFlags),
+ VERR_INVALID_FLAGS);
+
+ /* szName: */
+ AssertReturn(RTStrEnd(pWrappedModInfo->szName, sizeof(pWrappedModInfo->szName)) != NULL, VERR_INVALID_NAME);
+ AssertReturn(supdrvIsLdrModuleNameValid(pWrappedModInfo->szName), VERR_INVALID_NAME);
+ AssertCompile(sizeof(pImage->szName) == sizeof(pWrappedModInfo->szName));
+ cchName = strlen(pWrappedModInfo->szName);
+
+ /* Image range: */
+ AssertPtrReturn(pWrappedModInfo->pvImageStart, VERR_INVALID_POINTER);
+ AssertPtrReturn(pWrappedModInfo->pvImageEnd, VERR_INVALID_POINTER);
+ AssertReturn((uintptr_t)pWrappedModInfo->pvImageEnd > (uintptr_t)pWrappedModInfo->pvImageStart, VERR_INVALID_PARAMETER);
+
+ /* Symbol table: */
+ AssertMsgReturn(pWrappedModInfo->cSymbols <= _8K, ("Too many symbols: %u, max 8192\n", pWrappedModInfo->cSymbols),
+ VERR_TOO_MANY_SYMLINKS);
+ pszPrevSymbol = "\x7f";
+ paSymbols = pWrappedModInfo->paSymbols;
+ idx = pWrappedModInfo->cSymbols;
+ while (idx-- > 0)
+ {
+ const char *pszSymbol = paSymbols[idx].pszSymbol;
+ AssertMsgReturn(RT_VALID_PTR(pszSymbol) && RT_VALID_PTR(paSymbols[idx].pfnValue),
+ ("paSymbols[%u]: %p/%p\n", idx, pszSymbol, paSymbols[idx].pfnValue),
+ VERR_INVALID_POINTER);
+ AssertReturn(*pszSymbol != '\0', VERR_EMPTY_STRING);
+ AssertMsgReturn(strcmp(pszSymbol, pszPrevSymbol) < 0,
+ ("symbol table out of order at index %u: '%s' vs '%s'\n", idx, pszSymbol, pszPrevSymbol),
+ VERR_WRONG_ORDER);
+ pszPrevSymbol = pszSymbol;
+ }
+
+ /* Standard entry points: */
+ AssertPtrNullReturn(pWrappedModInfo->pfnModuleInit, VERR_INVALID_POINTER);
+ AssertPtrNullReturn(pWrappedModInfo->pfnModuleTerm, VERR_INVALID_POINTER);
+ AssertReturn((uintptr_t)pWrappedModInfo->pfnModuleInit != (uintptr_t)pWrappedModInfo->pfnModuleTerm || pWrappedModInfo->pfnModuleInit == NULL,
+ VERR_INVALID_PARAMETER);
+ if (pWrappedModInfo->fFlags & SUPLDRWRAPPEDMODULE_F_VMMR0)
+ {
+ AssertReturn(pWrappedModInfo->pfnServiceReqHandler == NULL, VERR_INVALID_PARAMETER);
+ AssertPtrReturn(pWrappedModInfo->pfnVMMR0EntryFast, VERR_INVALID_POINTER);
+ AssertPtrReturn(pWrappedModInfo->pfnVMMR0EntryEx, VERR_INVALID_POINTER);
+ AssertReturn(pWrappedModInfo->pfnVMMR0EntryFast != pWrappedModInfo->pfnVMMR0EntryEx, VERR_INVALID_PARAMETER);
+ }
+ else
+ {
+ AssertPtrNullReturn(pWrappedModInfo->pfnServiceReqHandler, VERR_INVALID_POINTER);
+ AssertReturn(pWrappedModInfo->pfnVMMR0EntryFast == NULL, VERR_INVALID_PARAMETER);
+ AssertReturn(pWrappedModInfo->pfnVMMR0EntryEx == NULL, VERR_INVALID_PARAMETER);
+ }
+
+ /*
+ * Check if we got an instance of the image already.
+ */
+ supdrvLdrLock(pDevExt);
+ SUPDRV_CHECK_SMAP_CHECK(pDevExt, RT_NOTHING);
+ for (pImage = pDevExt->pLdrImages; pImage; pImage = pImage->pNext)
+ {
+ if ( pImage->szName[cchName] == '\0'
+ && !memcmp(pImage->szName, pWrappedModInfo->szName, cchName))
+ {
+ supdrvLdrUnlock(pDevExt);
+ Log(("supdrvLdrRegisterWrappedModule: '%s' already loaded!\n", pWrappedModInfo->szName));
+ return VERR_ALREADY_LOADED;
+ }
+ }
+ /* (not found - add it!) */
+
+ /* If the loader interface is locked down, make userland fail early */
+ if (pDevExt->fLdrLockedDown)
+ {
+ supdrvLdrUnlock(pDevExt);
+ Log(("supdrvLdrRegisterWrappedModule: Not adding '%s' to image list, loader interface is locked down!\n", pWrappedModInfo->szName));
+ return VERR_PERMISSION_DENIED;
+ }
+
+ /* Only one VMMR0: */
+ if ( pDevExt->pvVMMR0 != NULL
+ && (pWrappedModInfo->fFlags & SUPLDRWRAPPEDMODULE_F_VMMR0))
+ {
+ supdrvLdrUnlock(pDevExt);
+ Log(("supdrvLdrRegisterWrappedModule: Rejecting '%s' as we already got a VMMR0 module!\n", pWrappedModInfo->szName));
+ return VERR_ALREADY_EXISTS;
+ }
+
+ /*
+ * Allocate memory.
+ */
+ Assert(cchName < sizeof(pImage->szName));
+ pImage = (PSUPDRVLDRIMAGE)RTMemAllocZ(sizeof(SUPDRVLDRIMAGE));
+ if (!pImage)
+ {
+ supdrvLdrUnlock(pDevExt);
+ Log(("supdrvLdrRegisterWrappedModule: RTMemAllocZ() failed\n"));
+ return VERR_NO_MEMORY;
+ }
+ SUPDRV_CHECK_SMAP_CHECK(pDevExt, RT_NOTHING);
+
+ /*
+ * Setup and link in the LDR stuff.
+ */
+ pImage->pvImage = (void *)pWrappedModInfo->pvImageStart;
+ pImage->hMemObjImage = NIL_RTR0MEMOBJ;
+ pImage->cbImageWithEverything
+ = pImage->cbImageBits = (uintptr_t)pWrappedModInfo->pvImageEnd - (uintptr_t)pWrappedModInfo->pvImageStart;
+ pImage->cSymbols = 0;
+ pImage->paSymbols = NULL;
+ pImage->pachStrTab = NULL;
+ pImage->cbStrTab = 0;
+ pImage->cSegments = 0;
+ pImage->paSegments = NULL;
+ pImage->pfnModuleInit = pWrappedModInfo->pfnModuleInit;
+ pImage->pfnModuleTerm = pWrappedModInfo->pfnModuleTerm;
+ pImage->pfnServiceReqHandler = NULL; /* Only setting this after module init */
+ pImage->uState = SUP_IOCTL_LDR_LOAD;
+ pImage->cImgUsage = 1; /* Held by the wrapper module till unload. */
+ pImage->pDevExt = pDevExt;
+ pImage->pImageImport = NULL;
+ pImage->uMagic = SUPDRVLDRIMAGE_MAGIC;
+ pImage->pWrappedModInfo = pWrappedModInfo;
+ pImage->pvWrappedNative = pvNative;
+ pImage->fNative = true;
+ memcpy(pImage->szName, pWrappedModInfo->szName, cchName + 1);
+
+ /*
+ * Link it.
+ */
+ pImage->pNext = pDevExt->pLdrImages;
+ pDevExt->pLdrImages = pImage;
+
+ /*
+ * Call module init function if found.
+ */
+ rc = VINF_SUCCESS;
+ if (pImage->pfnModuleInit)
+ {
+ Log(("supdrvIOCtl_LdrLoad: calling pfnModuleInit=%p\n", pImage->pfnModuleInit));
+ pDevExt->pLdrInitImage = pImage;
+ pDevExt->hLdrInitThread = RTThreadNativeSelf();
+ SUPDRV_CHECK_SMAP_CHECK(pDevExt, RT_NOTHING);
+ rc = pImage->pfnModuleInit(pImage);
+ SUPDRV_CHECK_SMAP_CHECK(pDevExt, RT_NOTHING);
+ pDevExt->pLdrInitImage = NULL;
+ pDevExt->hLdrInitThread = NIL_RTNATIVETHREAD;
+ }
+ if (RT_SUCCESS(rc))
+ {
+ /*
+ * Update entry points.
+ */
+ if (pWrappedModInfo->fFlags & SUPLDRWRAPPEDMODULE_F_VMMR0)
+ {
+ Assert(!pDevExt->pvVMMR0);
+ Assert(!pDevExt->pfnVMMR0EntryFast);
+ Assert(!pDevExt->pfnVMMR0EntryEx);
+ ASMAtomicWritePtrVoid(&pDevExt->pvVMMR0, pImage->pvImage);
+ ASMAtomicWritePtrVoid((void * volatile *)(uintptr_t)&pDevExt->pfnVMMR0EntryFast,
+ (void *)(uintptr_t) pWrappedModInfo->pfnVMMR0EntryFast);
+ ASMAtomicWritePtrVoid((void * volatile *)(uintptr_t)&pDevExt->pfnVMMR0EntryEx,
+ (void *)(uintptr_t) pWrappedModInfo->pfnVMMR0EntryEx);
+ }
+ else
+ pImage->pfnServiceReqHandler = pWrappedModInfo->pfnServiceReqHandler;
+#ifdef IN_RING3
+# error "WTF?"
+#endif
+ *phMod = pImage;
+ }
+ else
+ {
+ /*
+ * Module init failed - bail, no module term callout.
+ */
+ SUPR0Printf("ModuleInit failed for '%s': %Rrc\n", pImage->szName, rc);
+
+ pImage->pfnModuleTerm = NULL;
+ pImage->uState = SUP_IOCTL_LDR_OPEN;
+ supdrvLdrFree(pDevExt, pImage);
+ }
+
+ supdrvLdrUnlock(pDevExt);
+ SUPDRV_CHECK_SMAP_CHECK(pDevExt, RT_NOTHING);
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * Decrements SUPDRVLDRIMAGE::cImgUsage when two or greater.
+ *
+ * @param pDevExt Device globals.
+ * @param pImage The image.
+ * @param cReference Number of references being removed.
+ */
+DECLINLINE(void) supdrvLdrSubtractUsage(PSUPDRVDEVEXT pDevExt, PSUPDRVLDRIMAGE pImage, uint32_t cReference)
+{
+ Assert(cReference > 0);
+ Assert(pImage->cImgUsage > cReference);
+ pImage->cImgUsage -= cReference;
+ if (pImage->cImgUsage == 1 && pImage->pWrappedModInfo)
+ supdrvOSLdrReleaseWrapperModule(pDevExt, pImage);
+}
+
+
+/**
+ * Frees a previously loaded (prep'ed) image.
+ *
+ * @returns IPRT status code.
+ * @param pDevExt Device globals.
+ * @param pSession Session data.
+ * @param pReq The request.
+ */
+static int supdrvIOCtl_LdrFree(PSUPDRVDEVEXT pDevExt, PSUPDRVSESSION pSession, PSUPLDRFREE pReq)
+{
+ int rc;
+ PSUPDRVLDRUSAGE pUsagePrev;
+ PSUPDRVLDRUSAGE pUsage;
+ PSUPDRVLDRIMAGE pImage;
+ LogFlow(("supdrvIOCtl_LdrFree: pvImageBase=%p\n", pReq->u.In.pvImageBase));
+
+ /*
+ * Find the ldr image.
+ */
+ supdrvLdrLock(pDevExt);
+ pUsagePrev = NULL;
+ pUsage = pSession->pLdrUsage;
+ while (pUsage && pUsage->pImage->pvImage != pReq->u.In.pvImageBase)
+ {
+ pUsagePrev = pUsage;
+ pUsage = pUsage->pNext;
+ }
+ if (!pUsage)
+ {
+ supdrvLdrUnlock(pDevExt);
+ Log(("SUP_IOCTL_LDR_FREE: couldn't find image!\n"));
+ return VERR_INVALID_HANDLE;
+ }
+ if (pUsage->cRing3Usage == 0)
+ {
+ supdrvLdrUnlock(pDevExt);
+ Log(("SUP_IOCTL_LDR_FREE: No ring-3 reference to the image!\n"));
+ return VERR_CALLER_NO_REFERENCE;
+ }
+
+ /*
+ * Check if we can remove anything.
+ */
+ rc = VINF_SUCCESS;
+ pImage = pUsage->pImage;
+ Log(("SUP_IOCTL_LDR_FREE: pImage=%p %s cImgUsage=%d r3=%d r0=%u\n",
+ pImage, pImage->szName, pImage->cImgUsage, pUsage->cRing3Usage, pUsage->cRing0Usage));
+ if (pImage->cImgUsage <= 1 || pUsage->cRing3Usage + pUsage->cRing0Usage <= 1)
+ {
+ /*
+ * Check if there are any objects with destructors in the image, if
+ * so leave it for the session cleanup routine so we get a chance to
+ * clean things up in the right order and not leave them all dangling.
+ */
+ RTSpinlockAcquire(pDevExt->Spinlock);
+ if (pImage->cImgUsage <= 1)
+ {
+ PSUPDRVOBJ pObj;
+ for (pObj = pDevExt->pObjs; pObj; pObj = pObj->pNext)
+ if (RT_UNLIKELY((uintptr_t)pObj->pfnDestructor - (uintptr_t)pImage->pvImage < pImage->cbImageBits))
+ {
+ rc = VERR_DANGLING_OBJECTS;
+ break;
+ }
+ }
+ else
+ {
+ PSUPDRVUSAGE pGenUsage;
+ for (pGenUsage = pSession->pUsage; pGenUsage; pGenUsage = pGenUsage->pNext)
+ if (RT_UNLIKELY((uintptr_t)pGenUsage->pObj->pfnDestructor - (uintptr_t)pImage->pvImage < pImage->cbImageBits))
+ {
+ rc = VERR_DANGLING_OBJECTS;
+ break;
+ }
+ }
+ RTSpinlockRelease(pDevExt->Spinlock);
+ if (rc == VINF_SUCCESS)
+ {
+ /* unlink it */
+ if (pUsagePrev)
+ pUsagePrev->pNext = pUsage->pNext;
+ else
+ pSession->pLdrUsage = pUsage->pNext;
+
+ /* free it */
+ pUsage->pImage = NULL;
+ pUsage->pNext = NULL;
+ RTMemFree(pUsage);
+
+ /*
+ * Dereference the image.
+ */
+ if (pImage->cImgUsage <= 1)
+ supdrvLdrFree(pDevExt, pImage);
+ else
+ supdrvLdrSubtractUsage(pDevExt, pImage, 1);
+ }
+ else
+ Log(("supdrvIOCtl_LdrFree: Dangling objects in %p/%s!\n", pImage->pvImage, pImage->szName));
+ }
+ else
+ {
+ /*
+ * Dereference both image and usage.
+ */
+ pUsage->cRing3Usage--;
+ supdrvLdrSubtractUsage(pDevExt, pImage, 1);
+ }
+
+ supdrvLdrUnlock(pDevExt);
+ return rc;
+}
+
+
+/**
+ * Deregisters a wrapped .r0 module.
+ *
+ * @param pDevExt Device globals.
+ * @param pWrappedModInfo The wrapped module info.
+ * @param phMod Where to store the module is stored (NIL'ed on
+ * success).
+ */
+int VBOXCALL supdrvLdrDeregisterWrappedModule(PSUPDRVDEVEXT pDevExt, PCSUPLDRWRAPPEDMODULE pWrappedModInfo, void **phMod)
+{
+ PSUPDRVLDRIMAGE pImage;
+ uint32_t cSleeps;
+
+ /*
+ * Validate input.
+ */
+ AssertPtrReturn(pWrappedModInfo, VERR_INVALID_POINTER);
+ AssertMsgReturn(pWrappedModInfo->uMagic == SUPLDRWRAPPEDMODULE_MAGIC,
+ ("uMagic=%#x, expected %#x\n", pWrappedModInfo->uMagic, SUPLDRWRAPPEDMODULE_MAGIC),
+ VERR_INVALID_MAGIC);
+ AssertMsgReturn(pWrappedModInfo->uEndMagic == SUPLDRWRAPPEDMODULE_MAGIC,
+ ("uEndMagic=%#x, expected %#x\n", pWrappedModInfo->uEndMagic, SUPLDRWRAPPEDMODULE_MAGIC),
+ VERR_INVALID_MAGIC);
+
+ AssertPtrReturn(phMod, VERR_INVALID_POINTER);
+ pImage = *(PSUPDRVLDRIMAGE *)phMod;
+ if (!pImage)
+ return VINF_SUCCESS;
+ AssertPtrReturn(pImage, VERR_INVALID_POINTER);
+ AssertMsgReturn(pImage->uMagic == SUPDRVLDRIMAGE_MAGIC, ("pImage=%p uMagic=%#x\n", pImage, pImage->uMagic),
+ VERR_INVALID_MAGIC);
+ AssertMsgReturn(pImage->pvImage == pWrappedModInfo->pvImageStart,
+ ("pWrappedModInfo(%p)->pvImageStart=%p vs. pImage(=%p)->pvImage=%p\n",
+ pWrappedModInfo, pWrappedModInfo->pvImageStart, pImage, pImage->pvImage),
+ VERR_MISMATCH);
+
+ AssertPtrReturn(pDevExt, VERR_INVALID_POINTER);
+
+ /*
+ * Try free it, but first we have to wait for its usage count to reach 1 (our).
+ */
+ supdrvLdrLock(pDevExt);
+ for (cSleeps = 0; ; cSleeps++)
+ {
+ PSUPDRVLDRIMAGE pCur;
+
+ /* Check that the image is in the list. */
+ for (pCur = pDevExt->pLdrImages; pCur; pCur = pCur->pNext)
+ if (pCur == pImage)
+ break;
+ AssertBreak(pCur == pImage);
+
+ /* Anyone still using it? */
+ if (pImage->cImgUsage <= 1)
+ break;
+
+ /* Someone is using it, wait and check again. */
+ if (!(cSleeps % 60))
+ SUPR0Printf("supdrvLdrUnregisterWrappedModule: Still %u users of wrapped image '%s' ...\n",
+ pImage->cImgUsage, pImage->szName);
+ supdrvLdrUnlock(pDevExt);
+ RTThreadSleep(1000);
+ supdrvLdrLock(pDevExt);
+ }
+
+ /* We're the last 'user', free it. */
+ supdrvLdrFree(pDevExt, pImage);
+
+ supdrvLdrUnlock(pDevExt);
+
+ *phMod = NULL;
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * Lock down the image loader interface.
+ *
+ * @returns IPRT status code.
+ * @param pDevExt Device globals.
+ */
+static int supdrvIOCtl_LdrLockDown(PSUPDRVDEVEXT pDevExt)
+{
+ LogFlow(("supdrvIOCtl_LdrLockDown:\n"));
+
+ supdrvLdrLock(pDevExt);
+ if (!pDevExt->fLdrLockedDown)
+ {
+ pDevExt->fLdrLockedDown = true;
+ Log(("supdrvIOCtl_LdrLockDown: Image loader interface locked down\n"));
+ }
+ supdrvLdrUnlock(pDevExt);
+
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * Worker for getting the address of a symbol in an image.
+ *
+ * @returns IPRT status code.
+ * @param pDevExt Device globals.
+ * @param pImage The image to search.
+ * @param pszSymbol The symbol name.
+ * @param cchSymbol The length of the symbol name.
+ * @param ppvValue Where to return the symbol
+ * @note Caller owns the loader lock.
+ */
+static int supdrvLdrQuerySymbolWorker(PSUPDRVDEVEXT pDevExt, PSUPDRVLDRIMAGE pImage,
+ const char *pszSymbol, size_t cchSymbol, void **ppvValue)
+{
+ int rc = VERR_SYMBOL_NOT_FOUND;
+ if (pImage->fNative && !pImage->pWrappedModInfo)
+ rc = supdrvOSLdrQuerySymbol(pDevExt, pImage, pszSymbol, cchSymbol, ppvValue);
+ else if (pImage->fNative && pImage->pWrappedModInfo)
+ {
+ PCSUPLDRWRAPMODSYMBOL paSymbols = pImage->pWrappedModInfo->paSymbols;
+ uint32_t iEnd = pImage->pWrappedModInfo->cSymbols;
+ uint32_t iStart = 0;
+ while (iStart < iEnd)
+ {
+ uint32_t const i = iStart + (iEnd - iStart) / 2;
+ int const iDiff = strcmp(paSymbols[i].pszSymbol, pszSymbol);
+ if (iDiff < 0)
+ iStart = i + 1;
+ else if (iDiff > 0)
+ iEnd = i;
+ else
+ {
+ *ppvValue = (void *)(uintptr_t)paSymbols[i].pfnValue;
+ rc = VINF_SUCCESS;
+ break;
+ }
+ }
+#ifdef VBOX_STRICT
+ if (rc != VINF_SUCCESS)
+ for (iStart = 0, iEnd = pImage->pWrappedModInfo->cSymbols; iStart < iEnd; iStart++)
+ Assert(strcmp(paSymbols[iStart].pszSymbol, pszSymbol));
+#endif
+ }
+ else
+ {
+ const char *pchStrings = pImage->pachStrTab;
+ PSUPLDRSYM paSyms = pImage->paSymbols;
+ uint32_t i;
+ Assert(!pImage->pWrappedModInfo);
+ for (i = 0; i < pImage->cSymbols; i++)
+ {
+ if ( paSyms[i].offName + cchSymbol + 1 <= pImage->cbStrTab
+ && !memcmp(pchStrings + paSyms[i].offName, pszSymbol, cchSymbol + 1))
+ {
+ /*
+ * Note! The int32_t is for native loading on solaris where the data
+ * and text segments are in very different places.
+ */
+ *ppvValue = (uint8_t *)pImage->pvImage + (int32_t)paSyms[i].offSymbol;
+ rc = VINF_SUCCESS;
+ break;
+ }
+ }
+ }
+ return rc;
+}
+
+
+/**
+ * Queries the address of a symbol in an open image.
+ *
+ * @returns IPRT status code.
+ * @param pDevExt Device globals.
+ * @param pSession Session data.
+ * @param pReq The request buffer.
+ */
+static int supdrvIOCtl_LdrQuerySymbol(PSUPDRVDEVEXT pDevExt, PSUPDRVSESSION pSession, PSUPLDRGETSYMBOL pReq)
+{
+ PSUPDRVLDRIMAGE pImage;
+ PSUPDRVLDRUSAGE pUsage;
+ const size_t cchSymbol = strlen(pReq->u.In.szSymbol);
+ void *pvSymbol = NULL;
+ int rc;
+ Log3(("supdrvIOCtl_LdrQuerySymbol: pvImageBase=%p szSymbol=\"%s\"\n", pReq->u.In.pvImageBase, pReq->u.In.szSymbol));
+
+ /*
+ * Find the ldr image.
+ */
+ supdrvLdrLock(pDevExt);
+
+ pUsage = pSession->pLdrUsage;
+ while (pUsage && pUsage->pImage->pvImage != pReq->u.In.pvImageBase)
+ pUsage = pUsage->pNext;
+ if (pUsage)
+ {
+ pImage = pUsage->pImage;
+ if (pImage->uState == SUP_IOCTL_LDR_LOAD)
+ {
+ /*
+ * Search the image exports / symbol strings.
+ */
+ rc = supdrvLdrQuerySymbolWorker(pDevExt, pImage, pReq->u.In.szSymbol, cchSymbol, &pvSymbol);
+ }
+ else
+ {
+ Log(("SUP_IOCTL_LDR_GET_SYMBOL: invalid image state %d (%#x)!\n", pImage->uState, pImage->uState));
+ rc = VERR_WRONG_ORDER;
+ }
+ }
+ else
+ {
+ Log(("SUP_IOCTL_LDR_GET_SYMBOL: couldn't find image!\n"));
+ rc = VERR_INVALID_HANDLE;
+ }
+
+ supdrvLdrUnlock(pDevExt);
+
+ pReq->u.Out.pvSymbol = pvSymbol;
+ return rc;
+}
+
+
+/**
+ * Gets the address of a symbol in an open image or the support driver.
+ *
+ * @returns VBox status code.
+ * @param pDevExt Device globals.
+ * @param pSession Session data.
+ * @param pReq The request buffer.
+ */
+static int supdrvIDC_LdrGetSymbol(PSUPDRVDEVEXT pDevExt, PSUPDRVSESSION pSession, PSUPDRVIDCREQGETSYM pReq)
+{
+ const char *pszSymbol = pReq->u.In.pszSymbol;
+ const char *pszModule = pReq->u.In.pszModule;
+ size_t cchSymbol;
+ char const *pszEnd;
+ uint32_t i;
+ int rc;
+
+ /*
+ * Input validation.
+ */
+ AssertPtrReturn(pszSymbol, VERR_INVALID_POINTER);
+ pszEnd = RTStrEnd(pszSymbol, 512);
+ AssertReturn(pszEnd, VERR_INVALID_PARAMETER);
+ cchSymbol = pszEnd - pszSymbol;
+
+ if (pszModule)
+ {
+ AssertPtrReturn(pszModule, VERR_INVALID_POINTER);
+ pszEnd = RTStrEnd(pszModule, 64);
+ AssertReturn(pszEnd, VERR_INVALID_PARAMETER);
+ }
+ Log3(("supdrvIDC_LdrGetSymbol: pszModule=%p:{%s} pszSymbol=%p:{%s}\n", pszModule, pszModule, pszSymbol, pszSymbol));
+
+ if ( !pszModule
+ || !strcmp(pszModule, "SupDrv"))
+ {
+ /*
+ * Search the support driver export table.
+ */
+ rc = VERR_SYMBOL_NOT_FOUND;
+ for (i = 0; i < RT_ELEMENTS(g_aFunctions); i++)
+ if (!strcmp(g_aFunctions[i].szName, pszSymbol))
+ {
+ pReq->u.Out.pfnSymbol = (PFNRT)(uintptr_t)g_aFunctions[i].pfn;
+ rc = VINF_SUCCESS;
+ break;
+ }
+ }
+ else
+ {
+ /*
+ * Find the loader image.
+ */
+ PSUPDRVLDRIMAGE pImage;
+
+ supdrvLdrLock(pDevExt);
+
+ for (pImage = pDevExt->pLdrImages; pImage; pImage = pImage->pNext)
+ if (!strcmp(pImage->szName, pszModule))
+ break;
+ if (pImage && pImage->uState == SUP_IOCTL_LDR_LOAD)
+ {
+ /*
+ * Search the image exports / symbol strings. Do usage counting on the session.
+ */
+ rc = supdrvLdrQuerySymbolWorker(pDevExt, pImage, pszSymbol, cchSymbol, (void **)&pReq->u.Out.pfnSymbol);
+ if (RT_SUCCESS(rc))
+ rc = supdrvLdrAddUsage(pDevExt, pSession, pImage, true /*fRing3Usage*/);
+ }
+ else
+ rc = pImage ? VERR_WRONG_ORDER : VERR_MODULE_NOT_FOUND;
+
+ supdrvLdrUnlock(pDevExt);
+ }
+ return rc;
+}
+
+
+/**
+ * Looks up a symbol in g_aFunctions
+ *
+ * @returns VINF_SUCCESS on success, VERR_SYMBOL_NOT_FOUND on failure.
+ * @param pszSymbol The symbol to look up.
+ * @param puValue Where to return the value.
+ */
+int VBOXCALL supdrvLdrGetExportedSymbol(const char *pszSymbol, uintptr_t *puValue)
+{
+ uint32_t i;
+ for (i = 0; i < RT_ELEMENTS(g_aFunctions); i++)
+ if (!strcmp(g_aFunctions[i].szName, pszSymbol))
+ {
+ *puValue = (uintptr_t)g_aFunctions[i].pfn;
+ return VINF_SUCCESS;
+ }
+
+ if (!strcmp(pszSymbol, "g_SUPGlobalInfoPage"))
+ {
+ *puValue = (uintptr_t)g_pSUPGlobalInfoPage;
+ return VINF_SUCCESS;
+ }
+
+ return VERR_SYMBOL_NOT_FOUND;
+}
+
+
+/**
+ * Adds a usage reference in the specified session of an image.
+ *
+ * Called while owning the loader semaphore.
+ *
+ * @returns VINF_SUCCESS on success and VERR_NO_MEMORY on failure.
+ * @param pDevExt Pointer to device extension.
+ * @param pSession Session in question.
+ * @param pImage Image which the session is using.
+ * @param fRing3Usage Set if it's ring-3 usage, clear if ring-0.
+ */
+static int supdrvLdrAddUsage(PSUPDRVDEVEXT pDevExt, PSUPDRVSESSION pSession, PSUPDRVLDRIMAGE pImage, bool fRing3Usage)
+{
+ PSUPDRVLDRUSAGE pUsage;
+ LogFlow(("supdrvLdrAddUsage: pImage=%p %d\n", pImage, fRing3Usage));
+
+ /*
+ * Referenced it already?
+ */
+ pUsage = pSession->pLdrUsage;
+ while (pUsage)
+ {
+ if (pUsage->pImage == pImage)
+ {
+ if (fRing3Usage)
+ pUsage->cRing3Usage++;
+ else
+ pUsage->cRing0Usage++;
+ Assert(pImage->cImgUsage > 1 || !pImage->pWrappedModInfo);
+ pImage->cImgUsage++;
+ return VINF_SUCCESS;
+ }
+ pUsage = pUsage->pNext;
+ }
+
+ /*
+ * Allocate new usage record.
+ */
+ pUsage = (PSUPDRVLDRUSAGE)RTMemAlloc(sizeof(*pUsage));
+ AssertReturn(pUsage, VERR_NO_MEMORY);
+ pUsage->cRing3Usage = fRing3Usage ? 1 : 0;
+ pUsage->cRing0Usage = fRing3Usage ? 0 : 1;
+ pUsage->pImage = pImage;
+ pUsage->pNext = pSession->pLdrUsage;
+ pSession->pLdrUsage = pUsage;
+
+ /*
+ * Wrapped modules needs to retain a native module reference.
+ */
+ pImage->cImgUsage++;
+ if (pImage->cImgUsage == 2 && pImage->pWrappedModInfo)
+ supdrvOSLdrRetainWrapperModule(pDevExt, pImage);
+
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * Frees a load image.
+ *
+ * @param pDevExt Pointer to device extension.
+ * @param pImage Pointer to the image we're gonna free.
+ * This image must exit!
+ * @remark The caller MUST own SUPDRVDEVEXT::mtxLdr!
+ */
+static void supdrvLdrFree(PSUPDRVDEVEXT pDevExt, PSUPDRVLDRIMAGE pImage)
+{
+ unsigned cLoops;
+ for (cLoops = 0; ; cLoops++)
+ {
+ PSUPDRVLDRIMAGE pImagePrev;
+ PSUPDRVLDRIMAGE pImageImport;
+ LogFlow(("supdrvLdrFree: pImage=%p %s [loop %u]\n", pImage, pImage->szName, cLoops));
+ AssertBreak(cLoops < 2);
+
+ /*
+ * Warn if we're releasing images while the image loader interface is
+ * locked down -- we won't be able to reload them!
+ */
+ if (pDevExt->fLdrLockedDown)
+ Log(("supdrvLdrFree: Warning: unloading '%s' image, while loader interface is locked down!\n", pImage->szName));
+
+ /* find it - arg. should've used doubly linked list. */
+ Assert(pDevExt->pLdrImages);
+ pImagePrev = NULL;
+ if (pDevExt->pLdrImages != pImage)
+ {
+ pImagePrev = pDevExt->pLdrImages;
+ while (pImagePrev->pNext != pImage)
+ pImagePrev = pImagePrev->pNext;
+ Assert(pImagePrev->pNext == pImage);
+ }
+
+ /* unlink */
+ if (pImagePrev)
+ pImagePrev->pNext = pImage->pNext;
+ else
+ pDevExt->pLdrImages = pImage->pNext;
+
+ /* check if this is VMMR0.r0 unset its entry point pointers. */
+ if (pDevExt->pvVMMR0 == pImage->pvImage)
+ {
+ pDevExt->pvVMMR0 = NULL;
+ pDevExt->pfnVMMR0EntryFast = NULL;
+ pDevExt->pfnVMMR0EntryEx = NULL;
+ }
+
+ /* check for objects with destructors in this image. (Shouldn't happen.) */
+ if (pDevExt->pObjs)
+ {
+ unsigned cObjs = 0;
+ PSUPDRVOBJ pObj;
+ RTSpinlockAcquire(pDevExt->Spinlock);
+ for (pObj = pDevExt->pObjs; pObj; pObj = pObj->pNext)
+ if (RT_UNLIKELY((uintptr_t)pObj->pfnDestructor - (uintptr_t)pImage->pvImage < pImage->cbImageBits))
+ {
+ pObj->pfnDestructor = NULL;
+ cObjs++;
+ }
+ RTSpinlockRelease(pDevExt->Spinlock);
+ if (cObjs)
+ OSDBGPRINT(("supdrvLdrFree: Image '%s' has %d dangling objects!\n", pImage->szName, cObjs));
+ }
+
+ /* call termination function if fully loaded. */
+ if ( pImage->pfnModuleTerm
+ && pImage->uState == SUP_IOCTL_LDR_LOAD)
+ {
+ LogFlow(("supdrvIOCtl_LdrLoad: calling pfnModuleTerm=%p\n", pImage->pfnModuleTerm));
+ pDevExt->hLdrTermThread = RTThreadNativeSelf();
+ pImage->pfnModuleTerm(pImage);
+ pDevExt->hLdrTermThread = NIL_RTNATIVETHREAD;
+ }
+
+ /* Inform the tracing component. */
+ supdrvTracerModuleUnloading(pDevExt, pImage);
+
+ /* Do native unload if appropriate, then inform the native code about the
+ unloading (mainly for non-native loading case). */
+ if (pImage->fNative)
+ supdrvOSLdrUnload(pDevExt, pImage);
+ supdrvOSLdrNotifyUnloaded(pDevExt, pImage);
+
+ /* free the image */
+ pImage->uMagic = SUPDRVLDRIMAGE_MAGIC_DEAD;
+ pImage->cImgUsage = 0;
+ pImage->pDevExt = NULL;
+ pImage->pNext = NULL;
+ pImage->uState = SUP_IOCTL_LDR_FREE;
+ RTR0MemObjFree(pImage->hMemObjImage, true /*fMappings*/);
+ pImage->hMemObjImage = NIL_RTR0MEMOBJ;
+ pImage->pvImage = NULL;
+ RTMemFree(pImage->pachStrTab);
+ pImage->pachStrTab = NULL;
+ RTMemFree(pImage->paSymbols);
+ pImage->paSymbols = NULL;
+ RTMemFree(pImage->paSegments);
+ pImage->paSegments = NULL;
+
+ pImageImport = pImage->pImageImport;
+ pImage->pImageImport = NULL;
+
+ RTMemFree(pImage);
+
+ /*
+ * Deal with any import image.
+ */
+ if (!pImageImport)
+ break;
+ if (pImageImport->cImgUsage > 1)
+ {
+ supdrvLdrSubtractUsage(pDevExt, pImageImport, 1);
+ break;
+ }
+ pImage = pImageImport;
+ }
+}
+
+
+/**
+ * Acquires the loader lock.
+ *
+ * @returns IPRT status code.
+ * @param pDevExt The device extension.
+ * @note Not recursive on all platforms yet.
+ */
+DECLINLINE(int) supdrvLdrLock(PSUPDRVDEVEXT pDevExt)
+{
+#ifdef SUPDRV_USE_MUTEX_FOR_LDR
+ int rc = RTSemMutexRequest(pDevExt->mtxLdr, RT_INDEFINITE_WAIT);
+#else
+ int rc = RTSemFastMutexRequest(pDevExt->mtxLdr);
+#endif
+ AssertRC(rc);
+ return rc;
+}
+
+
+/**
+ * Releases the loader lock.
+ *
+ * @returns IPRT status code.
+ * @param pDevExt The device extension.
+ */
+DECLINLINE(int) supdrvLdrUnlock(PSUPDRVDEVEXT pDevExt)
+{
+#ifdef SUPDRV_USE_MUTEX_FOR_LDR
+ return RTSemMutexRelease(pDevExt->mtxLdr);
+#else
+ return RTSemFastMutexRelease(pDevExt->mtxLdr);
+#endif
+}
+
+
+/**
+ * Acquires the global loader lock.
+ *
+ * This can be useful when accessing structures being modified by the ModuleInit
+ * and ModuleTerm. Use SUPR0LdrUnlock() to unlock.
+ *
+ * @returns VBox status code.
+ * @param pSession The session doing the locking.
+ *
+ * @note Cannot be used during ModuleInit or ModuleTerm callbacks.
+ */
+SUPR0DECL(int) SUPR0LdrLock(PSUPDRVSESSION pSession)
+{
+ AssertReturn(SUP_IS_SESSION_VALID(pSession), VERR_INVALID_PARAMETER);
+ return supdrvLdrLock(pSession->pDevExt);
+}
+SUPR0_EXPORT_SYMBOL(SUPR0LdrLock);
+
+
+/**
+ * Releases the global loader lock.
+ *
+ * Must correspond to a SUPR0LdrLock call!
+ *
+ * @returns VBox status code.
+ * @param pSession The session doing the locking.
+ *
+ * @note Cannot be used during ModuleInit or ModuleTerm callbacks.
+ */
+SUPR0DECL(int) SUPR0LdrUnlock(PSUPDRVSESSION pSession)
+{
+ AssertReturn(SUP_IS_SESSION_VALID(pSession), VERR_INVALID_PARAMETER);
+ return supdrvLdrUnlock(pSession->pDevExt);
+}
+SUPR0_EXPORT_SYMBOL(SUPR0LdrUnlock);
+
+
+/**
+ * For checking lock ownership in Assert() statements during ModuleInit and
+ * ModuleTerm.
+ *
+ * @returns Whether we own the loader lock or not.
+ * @param hMod The module in question.
+ * @param fWantToHear For hosts where it is difficult to know who owns the
+ * lock, this will be returned instead.
+ */
+SUPR0DECL(bool) SUPR0LdrIsLockOwnerByMod(void *hMod, bool fWantToHear)
+{
+ PSUPDRVDEVEXT pDevExt;
+ RTNATIVETHREAD hOwner;
+
+ PSUPDRVLDRIMAGE pImage = (PSUPDRVLDRIMAGE)hMod;
+ AssertPtrReturn(pImage, fWantToHear);
+ AssertReturn(pImage->uMagic == SUPDRVLDRIMAGE_MAGIC, fWantToHear);
+
+ pDevExt = pImage->pDevExt;
+ AssertPtrReturn(pDevExt, fWantToHear);
+
+ /*
+ * Expecting this to be called at init/term time only, so this will be sufficient.
+ */
+ hOwner = pDevExt->hLdrInitThread;
+ if (hOwner == NIL_RTNATIVETHREAD)
+ hOwner = pDevExt->hLdrTermThread;
+ if (hOwner != NIL_RTNATIVETHREAD)
+ return hOwner == RTThreadNativeSelf();
+
+ /*
+ * Neither of the two semaphore variants currently offers very good
+ * introspection, so we wing it for now. This API is VBOX_STRICT only.
+ */
+#ifdef SUPDRV_USE_MUTEX_FOR_LDR
+ return RTSemMutexIsOwned(pDevExt->mtxLdr) && fWantToHear;
+#else
+ return fWantToHear;
+#endif
+}
+SUPR0_EXPORT_SYMBOL(SUPR0LdrIsLockOwnerByMod);
+
+
+/**
+ * Locates and retains the given module for ring-0 usage.
+ *
+ * @returns VBox status code.
+ * @param pSession The session to associate the module reference with.
+ * @param pszName The module name (no path).
+ * @param phMod Where to return the module handle. The module is
+ * referenced and a call to SUPR0LdrModRelease() is
+ * necessary when done with it.
+ */
+SUPR0DECL(int) SUPR0LdrModByName(PSUPDRVSESSION pSession, const char *pszName, void **phMod)
+{
+ int rc;
+ size_t cchName;
+ PSUPDRVDEVEXT pDevExt;
+
+ /*
+ * Validate input.
+ */
+ AssertPtrReturn(phMod, VERR_INVALID_POINTER);
+ *phMod = NULL;
+ AssertReturn(SUP_IS_SESSION_VALID(pSession), VERR_INVALID_PARAMETER);
+ AssertPtrReturn(pszName, VERR_INVALID_POINTER);
+ cchName = strlen(pszName);
+ AssertReturn(cchName > 0, VERR_EMPTY_STRING);
+ AssertReturn(cchName < RT_SIZEOFMEMB(SUPDRVLDRIMAGE, szName), VERR_MODULE_NOT_FOUND);
+
+ /*
+ * Do the lookup.
+ */
+ pDevExt = pSession->pDevExt;
+ rc = supdrvLdrLock(pDevExt);
+ if (RT_SUCCESS(rc))
+ {
+ PSUPDRVLDRIMAGE pImage;
+ for (pImage = pDevExt->pLdrImages; pImage; pImage = pImage->pNext)
+ {
+ if ( pImage->szName[cchName] == '\0'
+ && !memcmp(pImage->szName, pszName, cchName))
+ {
+ /*
+ * Check the state and make sure we don't overflow the reference counter before return it.
+ */
+ uint32_t uState = pImage->uState;
+ if (uState == SUP_IOCTL_LDR_LOAD)
+ {
+ if (RT_LIKELY(pImage->cImgUsage < UINT32_MAX / 2U))
+ {
+ supdrvLdrAddUsage(pDevExt, pSession, pImage, false /*fRing3Usage*/);
+ *phMod = pImage;
+ supdrvLdrUnlock(pDevExt);
+ return VINF_SUCCESS;
+ }
+ supdrvLdrUnlock(pDevExt);
+ Log(("SUPR0LdrModByName: Too many existing references to '%s'!\n", pszName));
+ return VERR_TOO_MANY_REFERENCES;
+ }
+ supdrvLdrUnlock(pDevExt);
+ Log(("SUPR0LdrModByName: Module '%s' is not in the loaded state (%d)!\n", pszName, uState));
+ return VERR_INVALID_STATE;
+ }
+ }
+ supdrvLdrUnlock(pDevExt);
+ Log(("SUPR0LdrModByName: Module '%s' not found!\n", pszName));
+ rc = VERR_MODULE_NOT_FOUND;
+ }
+ return rc;
+}
+SUPR0_EXPORT_SYMBOL(SUPR0LdrModByName);
+
+
+/**
+ * Retains a ring-0 module reference.
+ *
+ * Release reference when done by calling SUPR0LdrModRelease().
+ *
+ * @returns VBox status code.
+ * @param pSession The session to reference the module in. A usage
+ * record is added if needed.
+ * @param hMod The handle to the module to retain.
+ */
+SUPR0DECL(int) SUPR0LdrModRetain(PSUPDRVSESSION pSession, void *hMod)
+{
+ PSUPDRVDEVEXT pDevExt;
+ PSUPDRVLDRIMAGE pImage;
+ int rc;
+
+ /* Validate input a little. */
+ AssertReturn(SUP_IS_SESSION_VALID(pSession), VERR_INVALID_PARAMETER);
+ AssertPtrReturn(hMod, VERR_INVALID_HANDLE);
+ pImage = (PSUPDRVLDRIMAGE)hMod;
+ AssertReturn(pImage->uMagic == SUPDRVLDRIMAGE_MAGIC, VERR_INVALID_HANDLE);
+
+ /* Reference the module: */
+ pDevExt = pSession->pDevExt;
+ rc = supdrvLdrLock(pDevExt);
+ if (RT_SUCCESS(rc))
+ {
+ if (pImage->uMagic == SUPDRVLDRIMAGE_MAGIC)
+ {
+ if (RT_LIKELY(pImage->cImgUsage < UINT32_MAX / 2U))
+ rc = supdrvLdrAddUsage(pDevExt, pSession, pImage, false /*fRing3Usage*/);
+ else
+ AssertFailedStmt(rc = VERR_TOO_MANY_REFERENCES);
+ }
+ else
+ AssertFailedStmt(rc = VERR_INVALID_HANDLE);
+ supdrvLdrUnlock(pDevExt);
+ }
+ return rc;
+}
+SUPR0_EXPORT_SYMBOL(SUPR0LdrModRetain);
+
+
+/**
+ * Releases a ring-0 module reference retained by SUPR0LdrModByName() or
+ * SUPR0LdrModRetain().
+ *
+ * @returns VBox status code.
+ * @param pSession The session that the module was retained in.
+ * @param hMod The module handle. NULL is silently ignored.
+ */
+SUPR0DECL(int) SUPR0LdrModRelease(PSUPDRVSESSION pSession, void *hMod)
+{
+ PSUPDRVDEVEXT pDevExt;
+ PSUPDRVLDRIMAGE pImage;
+ int rc;
+
+ /*
+ * Validate input.
+ */
+ AssertReturn(SUP_IS_SESSION_VALID(pSession), VERR_INVALID_PARAMETER);
+ if (!hMod)
+ return VINF_SUCCESS;
+ AssertPtrReturn(hMod, VERR_INVALID_HANDLE);
+ pImage = (PSUPDRVLDRIMAGE)hMod;
+ AssertReturn(pImage->uMagic == SUPDRVLDRIMAGE_MAGIC, VERR_INVALID_HANDLE);
+
+ /*
+ * Take the loader lock and revalidate the module:
+ */
+ pDevExt = pSession->pDevExt;
+ rc = supdrvLdrLock(pDevExt);
+ if (RT_SUCCESS(rc))
+ {
+ if (pImage->uMagic == SUPDRVLDRIMAGE_MAGIC)
+ {
+ /*
+ * Find the usage record for the module:
+ */
+ PSUPDRVLDRUSAGE pPrevUsage = NULL;
+ PSUPDRVLDRUSAGE pUsage;
+
+ rc = VERR_MODULE_NOT_FOUND;
+ for (pUsage = pSession->pLdrUsage; pUsage; pUsage = pUsage->pNext)
+ {
+ if (pUsage->pImage == pImage)
+ {
+ /*
+ * Drop a ring-0 reference:
+ */
+ Assert(pImage->cImgUsage >= pUsage->cRing0Usage + pUsage->cRing3Usage);
+ if (pUsage->cRing0Usage > 0)
+ {
+ if (pImage->cImgUsage > 1)
+ {
+ pUsage->cRing0Usage -= 1;
+ supdrvLdrSubtractUsage(pDevExt, pImage, 1);
+ rc = VINF_SUCCESS;
+ }
+ else
+ {
+ Assert(!pImage->pWrappedModInfo /* (The wrapper kmod has the last reference.) */);
+ supdrvLdrFree(pDevExt, pImage);
+
+ if (pPrevUsage)
+ pPrevUsage->pNext = pUsage->pNext;
+ else
+ pSession->pLdrUsage = pUsage->pNext;
+ pUsage->pNext = NULL;
+ pUsage->pImage = NULL;
+ pUsage->cRing0Usage = 0;
+ pUsage->cRing3Usage = 0;
+ RTMemFree(pUsage);
+
+ rc = VINF_OBJECT_DESTROYED;
+ }
+ }
+ else
+ AssertFailedStmt(rc = VERR_CALLER_NO_REFERENCE);
+ break;
+ }
+ pPrevUsage = pUsage;
+ }
+ }
+ else
+ AssertFailedStmt(rc = VERR_INVALID_HANDLE);
+ supdrvLdrUnlock(pDevExt);
+ }
+ return rc;
+
+}
+SUPR0_EXPORT_SYMBOL(SUPR0LdrModRelease);
+
+
+/**
+ * Implements the service call request.
+ *
+ * @returns VBox status code.
+ * @param pDevExt The device extension.
+ * @param pSession The calling session.
+ * @param pReq The request packet, valid.
+ */
+static int supdrvIOCtl_CallServiceModule(PSUPDRVDEVEXT pDevExt, PSUPDRVSESSION pSession, PSUPCALLSERVICE pReq)
+{
+#if !defined(RT_OS_WINDOWS) || defined(RT_ARCH_AMD64) || defined(DEBUG)
+ int rc;
+
+ /*
+ * Find the module first in the module referenced by the calling session.
+ */
+ rc = supdrvLdrLock(pDevExt);
+ if (RT_SUCCESS(rc))
+ {
+ PFNSUPR0SERVICEREQHANDLER pfnServiceReqHandler = NULL;
+ PSUPDRVLDRUSAGE pUsage;
+
+ for (pUsage = pSession->pLdrUsage; pUsage; pUsage = pUsage->pNext)
+ if ( pUsage->pImage->pfnServiceReqHandler
+ && !strcmp(pUsage->pImage->szName, pReq->u.In.szName))
+ {
+ pfnServiceReqHandler = pUsage->pImage->pfnServiceReqHandler;
+ break;
+ }
+ supdrvLdrUnlock(pDevExt);
+
+ if (pfnServiceReqHandler)
+ {
+ /*
+ * Call it.
+ */
+ if (pReq->Hdr.cbIn == SUP_IOCTL_CALL_SERVICE_SIZE(0))
+ rc = pfnServiceReqHandler(pSession, pReq->u.In.uOperation, pReq->u.In.u64Arg, NULL);
+ else
+ rc = pfnServiceReqHandler(pSession, pReq->u.In.uOperation, pReq->u.In.u64Arg, (PSUPR0SERVICEREQHDR)&pReq->abReqPkt[0]);
+ }
+ else
+ rc = VERR_SUPDRV_SERVICE_NOT_FOUND;
+ }
+
+ /* log it */
+ if ( RT_FAILURE(rc)
+ && rc != VERR_INTERRUPTED
+ && rc != VERR_TIMEOUT)
+ Log(("SUP_IOCTL_CALL_SERVICE: rc=%Rrc op=%u out=%u arg=%RX64 p/t=%RTproc/%RTthrd\n",
+ rc, pReq->u.In.uOperation, pReq->Hdr.cbOut, pReq->u.In.u64Arg, RTProcSelf(), RTThreadNativeSelf()));
+ else
+ Log4(("SUP_IOCTL_CALL_SERVICE: rc=%Rrc op=%u out=%u arg=%RX64 p/t=%RTproc/%RTthrd\n",
+ rc, pReq->u.In.uOperation, pReq->Hdr.cbOut, pReq->u.In.u64Arg, RTProcSelf(), RTThreadNativeSelf()));
+ return rc;
+#else /* RT_OS_WINDOWS && !RT_ARCH_AMD64 && !DEBUG */
+ RT_NOREF3(pDevExt, pSession, pReq);
+ return VERR_NOT_IMPLEMENTED;
+#endif /* RT_OS_WINDOWS && !RT_ARCH_AMD64 && !DEBUG */
+}
+
+
+/**
+ * Implements the logger settings request.
+ *
+ * @returns VBox status code.
+ * @param pReq The request.
+ */
+static int supdrvIOCtl_LoggerSettings(PSUPLOGGERSETTINGS pReq)
+{
+ const char *pszGroup = &pReq->u.In.szStrings[pReq->u.In.offGroups];
+ const char *pszFlags = &pReq->u.In.szStrings[pReq->u.In.offFlags];
+ const char *pszDest = &pReq->u.In.szStrings[pReq->u.In.offDestination];
+ PRTLOGGER pLogger = NULL;
+ int rc;
+
+ /*
+ * Some further validation.
+ */
+ switch (pReq->u.In.fWhat)
+ {
+ case SUPLOGGERSETTINGS_WHAT_SETTINGS:
+ case SUPLOGGERSETTINGS_WHAT_CREATE:
+ break;
+
+ case SUPLOGGERSETTINGS_WHAT_DESTROY:
+ if (*pszGroup || *pszFlags || *pszDest)
+ return VERR_INVALID_PARAMETER;
+ if (pReq->u.In.fWhich == SUPLOGGERSETTINGS_WHICH_RELEASE)
+ return VERR_ACCESS_DENIED;
+ break;
+
+ default:
+ return VERR_INTERNAL_ERROR;
+ }
+
+ /*
+ * Get the logger.
+ */
+ switch (pReq->u.In.fWhich)
+ {
+ case SUPLOGGERSETTINGS_WHICH_DEBUG:
+ pLogger = RTLogGetDefaultInstance();
+ break;
+
+ case SUPLOGGERSETTINGS_WHICH_RELEASE:
+ pLogger = RTLogRelGetDefaultInstance();
+ break;
+
+ default:
+ return VERR_INTERNAL_ERROR;
+ }
+
+ /*
+ * Do the job.
+ */
+ switch (pReq->u.In.fWhat)
+ {
+ case SUPLOGGERSETTINGS_WHAT_SETTINGS:
+ if (pLogger)
+ {
+ rc = RTLogFlags(pLogger, pszFlags);
+ if (RT_SUCCESS(rc))
+ rc = RTLogGroupSettings(pLogger, pszGroup);
+ NOREF(pszDest);
+ }
+ else
+ rc = VERR_NOT_FOUND;
+ break;
+
+ case SUPLOGGERSETTINGS_WHAT_CREATE:
+ {
+ if (pLogger)
+ rc = VERR_ALREADY_EXISTS;
+ else
+ {
+ static const char * const s_apszGroups[] = VBOX_LOGGROUP_NAMES;
+
+ rc = RTLogCreate(&pLogger,
+ 0 /* fFlags */,
+ pszGroup,
+ pReq->u.In.fWhich == SUPLOGGERSETTINGS_WHICH_DEBUG
+ ? "VBOX_LOG"
+ : "VBOX_RELEASE_LOG",
+ RT_ELEMENTS(s_apszGroups),
+ s_apszGroups,
+ RTLOGDEST_STDOUT | RTLOGDEST_DEBUGGER,
+ NULL);
+ if (RT_SUCCESS(rc))
+ {
+ rc = RTLogFlags(pLogger, pszFlags);
+ NOREF(pszDest);
+ if (RT_SUCCESS(rc))
+ {
+ switch (pReq->u.In.fWhich)
+ {
+ case SUPLOGGERSETTINGS_WHICH_DEBUG:
+ pLogger = RTLogSetDefaultInstance(pLogger);
+ break;
+ case SUPLOGGERSETTINGS_WHICH_RELEASE:
+ pLogger = RTLogRelSetDefaultInstance(pLogger);
+ break;
+ }
+ }
+ RTLogDestroy(pLogger);
+ }
+ }
+ break;
+ }
+
+ case SUPLOGGERSETTINGS_WHAT_DESTROY:
+ switch (pReq->u.In.fWhich)
+ {
+ case SUPLOGGERSETTINGS_WHICH_DEBUG:
+ pLogger = RTLogSetDefaultInstance(NULL);
+ break;
+ case SUPLOGGERSETTINGS_WHICH_RELEASE:
+ pLogger = RTLogRelSetDefaultInstance(NULL);
+ break;
+ }
+ rc = RTLogDestroy(pLogger);
+ break;
+
+ default:
+ {
+ rc = VERR_INTERNAL_ERROR;
+ break;
+ }
+ }
+
+ return rc;
+}
+
+
+/**
+ * Implements the MSR prober operations.
+ *
+ * @returns VBox status code.
+ * @param pDevExt The device extension.
+ * @param pReq The request.
+ */
+static int supdrvIOCtl_MsrProber(PSUPDRVDEVEXT pDevExt, PSUPMSRPROBER pReq)
+{
+#ifdef SUPDRV_WITH_MSR_PROBER
+ RTCPUID const idCpu = pReq->u.In.idCpu == UINT32_MAX ? NIL_RTCPUID : pReq->u.In.idCpu;
+ int rc;
+
+ switch (pReq->u.In.enmOp)
+ {
+ case SUPMSRPROBEROP_READ:
+ {
+ uint64_t uValue;
+ rc = supdrvOSMsrProberRead(pReq->u.In.uMsr, idCpu, &uValue);
+ if (RT_SUCCESS(rc))
+ {
+ pReq->u.Out.uResults.Read.uValue = uValue;
+ pReq->u.Out.uResults.Read.fGp = false;
+ }
+ else if (rc == VERR_ACCESS_DENIED)
+ {
+ pReq->u.Out.uResults.Read.uValue = 0;
+ pReq->u.Out.uResults.Read.fGp = true;
+ rc = VINF_SUCCESS;
+ }
+ break;
+ }
+
+ case SUPMSRPROBEROP_WRITE:
+ rc = supdrvOSMsrProberWrite(pReq->u.In.uMsr, idCpu, pReq->u.In.uArgs.Write.uToWrite);
+ if (RT_SUCCESS(rc))
+ pReq->u.Out.uResults.Write.fGp = false;
+ else if (rc == VERR_ACCESS_DENIED)
+ {
+ pReq->u.Out.uResults.Write.fGp = true;
+ rc = VINF_SUCCESS;
+ }
+ break;
+
+ case SUPMSRPROBEROP_MODIFY:
+ case SUPMSRPROBEROP_MODIFY_FASTER:
+ rc = supdrvOSMsrProberModify(idCpu, pReq);
+ break;
+
+ default:
+ return VERR_INVALID_FUNCTION;
+ }
+ RT_NOREF1(pDevExt);
+ return rc;
+#else
+ RT_NOREF2(pDevExt, pReq);
+ return VERR_NOT_IMPLEMENTED;
+#endif
+}
+
+
+/**
+ * Resume built-in keyboard on MacBook Air and Pro hosts.
+ * If there is no built-in keyboard device, return success anyway.
+ *
+ * @returns 0 on Mac OS X platform, VERR_NOT_IMPLEMENTED on the other ones.
+ */
+static int supdrvIOCtl_ResumeSuspendedKbds(void)
+{
+#if defined(RT_OS_DARWIN)
+ return supdrvDarwinResumeSuspendedKbds();
+#else
+ return VERR_NOT_IMPLEMENTED;
+#endif
+}
+
diff --git a/src/VBox/HostDrivers/Support/SUPDrv.d b/src/VBox/HostDrivers/Support/SUPDrv.d
new file mode 100644
index 00000000..e2249a96
--- /dev/null
+++ b/src/VBox/HostDrivers/Support/SUPDrv.d
@@ -0,0 +1,51 @@
+/* $Id: SUPDrv.d $ */
+/** @file
+ * SUPDrv - Static dtrace probes.
+ */
+
+/*
+ * Copyright (C) 2010-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+provider vboxdrv
+{
+ probe session__create(struct SUPDRVSESSION *pSession, int fUser);
+ probe session__close(struct SUPDRVSESSION *pSession);
+ probe ioctl__entry(struct SUPDRVSESSION *pSession, uintptr_t uIOCtl, void *pvReqHdr);
+ probe ioctl__return(struct SUPDRVSESSION *pSession, uintptr_t uIOCtl, void *pvReqHdr, int rc, int rcReq);
+};
+
+#pragma D attributes Evolving/Evolving/Common provider vboxdrv provider
+#pragma D attributes Private/Private/Unknown provider vboxdrv module
+#pragma D attributes Private/Private/Unknown provider vboxdrv function
+#pragma D attributes Evolving/Evolving/Common provider vboxdrv name
+#pragma D attributes Evolving/Evolving/Common provider vboxdrv args
+
diff --git a/src/VBox/HostDrivers/Support/SUPDrvGip.cpp b/src/VBox/HostDrivers/Support/SUPDrvGip.cpp
new file mode 100644
index 00000000..c203dd46
--- /dev/null
+++ b/src/VBox/HostDrivers/Support/SUPDrvGip.cpp
@@ -0,0 +1,5120 @@
+/* $Id: SUPDrvGip.cpp $ */
+/** @file
+ * VBoxDrv - The VirtualBox Support Driver - Common code for GIP.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#define LOG_GROUP LOG_GROUP_SUP_DRV
+#define SUPDRV_AGNOSTIC
+#include "SUPDrvInternal.h"
+#ifndef PAGE_SHIFT
+# include <iprt/param.h>
+#endif
+#include <iprt/asm.h>
+#include <iprt/asm-amd64-x86.h>
+#include <iprt/asm-math.h>
+#include <iprt/cpuset.h>
+#include <iprt/handletable.h>
+#include <iprt/mem.h>
+#include <iprt/mp.h>
+#include <iprt/power.h>
+#include <iprt/process.h>
+#include <iprt/semaphore.h>
+#include <iprt/spinlock.h>
+#include <iprt/thread.h>
+#include <iprt/uuid.h>
+#include <iprt/net.h>
+#include <iprt/crc.h>
+#include <iprt/string.h>
+#include <iprt/timer.h>
+#if defined(RT_OS_DARWIN) || defined(RT_OS_SOLARIS) || defined(RT_OS_FREEBSD)
+# include <iprt/rand.h>
+# include <iprt/path.h>
+#endif
+#include <iprt/uint128.h>
+#include <iprt/x86.h>
+
+#include <VBox/param.h>
+#include <VBox/log.h>
+#include <VBox/err.h>
+
+#if defined(RT_OS_SOLARIS) || defined(RT_OS_DARWIN)
+# include "dtrace/SUPDrv.h"
+#else
+/* ... */
+#endif
+
+
+/*********************************************************************************************************************************
+* Defined Constants And Macros *
+*********************************************************************************************************************************/
+/** The frequency by which we recalculate the u32UpdateHz and
+ * u32UpdateIntervalNS GIP members. The value must be a power of 2.
+ *
+ * Warning: Bumping this too high might overflow u32UpdateIntervalNS.
+ */
+#define GIP_UPDATEHZ_RECALC_FREQ 0x800
+
+/** A reserved TSC value used for synchronization as well as measurement of
+ * TSC deltas. */
+#define GIP_TSC_DELTA_RSVD UINT64_MAX
+/** The number of TSC delta measurement loops in total (includes primer and
+ * read-time loops). */
+#define GIP_TSC_DELTA_LOOPS 96
+/** The number of cache primer loops. */
+#define GIP_TSC_DELTA_PRIMER_LOOPS 4
+/** The number of loops until we keep computing the minumum read time. */
+#define GIP_TSC_DELTA_READ_TIME_LOOPS 24
+
+/** The TSC frequency refinement period in seconds.
+ * The timer fires after 200ms, then every second, this value just says when
+ * to stop it after that. */
+#define GIP_TSC_REFINE_PERIOD_IN_SECS 12
+/** The TSC-delta threshold for the SUPGIPUSETSCDELTA_PRACTICALLY_ZERO rating */
+#define GIP_TSC_DELTA_THRESHOLD_PRACTICALLY_ZERO 32
+/** The TSC-delta threshold for the SUPGIPUSETSCDELTA_ROUGHLY_ZERO rating */
+#define GIP_TSC_DELTA_THRESHOLD_ROUGHLY_ZERO 448
+/** The TSC delta value for the initial GIP master - 0 in regular builds.
+ * To test the delta code this can be set to a non-zero value. */
+#if 0
+# define GIP_TSC_DELTA_INITIAL_MASTER_VALUE INT64_C(170139095182512) /* 0x00009abd9854acb0 */
+#else
+# define GIP_TSC_DELTA_INITIAL_MASTER_VALUE INT64_C(0)
+#endif
+
+AssertCompile(GIP_TSC_DELTA_PRIMER_LOOPS < GIP_TSC_DELTA_READ_TIME_LOOPS);
+AssertCompile(GIP_TSC_DELTA_PRIMER_LOOPS + GIP_TSC_DELTA_READ_TIME_LOOPS < GIP_TSC_DELTA_LOOPS);
+
+/** @def VBOX_SVN_REV
+ * The makefile should define this if it can. */
+#ifndef VBOX_SVN_REV
+# define VBOX_SVN_REV 0
+#endif
+
+#if 0 /* Don't start the GIP timers. Useful when debugging the IPRT timer code. */
+# define DO_NOT_START_GIP
+#endif
+
+
+/*********************************************************************************************************************************
+* Internal Functions *
+*********************************************************************************************************************************/
+static DECLCALLBACK(void) supdrvGipSyncAndInvariantTimer(PRTTIMER pTimer, void *pvUser, uint64_t iTick);
+static DECLCALLBACK(void) supdrvGipAsyncTimer(PRTTIMER pTimer, void *pvUser, uint64_t iTick);
+static int supdrvGipSetFlags(PSUPDRVDEVEXT pDevExt, PSUPDRVSESSION pSession, uint32_t fOrMask, uint32_t fAndMask);
+static void supdrvGipInitCpu(PSUPGLOBALINFOPAGE pGip, PSUPGIPCPU pCpu, uint64_t u64NanoTS, uint64_t uCpuHz);
+static void supdrvTscResetSamples(PSUPDRVDEVEXT pDevExt, bool fClearDeltas);
+#ifdef SUPDRV_USE_TSC_DELTA_THREAD
+static int supdrvTscDeltaThreadInit(PSUPDRVDEVEXT pDevExt);
+static void supdrvTscDeltaTerm(PSUPDRVDEVEXT pDevExt);
+static void supdrvTscDeltaThreadStartMeasurement(PSUPDRVDEVEXT pDevExt, bool fForceAll);
+#else
+static int supdrvTscMeasureInitialDeltas(PSUPDRVDEVEXT pDevExt);
+static int supdrvTscMeasureDeltaOne(PSUPDRVDEVEXT pDevExt, uint32_t idxWorker);
+#endif
+
+
+/*********************************************************************************************************************************
+* Global Variables *
+*********************************************************************************************************************************/
+DECLEXPORT(PSUPGLOBALINFOPAGE) g_pSUPGlobalInfoPage = NULL;
+SUPR0_EXPORT_SYMBOL(g_pSUPGlobalInfoPage);
+
+
+
+/*
+ *
+ * Misc Common GIP Code
+ * Misc Common GIP Code
+ * Misc Common GIP Code
+ *
+ *
+ */
+
+
+/**
+ * Finds the GIP CPU index corresponding to @a idCpu.
+ *
+ * @returns GIP CPU array index, UINT32_MAX if not found.
+ * @param pGip The GIP.
+ * @param idCpu The CPU ID.
+ */
+static uint32_t supdrvGipFindCpuIndexForCpuId(PSUPGLOBALINFOPAGE pGip, RTCPUID idCpu)
+{
+ uint32_t i;
+ for (i = 0; i < pGip->cCpus; i++)
+ if (pGip->aCPUs[i].idCpu == idCpu)
+ return i;
+ return UINT32_MAX;
+}
+
+
+/**
+ * Gets the APIC ID using the best available method.
+ *
+ * @returns APIC ID.
+ * @param pGip The GIP, for SUPGIPGETCPU_XXX.
+ */
+DECLINLINE(uint32_t) supdrvGipGetApicId(PSUPGLOBALINFOPAGE pGip)
+{
+ if (pGip->fGetGipCpu & SUPGIPGETCPU_APIC_ID_EXT_0B)
+ return ASMGetApicIdExt0B();
+ if (pGip->fGetGipCpu & SUPGIPGETCPU_APIC_ID_EXT_8000001E)
+ return ASMGetApicIdExt8000001E();
+ return ASMGetApicId();
+}
+
+
+/**
+ * Gets the APIC ID using the best available method, slow version.
+ */
+static uint32_t supdrvGipGetApicIdSlow(void)
+{
+ uint32_t const idApic = ASMGetApicId();
+
+ /* The Intel CPU topology leaf: */
+ uint32_t uOther = ASMCpuId_EAX(0);
+ if (uOther >= UINT32_C(0xb) && RTX86IsValidStdRange(uOther))
+ {
+ uint32_t uEax = 0;
+ uint32_t uEbx = 0;
+ uint32_t uEcx = 0;
+ uint32_t uEdx = 0;
+#if defined(RT_OS_LINUX) || defined(RT_OS_FREEBSD)
+ ASMCpuId_Idx_ECX(0xb, 0, &uEax, &uEbx, &uEcx, &uEdx);
+#else
+ ASMCpuIdExSlow(0xb, 0, 0, 0, &uEax, &uEbx, &uEcx, &uEdx);
+#endif
+ if ((uEcx >> 8) != 0) /* level type != invalid */
+ {
+ if ((uEdx & 0xff) == idApic)
+ return uEdx;
+ AssertMsgFailed(("ASMGetApicIdExt0B=>%#x idApic=%#x\n", uEdx, idApic));
+ }
+ }
+
+ /* The AMD leaf: */
+ uOther = ASMCpuId_EAX(UINT32_C(0x80000000));
+ if (uOther >= UINT32_C(0x8000001e) && RTX86IsValidExtRange(uOther))
+ {
+ uOther = ASMGetApicIdExt8000001E();
+ if ((uOther & 0xff) == idApic)
+ return uOther;
+ AssertMsgFailed(("ASMGetApicIdExt8000001E=>%#x idApic=%#x\n", uOther, idApic));
+ }
+ return idApic;
+}
+
+
+/*
+ *
+ * GIP Mapping and Unmapping Related Code.
+ * GIP Mapping and Unmapping Related Code.
+ * GIP Mapping and Unmapping Related Code.
+ *
+ *
+ */
+
+
+/**
+ * (Re-)initializes the per-cpu structure prior to starting or resuming the GIP
+ * updating.
+ *
+ * @param pGipCpu The per CPU structure for this CPU.
+ * @param u64NanoTS The current time.
+ */
+static void supdrvGipReInitCpu(PSUPGIPCPU pGipCpu, uint64_t u64NanoTS)
+{
+ /*
+ * Here we don't really care about applying the TSC delta. The re-initialization of this
+ * value is not relevant especially while (re)starting the GIP as the first few ones will
+ * be ignored anyway, see supdrvGipDoUpdateCpu().
+ */
+ pGipCpu->u64TSC = ASMReadTSC() - pGipCpu->u32UpdateIntervalTSC;
+ pGipCpu->u64NanoTS = u64NanoTS;
+}
+
+
+/**
+ * Set the current TSC and NanoTS value for the CPU.
+ *
+ * @param idCpu The CPU ID. Unused - we have to use the APIC ID.
+ * @param pvUser1 Pointer to the ring-0 GIP mapping.
+ * @param pvUser2 Pointer to the variable holding the current time.
+ */
+static DECLCALLBACK(void) supdrvGipReInitCpuCallback(RTCPUID idCpu, void *pvUser1, void *pvUser2)
+{
+ PSUPGLOBALINFOPAGE pGip = (PSUPGLOBALINFOPAGE)pvUser1;
+ uint32_t const idApic = supdrvGipGetApicId(pGip);
+ if (idApic < RT_ELEMENTS(pGip->aiCpuFromApicId))
+ {
+ unsigned const iCpu = pGip->aiCpuFromApicId[idApic];
+
+ if (RT_LIKELY(iCpu < pGip->cCpus && pGip->aCPUs[iCpu].idCpu == idCpu))
+ supdrvGipReInitCpu(&pGip->aCPUs[iCpu], *(uint64_t *)pvUser2);
+ else
+ LogRelMax(64, ("supdrvGipReInitCpuCallback: iCpu=%#x out of bounds (%#zx, idApic=%#x)\n",
+ iCpu, RT_ELEMENTS(pGip->aiCpuFromApicId), idApic));
+ }
+ else
+ LogRelMax(64, ("supdrvGipReInitCpuCallback: idApic=%#x out of bounds (%#zx)\n",
+ idApic, RT_ELEMENTS(pGip->aiCpuFromApicId)));
+
+ NOREF(pvUser2);
+}
+
+
+/**
+ * State structure for supdrvGipDetectGetGipCpuCallback.
+ */
+typedef struct SUPDRVGIPDETECTGETCPU
+{
+ /** Bitmap of APIC IDs that has been seen (initialized to zero).
+ * Used to detect duplicate APIC IDs (paranoia). */
+ uint8_t volatile bmApicId[4096 / 8];
+ /** Mask of supported GIP CPU getter methods (SUPGIPGETCPU_XXX) (all bits set
+ * initially). The callback clears the methods not detected. */
+ uint32_t volatile fSupported;
+ /** The first callback detecting any kind of range issues (initialized to
+ * NIL_RTCPUID). */
+ RTCPUID volatile idCpuProblem;
+} SUPDRVGIPDETECTGETCPU;
+/** Pointer to state structure for supdrvGipDetectGetGipCpuCallback. */
+typedef SUPDRVGIPDETECTGETCPU *PSUPDRVGIPDETECTGETCPU;
+
+
+/**
+ * Checks for alternative ways of getting the CPU ID.
+ *
+ * This also checks the APIC ID, CPU ID and CPU set index values against the
+ * GIP tables.
+ *
+ * @param idCpu The CPU ID. Unused - we have to use the APIC ID.
+ * @param pvUser1 Pointer to the state structure.
+ * @param pvUser2 Pointer to the GIP.
+ */
+static DECLCALLBACK(void) supdrvGipDetectGetGipCpuCallback(RTCPUID idCpu, void *pvUser1, void *pvUser2)
+{
+ PSUPDRVGIPDETECTGETCPU pState = (PSUPDRVGIPDETECTGETCPU)pvUser1;
+ PSUPGLOBALINFOPAGE pGip = (PSUPGLOBALINFOPAGE)pvUser2;
+ uint32_t fSupported = 0;
+ uint32_t idApic;
+ uint32_t uEax, uEbx, uEcx, uEdx;
+ int iCpuSet;
+ NOREF(pGip);
+
+ AssertMsg(idCpu == RTMpCpuId(), ("idCpu=%#x RTMpCpuId()=%#x\n", idCpu, RTMpCpuId())); /* paranoia^3 */
+
+ /*
+ * Check that the CPU ID and CPU set index are interchangable.
+ */
+ iCpuSet = RTMpCpuIdToSetIndex(idCpu);
+ if ((RTCPUID)iCpuSet == idCpu)
+ {
+ AssertCompile(RT_IS_POWER_OF_TWO(RTCPUSET_MAX_CPUS));
+ if ( iCpuSet >= 0
+ && iCpuSet < RTCPUSET_MAX_CPUS
+ && RT_IS_POWER_OF_TWO(RTCPUSET_MAX_CPUS))
+ {
+ PSUPGIPCPU pGipCpu = SUPGetGipCpuBySetIndex(pGip, iCpuSet);
+
+ /*
+ * Check whether the IDTR.LIMIT contains a CPU number.
+ */
+#ifdef RT_ARCH_X86
+ uint16_t const cbIdt = sizeof(X86DESC64SYSTEM) * 256;
+#else
+ uint16_t const cbIdt = sizeof(X86DESCGATE) * 256;
+#endif
+ RTIDTR Idtr;
+ ASMGetIDTR(&Idtr);
+ if (Idtr.cbIdt >= cbIdt)
+ {
+ uint32_t uTmp = Idtr.cbIdt - cbIdt;
+ uTmp &= RTCPUSET_MAX_CPUS - 1;
+ if (uTmp == idCpu)
+ {
+ RTIDTR Idtr2;
+ ASMGetIDTR(&Idtr2);
+ if (Idtr2.cbIdt == Idtr.cbIdt)
+ fSupported |= SUPGIPGETCPU_IDTR_LIMIT_MASK_MAX_SET_CPUS;
+ }
+ }
+
+ /*
+ * Check whether RDTSCP is an option.
+ */
+ if (ASMHasCpuId())
+ {
+ if ( RTX86IsValidExtRange(ASMCpuId_EAX(UINT32_C(0x80000000)))
+ && (ASMCpuId_EDX(UINT32_C(0x80000001)) & X86_CPUID_EXT_FEATURE_EDX_RDTSCP) )
+ {
+ uint32_t uAux;
+ ASMReadTscWithAux(&uAux);
+ if ((uAux & (RTCPUSET_MAX_CPUS - 1)) == idCpu)
+ {
+ ASMNopPause();
+ ASMReadTscWithAux(&uAux);
+ if ((uAux & (RTCPUSET_MAX_CPUS - 1)) == idCpu)
+ fSupported |= SUPGIPGETCPU_RDTSCP_MASK_MAX_SET_CPUS;
+ }
+
+ if (pGipCpu)
+ {
+ uint32_t const uGroupedAux = (uint8_t)pGipCpu->iCpuGroupMember | ((uint32_t)pGipCpu->iCpuGroup << 8);
+ if ( (uAux & UINT16_MAX) == uGroupedAux
+ && pGipCpu->iCpuGroupMember <= UINT8_MAX)
+ {
+ ASMNopPause();
+ ASMReadTscWithAux(&uAux);
+ if ((uAux & UINT16_MAX) == uGroupedAux)
+ fSupported |= SUPGIPGETCPU_RDTSCP_GROUP_IN_CH_NUMBER_IN_CL;
+ }
+ }
+ }
+ }
+ }
+ }
+
+ /*
+ * Check for extended APIC ID methods.
+ */
+ idApic = UINT32_MAX;
+ uEax = ASMCpuId_EAX(0);
+ if (uEax >= UINT32_C(0xb) && RTX86IsValidStdRange(uEax))
+ {
+#if defined(RT_OS_LINUX) || defined(RT_OS_FREEBSD)
+ ASMCpuId_Idx_ECX(0xb, 0, &uEax, &uEbx, &uEcx, &uEdx);
+#else
+ ASMCpuIdExSlow(0xb, 0, 0, 0, &uEax, &uEbx, &uEcx, &uEdx);
+#endif
+ if ((uEcx >> 8) != 0) /* level type != invalid */
+ {
+ if (RT_LIKELY( uEdx < RT_ELEMENTS(pGip->aiCpuFromApicId)
+ && !ASMBitTest(pState->bmApicId, uEdx)))
+ {
+ if (uEdx == ASMGetApicIdExt0B())
+ {
+ idApic = uEdx;
+ fSupported |= SUPGIPGETCPU_APIC_ID_EXT_0B;
+ }
+ else
+ AssertMsgFailed(("%#x vs %#x\n", uEdx, ASMGetApicIdExt0B()));
+ }
+ }
+ }
+
+ uEax = ASMCpuId_EAX(UINT32_C(0x80000000));
+ if (uEax >= UINT32_C(0x8000001e) && RTX86IsValidExtRange(uEax))
+ {
+#if defined(RT_OS_LINUX) || defined(RT_OS_FREEBSD)
+ ASMCpuId_Idx_ECX(UINT32_C(0x8000001e), 0, &uEax, &uEbx, &uEcx, &uEdx);
+#else
+ ASMCpuIdExSlow(UINT32_C(0x8000001e), 0, 0, 0, &uEax, &uEbx, &uEcx, &uEdx);
+#endif
+ if (uEax || uEbx || uEcx || uEdx)
+ {
+ if (RT_LIKELY( uEax < RT_ELEMENTS(pGip->aiCpuFromApicId)
+ && ( idApic == UINT32_MAX
+ || idApic == uEax)
+ && !ASMBitTest(pState->bmApicId, uEax)))
+ {
+ if (uEax == ASMGetApicIdExt8000001E())
+ {
+ idApic = uEax;
+ fSupported |= SUPGIPGETCPU_APIC_ID_EXT_8000001E;
+ }
+ else
+ AssertMsgFailed(("%#x vs %#x\n", uEax, ASMGetApicIdExt8000001E()));
+ }
+ }
+ }
+
+ /*
+ * Check that the APIC ID is unique.
+ */
+ uEax = ASMGetApicId();
+ if (RT_LIKELY( uEax < RT_ELEMENTS(pGip->aiCpuFromApicId)
+ && ( idApic == UINT32_MAX
+ || idApic == uEax)
+ && !ASMAtomicBitTestAndSet(pState->bmApicId, uEax)))
+ {
+ idApic = uEax;
+ fSupported |= SUPGIPGETCPU_APIC_ID;
+ }
+ else if ( idApic == UINT32_MAX
+ || idApic >= RT_ELEMENTS(pGip->aiCpuFromApicId) /* parnaoia */
+ || ASMAtomicBitTestAndSet(pState->bmApicId, idApic))
+ {
+ AssertCompile(sizeof(pState->bmApicId) * 8 == RT_ELEMENTS(pGip->aiCpuFromApicId));
+ ASMAtomicCmpXchgU32(&pState->idCpuProblem, idCpu, NIL_RTCPUID);
+ LogRel(("supdrvGipDetectGetGipCpuCallback: idCpu=%#x iCpuSet=%d idApic=%#x/%#x - duplicate APIC ID.\n",
+ idCpu, iCpuSet, uEax, idApic));
+ }
+
+ /*
+ * Check that the iCpuSet is within the expected range.
+ */
+ if (RT_UNLIKELY( iCpuSet < 0
+ || (unsigned)iCpuSet >= RTCPUSET_MAX_CPUS
+ || (unsigned)iCpuSet >= RT_ELEMENTS(pGip->aiCpuFromCpuSetIdx)))
+ {
+ ASMAtomicCmpXchgU32(&pState->idCpuProblem, idCpu, NIL_RTCPUID);
+ LogRel(("supdrvGipDetectGetGipCpuCallback: idCpu=%#x iCpuSet=%d idApic=%#x - CPU set index is out of range.\n",
+ idCpu, iCpuSet, idApic));
+ }
+ else
+ {
+ RTCPUID idCpu2 = RTMpCpuIdFromSetIndex(iCpuSet);
+ if (RT_UNLIKELY(idCpu2 != idCpu))
+ {
+ ASMAtomicCmpXchgU32(&pState->idCpuProblem, idCpu, NIL_RTCPUID);
+ LogRel(("supdrvGipDetectGetGipCpuCallback: idCpu=%#x iCpuSet=%d idApic=%#x - CPU id/index roundtrip problem: %#x\n",
+ idCpu, iCpuSet, idApic, idCpu2));
+ }
+ }
+
+ /*
+ * Update the supported feature mask before we return.
+ */
+ ASMAtomicAndU32(&pState->fSupported, fSupported);
+
+ NOREF(pvUser2);
+}
+
+
+/**
+ * Increase the timer freqency on hosts where this is possible (NT).
+ *
+ * The idea is that more interrupts is better for us... Also, it's better than
+ * we increase the timer frequence, because we might end up getting inaccurate
+ * callbacks if someone else does it.
+ *
+ * @param pDevExt Sets u32SystemTimerGranularityGrant if increased.
+ */
+static void supdrvGipRequestHigherTimerFrequencyFromSystem(PSUPDRVDEVEXT pDevExt)
+{
+ if (pDevExt->u32SystemTimerGranularityGrant == 0)
+ {
+ uint32_t u32SystemResolution;
+ if ( RT_SUCCESS_NP(RTTimerRequestSystemGranularity( 976563 /* 1024 HZ */, &u32SystemResolution))
+ || RT_SUCCESS_NP(RTTimerRequestSystemGranularity( 1000000 /* 1000 HZ */, &u32SystemResolution))
+ || RT_SUCCESS_NP(RTTimerRequestSystemGranularity( 1953125 /* 512 HZ */, &u32SystemResolution))
+ || RT_SUCCESS_NP(RTTimerRequestSystemGranularity( 2000000 /* 500 HZ */, &u32SystemResolution))
+ )
+ {
+#if 0 /* def VBOX_STRICT - this is somehow triggers bogus assertions on windows 10 */
+ uint32_t u32After = RTTimerGetSystemGranularity();
+ AssertMsg(u32After <= u32SystemResolution, ("u32After=%u u32SystemResolution=%u\n", u32After, u32SystemResolution));
+#endif
+ pDevExt->u32SystemTimerGranularityGrant = u32SystemResolution;
+ }
+ }
+}
+
+
+/**
+ * Undoes supdrvGipRequestHigherTimerFrequencyFromSystem.
+ *
+ * @param pDevExt Clears u32SystemTimerGranularityGrant.
+ */
+static void supdrvGipReleaseHigherTimerFrequencyFromSystem(PSUPDRVDEVEXT pDevExt)
+{
+ if (pDevExt->u32SystemTimerGranularityGrant)
+ {
+ int rc2 = RTTimerReleaseSystemGranularity(pDevExt->u32SystemTimerGranularityGrant);
+ AssertRC(rc2);
+ pDevExt->u32SystemTimerGranularityGrant = 0;
+ }
+}
+
+
+/**
+ * Maps the GIP into userspace and/or get the physical address of the GIP.
+ *
+ * @returns IPRT status code.
+ * @param pSession Session to which the GIP mapping should belong.
+ * @param ppGipR3 Where to store the address of the ring-3 mapping. (optional)
+ * @param pHCPhysGip Where to store the physical address. (optional)
+ *
+ * @remark There is no reference counting on the mapping, so one call to this function
+ * count globally as one reference. One call to SUPR0GipUnmap() is will unmap GIP
+ * and remove the session as a GIP user.
+ */
+SUPR0DECL(int) SUPR0GipMap(PSUPDRVSESSION pSession, PRTR3PTR ppGipR3, PRTHCPHYS pHCPhysGip)
+{
+ int rc;
+ PSUPDRVDEVEXT pDevExt = pSession->pDevExt;
+ RTR3PTR pGipR3 = NIL_RTR3PTR;
+ RTHCPHYS HCPhys = NIL_RTHCPHYS;
+ LogFlow(("SUPR0GipMap: pSession=%p ppGipR3=%p pHCPhysGip=%p\n", pSession, ppGipR3, pHCPhysGip));
+
+ /*
+ * Validate
+ */
+ AssertReturn(SUP_IS_SESSION_VALID(pSession), VERR_INVALID_PARAMETER);
+ AssertPtrNullReturn(ppGipR3, VERR_INVALID_POINTER);
+ AssertPtrNullReturn(pHCPhysGip, VERR_INVALID_POINTER);
+
+#ifdef SUPDRV_USE_MUTEX_FOR_GIP
+ RTSemMutexRequest(pDevExt->mtxGip, RT_INDEFINITE_WAIT);
+#else
+ RTSemFastMutexRequest(pDevExt->mtxGip);
+#endif
+ if (pDevExt->pGip)
+ {
+ /*
+ * Map it?
+ */
+ rc = VINF_SUCCESS;
+ if (ppGipR3)
+ {
+ if (pSession->GipMapObjR3 == NIL_RTR0MEMOBJ)
+ rc = RTR0MemObjMapUser(&pSession->GipMapObjR3, pDevExt->GipMemObj, (RTR3PTR)-1, 0,
+ RTMEM_PROT_READ, NIL_RTR0PROCESS);
+ if (RT_SUCCESS(rc))
+ pGipR3 = RTR0MemObjAddressR3(pSession->GipMapObjR3);
+ }
+
+ /*
+ * Get physical address.
+ */
+ if (pHCPhysGip && RT_SUCCESS(rc))
+ HCPhys = pDevExt->HCPhysGip;
+
+ /*
+ * Reference globally.
+ */
+ if (!pSession->fGipReferenced && RT_SUCCESS(rc))
+ {
+ pSession->fGipReferenced = 1;
+ pDevExt->cGipUsers++;
+ if (pDevExt->cGipUsers == 1)
+ {
+ PSUPGLOBALINFOPAGE pGipR0 = pDevExt->pGip;
+ uint64_t u64NanoTS;
+
+ /*
+ * GIP starts/resumes updating again. On windows we bump the
+ * host timer frequency to make sure we don't get stuck in guest
+ * mode and to get better timer (and possibly clock) accuracy.
+ */
+ LogFlow(("SUPR0GipMap: Resumes GIP updating\n"));
+
+ supdrvGipRequestHigherTimerFrequencyFromSystem(pDevExt);
+
+ /*
+ * document me
+ */
+ if (pGipR0->aCPUs[0].u32TransactionId != 2 /* not the first time */)
+ {
+ unsigned i;
+ for (i = 0; i < pGipR0->cCpus; i++)
+ ASMAtomicUoWriteU32(&pGipR0->aCPUs[i].u32TransactionId,
+ (pGipR0->aCPUs[i].u32TransactionId + GIP_UPDATEHZ_RECALC_FREQ * 2)
+ & ~(GIP_UPDATEHZ_RECALC_FREQ * 2 - 1));
+ ASMAtomicWriteU64(&pGipR0->u64NanoTSLastUpdateHz, 0);
+ }
+
+ /*
+ * document me
+ */
+ u64NanoTS = RTTimeSystemNanoTS() - pGipR0->u32UpdateIntervalNS;
+ if ( pGipR0->u32Mode == SUPGIPMODE_INVARIANT_TSC
+ || pGipR0->u32Mode == SUPGIPMODE_SYNC_TSC
+ || RTMpGetOnlineCount() == 1)
+ supdrvGipReInitCpu(&pGipR0->aCPUs[0], u64NanoTS);
+ else
+ RTMpOnAll(supdrvGipReInitCpuCallback, pGipR0, &u64NanoTS);
+
+ /*
+ * Detect alternative ways to figure the CPU ID in ring-3 and
+ * raw-mode context. Check the sanity of the APIC IDs, CPU IDs,
+ * and CPU set indexes while we're at it.
+ */
+ if (RT_SUCCESS(rc))
+ {
+ PSUPDRVGIPDETECTGETCPU pDetectState = (PSUPDRVGIPDETECTGETCPU)RTMemTmpAllocZ(sizeof(*pDetectState));
+ if (pDetectState)
+ {
+ pDetectState->fSupported = UINT32_MAX;
+ pDetectState->idCpuProblem = NIL_RTCPUID;
+ rc = RTMpOnAll(supdrvGipDetectGetGipCpuCallback, pDetectState, pGipR0);
+ if (pDetectState->idCpuProblem == NIL_RTCPUID)
+ {
+ if ( pDetectState->fSupported != UINT32_MAX
+ && pDetectState->fSupported != 0)
+ {
+ if (pGipR0->fGetGipCpu != pDetectState->fSupported)
+ {
+ pGipR0->fGetGipCpu = pDetectState->fSupported;
+ LogRel(("SUPR0GipMap: fGetGipCpu=%#x\n", pDetectState->fSupported));
+ }
+ }
+ else
+ {
+ LogRel(("SUPR0GipMap: No supported ways of getting the APIC ID or CPU number in ring-3! (%#x)\n",
+ pDetectState->fSupported));
+ rc = VERR_UNSUPPORTED_CPU;
+ }
+ }
+ else
+ {
+ LogRel(("SUPR0GipMap: APIC ID, CPU ID or CPU set index problem detected on CPU #%u (%#x)!\n",
+ pDetectState->idCpuProblem, pDetectState->idCpuProblem));
+ rc = VERR_INVALID_CPU_ID;
+ }
+ RTMemTmpFree(pDetectState);
+ }
+ else
+ rc = VERR_NO_TMP_MEMORY;
+ }
+
+ /*
+ * Start the GIP timer if all is well..
+ */
+ if (RT_SUCCESS(rc))
+ {
+#ifndef DO_NOT_START_GIP
+ rc = RTTimerStart(pDevExt->pGipTimer, 0 /* fire ASAP */); AssertRC(rc);
+#endif
+ rc = VINF_SUCCESS;
+ }
+
+ /*
+ * Bail out on error.
+ */
+ if (RT_FAILURE(rc))
+ {
+ LogRel(("SUPR0GipMap: failed rc=%Rrc\n", rc));
+ pDevExt->cGipUsers = 0;
+ pSession->fGipReferenced = 0;
+ if (pSession->GipMapObjR3 != NIL_RTR0MEMOBJ)
+ {
+ int rc2 = RTR0MemObjFree(pSession->GipMapObjR3, false); AssertRC(rc2);
+ if (RT_SUCCESS(rc2))
+ pSession->GipMapObjR3 = NIL_RTR0MEMOBJ;
+ }
+ HCPhys = NIL_RTHCPHYS;
+ pGipR3 = NIL_RTR3PTR;
+ }
+ }
+ }
+ }
+ else
+ {
+ rc = VERR_GENERAL_FAILURE;
+ Log(("SUPR0GipMap: GIP is not available!\n"));
+ }
+#ifdef SUPDRV_USE_MUTEX_FOR_GIP
+ RTSemMutexRelease(pDevExt->mtxGip);
+#else
+ RTSemFastMutexRelease(pDevExt->mtxGip);
+#endif
+
+ /*
+ * Write returns.
+ */
+ if (pHCPhysGip)
+ *pHCPhysGip = HCPhys;
+ if (ppGipR3)
+ *ppGipR3 = pGipR3;
+
+#ifdef DEBUG_DARWIN_GIP
+ OSDBGPRINT(("SUPR0GipMap: returns %d *pHCPhysGip=%lx pGipR3=%p\n", rc, (unsigned long)HCPhys, (void *)pGipR3));
+#else
+ LogFlow(( "SUPR0GipMap: returns %d *pHCPhysGip=%lx pGipR3=%p\n", rc, (unsigned long)HCPhys, (void *)pGipR3));
+#endif
+ return rc;
+}
+SUPR0_EXPORT_SYMBOL(SUPR0GipMap);
+
+
+/**
+ * Unmaps any user mapping of the GIP and terminates all GIP access
+ * from this session.
+ *
+ * @returns IPRT status code.
+ * @param pSession Session to which the GIP mapping should belong.
+ */
+SUPR0DECL(int) SUPR0GipUnmap(PSUPDRVSESSION pSession)
+{
+ int rc = VINF_SUCCESS;
+ PSUPDRVDEVEXT pDevExt = pSession->pDevExt;
+#ifdef DEBUG_DARWIN_GIP
+ OSDBGPRINT(("SUPR0GipUnmap: pSession=%p pGip=%p GipMapObjR3=%p\n",
+ pSession,
+ pSession->GipMapObjR3 != NIL_RTR0MEMOBJ ? RTR0MemObjAddress(pSession->GipMapObjR3) : NULL,
+ pSession->GipMapObjR3));
+#else
+ LogFlow(("SUPR0GipUnmap: pSession=%p\n", pSession));
+#endif
+ AssertReturn(SUP_IS_SESSION_VALID(pSession), VERR_INVALID_PARAMETER);
+
+#ifdef SUPDRV_USE_MUTEX_FOR_GIP
+ RTSemMutexRequest(pDevExt->mtxGip, RT_INDEFINITE_WAIT);
+#else
+ RTSemFastMutexRequest(pDevExt->mtxGip);
+#endif
+
+ /*
+ * GIP test-mode session?
+ */
+ if ( pSession->fGipTestMode
+ && pDevExt->pGip)
+ {
+ supdrvGipSetFlags(pDevExt, pSession, 0, ~SUPGIP_FLAGS_TESTING_ENABLE);
+ Assert(!pSession->fGipTestMode);
+ }
+
+ /*
+ * Unmap anything?
+ */
+ if (pSession->GipMapObjR3 != NIL_RTR0MEMOBJ)
+ {
+ rc = RTR0MemObjFree(pSession->GipMapObjR3, false);
+ AssertRC(rc);
+ if (RT_SUCCESS(rc))
+ pSession->GipMapObjR3 = NIL_RTR0MEMOBJ;
+ }
+
+ /*
+ * Dereference global GIP.
+ */
+ if (pSession->fGipReferenced && !rc)
+ {
+ pSession->fGipReferenced = 0;
+ if ( pDevExt->cGipUsers > 0
+ && !--pDevExt->cGipUsers)
+ {
+ LogFlow(("SUPR0GipUnmap: Suspends GIP updating\n"));
+#ifndef DO_NOT_START_GIP
+ rc = RTTimerStop(pDevExt->pGipTimer); AssertRC(rc); rc = VINF_SUCCESS;
+#endif
+ supdrvGipReleaseHigherTimerFrequencyFromSystem(pDevExt);
+ }
+ }
+
+#ifdef SUPDRV_USE_MUTEX_FOR_GIP
+ RTSemMutexRelease(pDevExt->mtxGip);
+#else
+ RTSemFastMutexRelease(pDevExt->mtxGip);
+#endif
+
+ return rc;
+}
+SUPR0_EXPORT_SYMBOL(SUPR0GipUnmap);
+
+
+/**
+ * Gets the GIP pointer.
+ *
+ * @returns Pointer to the GIP or NULL.
+ */
+SUPDECL(PSUPGLOBALINFOPAGE) SUPGetGIP(void)
+{
+ return g_pSUPGlobalInfoPage;
+}
+
+
+
+
+
+/*
+ *
+ *
+ * GIP Initialization, Termination and CPU Offline / Online Related Code.
+ * GIP Initialization, Termination and CPU Offline / Online Related Code.
+ * GIP Initialization, Termination and CPU Offline / Online Related Code.
+ *
+ *
+ */
+
+/**
+ * Used by supdrvGipInitRefineInvariantTscFreqTimer and supdrvGipInitMeasureTscFreq
+ * to update the TSC frequency related GIP variables.
+ *
+ * @param pGip The GIP.
+ * @param nsElapsed The number of nanoseconds elapsed.
+ * @param cElapsedTscTicks The corresponding number of TSC ticks.
+ * @param iTick The tick number for debugging.
+ */
+static void supdrvGipInitSetCpuFreq(PSUPGLOBALINFOPAGE pGip, uint64_t nsElapsed, uint64_t cElapsedTscTicks, uint32_t iTick)
+{
+ /*
+ * Calculate the frequency.
+ */
+ uint64_t uCpuHz;
+ if ( cElapsedTscTicks < UINT64_MAX / RT_NS_1SEC
+ && nsElapsed < UINT32_MAX)
+ uCpuHz = ASMMultU64ByU32DivByU32(cElapsedTscTicks, RT_NS_1SEC, (uint32_t)nsElapsed);
+ else
+ {
+ RTUINT128U CpuHz, Tmp, Divisor;
+ CpuHz.s.Lo = CpuHz.s.Hi = 0;
+ RTUInt128MulU64ByU64(&Tmp, cElapsedTscTicks, RT_NS_1SEC_64);
+ RTUInt128Div(&CpuHz, &Tmp, RTUInt128AssignU64(&Divisor, nsElapsed));
+ uCpuHz = CpuHz.s.Lo;
+ }
+
+ /*
+ * Update the GIP.
+ */
+ ASMAtomicWriteU64(&pGip->u64CpuHz, uCpuHz);
+ if (pGip->u32Mode != SUPGIPMODE_ASYNC_TSC)
+ {
+ ASMAtomicWriteU64(&pGip->aCPUs[0].u64CpuHz, uCpuHz);
+
+ /* For inspecting the frequency calcs using tstGIP-2, debugger or similar. */
+ if (iTick + 1 < pGip->cCpus)
+ ASMAtomicWriteU64(&pGip->aCPUs[iTick + 1].u64CpuHz, uCpuHz);
+ }
+}
+
+
+/**
+ * Timer callback function for TSC frequency refinement in invariant GIP mode.
+ *
+ * This is started during driver init and fires once
+ * GIP_TSC_REFINE_PERIOD_IN_SECS seconds later.
+ *
+ * @param pTimer The timer.
+ * @param pvUser Opaque pointer to the device instance data.
+ * @param iTick The timer tick.
+ */
+static DECLCALLBACK(void) supdrvGipInitRefineInvariantTscFreqTimer(PRTTIMER pTimer, void *pvUser, uint64_t iTick)
+{
+ PSUPDRVDEVEXT pDevExt = (PSUPDRVDEVEXT)pvUser;
+ PSUPGLOBALINFOPAGE pGip = pDevExt->pGip;
+ RTCPUID idCpu;
+ uint64_t cNsElapsed;
+ uint64_t cTscTicksElapsed;
+ uint64_t nsNow;
+ uint64_t uTsc;
+ RTCCUINTREG fEFlags;
+
+ /* Paranoia. */
+ AssertReturnVoid(pGip);
+ AssertReturnVoid(pGip->u32Mode == SUPGIPMODE_INVARIANT_TSC);
+
+ /*
+ * If we got a power event, stop the refinement process.
+ */
+ if (pDevExt->fInvTscRefinePowerEvent)
+ {
+ int rc = RTTimerStop(pTimer); AssertRC(rc);
+ return;
+ }
+
+ /*
+ * Read the TSC and time, noting which CPU we are on.
+ *
+ * Don't bother spinning until RTTimeSystemNanoTS changes, since on
+ * systems where it matters we're in a context where we cannot waste that
+ * much time (DPC watchdog, called from clock interrupt).
+ */
+ fEFlags = ASMIntDisableFlags();
+ uTsc = ASMReadTSC();
+ nsNow = RTTimeSystemNanoTS();
+ idCpu = RTMpCpuId();
+ ASMSetFlags(fEFlags);
+
+ cNsElapsed = nsNow - pDevExt->nsStartInvarTscRefine;
+ cTscTicksElapsed = uTsc - pDevExt->uTscStartInvarTscRefine;
+
+ /*
+ * If the above measurement was taken on a different CPU than the one we
+ * started the process on, cTscTicksElapsed will need to be adjusted with
+ * the TSC deltas of both the CPUs.
+ *
+ * We ASSUME that the delta calculation process takes less time than the
+ * TSC frequency refinement timer. If it doesn't, we'll complain and
+ * drop the frequency refinement.
+ *
+ * Note! We cannot entirely trust enmUseTscDelta here because it's
+ * downgraded after each delta calculation.
+ */
+ if ( idCpu != pDevExt->idCpuInvarTscRefine
+ && pGip->enmUseTscDelta > SUPGIPUSETSCDELTA_ZERO_CLAIMED)
+ {
+ uint32_t iStartCpuSet = RTMpCpuIdToSetIndex(pDevExt->idCpuInvarTscRefine);
+ uint32_t iStopCpuSet = RTMpCpuIdToSetIndex(idCpu);
+ uint16_t iStartGipCpu = iStartCpuSet < RT_ELEMENTS(pGip->aiCpuFromCpuSetIdx)
+ ? pGip->aiCpuFromCpuSetIdx[iStartCpuSet] : UINT16_MAX;
+ uint16_t iStopGipCpu = iStopCpuSet < RT_ELEMENTS(pGip->aiCpuFromCpuSetIdx)
+ ? pGip->aiCpuFromCpuSetIdx[iStopCpuSet] : UINT16_MAX;
+ int64_t iStartTscDelta = iStartGipCpu < pGip->cCpus ? pGip->aCPUs[iStartGipCpu].i64TSCDelta : INT64_MAX;
+ int64_t iStopTscDelta = iStopGipCpu < pGip->cCpus ? pGip->aCPUs[iStopGipCpu].i64TSCDelta : INT64_MAX;
+ if (RT_LIKELY(iStartTscDelta != INT64_MAX && iStopTscDelta != INT64_MAX))
+ {
+ if (pGip->enmUseTscDelta > SUPGIPUSETSCDELTA_PRACTICALLY_ZERO)
+ {
+ /* cTscTicksElapsed = (uTsc - iStopTscDelta) - (pDevExt->uTscStartInvarTscRefine - iStartTscDelta); */
+ cTscTicksElapsed += iStartTscDelta - iStopTscDelta;
+ }
+ }
+ /*
+ * Allow 5 times the refinement period to elapse before we give up on the TSC delta
+ * calculations.
+ */
+ else if (cNsElapsed > GIP_TSC_REFINE_PERIOD_IN_SECS * 5 * RT_NS_1SEC_64)
+ {
+ SUPR0Printf("vboxdrv: Failed to refine invariant TSC frequency because deltas are unavailable after %u (%u) seconds\n",
+ (uint32_t)(cNsElapsed / RT_NS_1SEC), GIP_TSC_REFINE_PERIOD_IN_SECS);
+ SUPR0Printf("vboxdrv: start: %u, %u, %#llx stop: %u, %u, %#llx\n",
+ iStartCpuSet, iStartGipCpu, iStartTscDelta, iStopCpuSet, iStopGipCpu, iStopTscDelta);
+ int rc = RTTimerStop(pTimer); AssertRC(rc);
+ return;
+ }
+ }
+
+ /*
+ * Calculate and update the CPU frequency variables in GIP.
+ *
+ * If there is a GIP user already and we've already refined the frequency
+ * a couple of times, don't update it as we want a stable frequency value
+ * for all VMs.
+ */
+ if ( pDevExt->cGipUsers == 0
+ || cNsElapsed < RT_NS_1SEC * 2)
+ {
+ supdrvGipInitSetCpuFreq(pGip, cNsElapsed, cTscTicksElapsed, (uint32_t)iTick);
+
+ /*
+ * Stop the timer once we've reached the defined refinement period.
+ */
+ if (cNsElapsed > GIP_TSC_REFINE_PERIOD_IN_SECS * RT_NS_1SEC_64)
+ {
+ int rc = RTTimerStop(pTimer);
+ AssertRC(rc);
+ }
+ }
+ else
+ {
+ int rc = RTTimerStop(pTimer);
+ AssertRC(rc);
+ }
+}
+
+
+/**
+ * @callback_method_impl{FNRTPOWERNOTIFICATION}
+ */
+static DECLCALLBACK(void) supdrvGipPowerNotificationCallback(RTPOWEREVENT enmEvent, void *pvUser)
+{
+ PSUPDRVDEVEXT pDevExt = (PSUPDRVDEVEXT)pvUser;
+ PSUPGLOBALINFOPAGE pGip = pDevExt->pGip;
+
+ /*
+ * If the TSC frequency refinement timer is running, we need to cancel it so it
+ * doesn't screw up the frequency after a long suspend.
+ *
+ * Recalculate all TSC-deltas on host resume as it may have changed, seen
+ * on Windows 7 running on the Dell Optiplex Intel Core i5-3570.
+ */
+ if (enmEvent == RTPOWEREVENT_RESUME)
+ {
+ ASMAtomicWriteBool(&pDevExt->fInvTscRefinePowerEvent, true);
+ if ( RT_LIKELY(pGip)
+ && pGip->enmUseTscDelta > SUPGIPUSETSCDELTA_ZERO_CLAIMED
+ && !supdrvOSAreCpusOfflinedOnSuspend())
+ {
+#ifdef SUPDRV_USE_TSC_DELTA_THREAD
+ supdrvTscDeltaThreadStartMeasurement(pDevExt, true /* fForceAll */);
+#else
+ RTCpuSetCopy(&pDevExt->TscDeltaCpuSet, &pGip->OnlineCpuSet);
+ supdrvTscMeasureInitialDeltas(pDevExt);
+#endif
+ }
+ }
+ else if (enmEvent == RTPOWEREVENT_SUSPEND)
+ ASMAtomicWriteBool(&pDevExt->fInvTscRefinePowerEvent, true);
+}
+
+
+/**
+ * Start the TSC-frequency refinment timer for the invariant TSC GIP mode.
+ *
+ * We cannot use this in the synchronous and asynchronous tsc GIP modes because
+ * the CPU may change the TSC frequence between now and when the timer fires
+ * (supdrvInitAsyncRefineTscTimer).
+ *
+ * @param pDevExt Pointer to the device instance data.
+ */
+static void supdrvGipInitStartTimerForRefiningInvariantTscFreq(PSUPDRVDEVEXT pDevExt)
+{
+ uint64_t u64NanoTS;
+ RTCCUINTREG fEFlags;
+ int rc;
+
+ /*
+ * Register a power management callback.
+ */
+ pDevExt->fInvTscRefinePowerEvent = false;
+ rc = RTPowerNotificationRegister(supdrvGipPowerNotificationCallback, pDevExt);
+ AssertRC(rc); /* ignore */
+
+ /*
+ * Record the TSC and NanoTS as the starting anchor point for refinement
+ * of the TSC. We try get as close to a clock tick as possible on systems
+ * which does not provide high resolution time.
+ */
+ u64NanoTS = RTTimeSystemNanoTS();
+ while (RTTimeSystemNanoTS() == u64NanoTS)
+ ASMNopPause();
+
+ fEFlags = ASMIntDisableFlags();
+ pDevExt->uTscStartInvarTscRefine = ASMReadTSC();
+ pDevExt->nsStartInvarTscRefine = RTTimeSystemNanoTS();
+ pDevExt->idCpuInvarTscRefine = RTMpCpuId();
+ ASMSetFlags(fEFlags);
+
+ /*
+ * Create a timer that runs on the same CPU so we won't have a depencency
+ * on the TSC-delta and can run in parallel to it. On systems that does not
+ * implement CPU specific timers we'll apply deltas in the timer callback,
+ * just like we do for CPUs going offline.
+ *
+ * The longer the refinement interval the better the accuracy, at least in
+ * theory. If it's too long though, ring-3 may already be starting its
+ * first VMs before we're done. On most systems we will be loading the
+ * support driver during boot and VMs won't be started for a while yet,
+ * it is really only a problem during development (especially with
+ * on-demand driver starting on windows).
+ *
+ * To avoid wasting time doing a long supdrvGipInitMeasureTscFreq() call
+ * to calculate the frequency during driver loading, the timer is set
+ * to fire after 200 ms the first time. It will then reschedule itself
+ * to fire every second until GIP_TSC_REFINE_PERIOD_IN_SECS has been
+ * reached or it notices that there is a user land client with GIP
+ * mapped (we want a stable frequency for all VMs).
+ */
+ rc = RTTimerCreateEx(&pDevExt->pInvarTscRefineTimer, RT_NS_1SEC,
+ RTTIMER_FLAGS_CPU(RTMpCpuIdToSetIndex(pDevExt->idCpuInvarTscRefine)),
+ supdrvGipInitRefineInvariantTscFreqTimer, pDevExt);
+ if (RT_SUCCESS(rc))
+ {
+ rc = RTTimerStart(pDevExt->pInvarTscRefineTimer, 2*RT_NS_100MS);
+ if (RT_SUCCESS(rc))
+ return;
+ RTTimerDestroy(pDevExt->pInvarTscRefineTimer);
+ }
+
+ if (rc == VERR_CPU_OFFLINE || rc == VERR_NOT_SUPPORTED)
+ {
+ rc = RTTimerCreateEx(&pDevExt->pInvarTscRefineTimer, RT_NS_1SEC, RTTIMER_FLAGS_CPU_ANY,
+ supdrvGipInitRefineInvariantTscFreqTimer, pDevExt);
+ if (RT_SUCCESS(rc))
+ {
+ rc = RTTimerStart(pDevExt->pInvarTscRefineTimer, 2*RT_NS_100MS);
+ if (RT_SUCCESS(rc))
+ return;
+ RTTimerDestroy(pDevExt->pInvarTscRefineTimer);
+ }
+ }
+
+ pDevExt->pInvarTscRefineTimer = NULL;
+ OSDBGPRINT(("vboxdrv: Failed to create or start TSC frequency refinement timer: rc=%Rrc\n", rc));
+}
+
+
+/**
+ * @callback_method_impl{PFNRTMPWORKER,
+ * RTMpOnSpecific callback for reading TSC and time on the CPU we started
+ * the measurements on.}
+ */
+static DECLCALLBACK(void) supdrvGipInitReadTscAndNanoTsOnCpu(RTCPUID idCpu, void *pvUser1, void *pvUser2)
+{
+ RTCCUINTREG fEFlags = ASMIntDisableFlags();
+ uint64_t *puTscStop = (uint64_t *)pvUser1;
+ uint64_t *pnsStop = (uint64_t *)pvUser2;
+ RT_NOREF1(idCpu);
+
+ *puTscStop = ASMReadTSC();
+ *pnsStop = RTTimeSystemNanoTS();
+
+ ASMSetFlags(fEFlags);
+}
+
+
+/**
+ * Measures the TSC frequency of the system.
+ *
+ * The TSC frequency can vary on systems which are not reported as invariant.
+ * On such systems the object of this function is to find out what the nominal,
+ * maximum TSC frequency under 'normal' CPU operation.
+ *
+ * @returns VBox status code.
+ * @param pGip Pointer to the GIP.
+ * @param fRough Set if we're doing the rough calculation that the
+ * TSC measuring code needs, where accuracy isn't all
+ * that important (too high is better than too low).
+ * When clear we try for best accuracy that we can
+ * achieve in reasonably short time.
+ */
+static int supdrvGipInitMeasureTscFreq(PSUPGLOBALINFOPAGE pGip, bool fRough)
+{
+ uint32_t nsTimerIncr = RTTimerGetSystemGranularity();
+ int cTriesLeft = fRough ? 4 : 2;
+ while (cTriesLeft-- > 0)
+ {
+ RTCCUINTREG fEFlags;
+ uint64_t nsStart;
+ uint64_t nsStop;
+ uint64_t uTscStart;
+ uint64_t uTscStop;
+ RTCPUID idCpuStart;
+ RTCPUID idCpuStop;
+
+ /*
+ * Synchronize with the host OS clock tick on systems without high
+ * resolution time API (older Windows version for example).
+ */
+ nsStart = RTTimeSystemNanoTS();
+ while (RTTimeSystemNanoTS() == nsStart)
+ ASMNopPause();
+
+ /*
+ * Read the TSC and current time, noting which CPU we're on.
+ */
+ fEFlags = ASMIntDisableFlags();
+ uTscStart = ASMReadTSC();
+ nsStart = RTTimeSystemNanoTS();
+ idCpuStart = RTMpCpuId();
+ ASMSetFlags(fEFlags);
+
+ /*
+ * Delay for a while.
+ */
+ if (pGip->u32Mode == SUPGIPMODE_INVARIANT_TSC)
+ {
+ /*
+ * Sleep-wait since the TSC frequency is constant, it eases host load.
+ * Shorter interval produces more variance in the frequency (esp. Windows).
+ */
+ uint64_t msElapsed = 0;
+ uint64_t msDelay = ( ((fRough ? 16 : 200) * RT_NS_1MS + nsTimerIncr - 1) / nsTimerIncr * nsTimerIncr - RT_NS_100US )
+ / RT_NS_1MS;
+ do
+ {
+ RTThreadSleep((RTMSINTERVAL)(msDelay - msElapsed));
+ nsStop = RTTimeSystemNanoTS();
+ msElapsed = (nsStop - nsStart) / RT_NS_1MS;
+ } while (msElapsed < msDelay);
+
+ while (RTTimeSystemNanoTS() == nsStop)
+ ASMNopPause();
+ }
+ else
+ {
+ /*
+ * Busy-wait keeping the frequency up.
+ */
+ do
+ {
+ ASMNopPause();
+ nsStop = RTTimeSystemNanoTS();
+ } while (nsStop - nsStart < RT_NS_100MS);
+ }
+
+ /*
+ * Read the TSC and time again.
+ */
+ fEFlags = ASMIntDisableFlags();
+ uTscStop = ASMReadTSC();
+ nsStop = RTTimeSystemNanoTS();
+ idCpuStop = RTMpCpuId();
+ ASMSetFlags(fEFlags);
+
+ /*
+ * If the CPU changes, things get a bit complicated and what we
+ * can get away with depends on the GIP mode / TSC reliability.
+ */
+ if (idCpuStop != idCpuStart)
+ {
+ bool fDoXCall = false;
+
+ /*
+ * Synchronous TSC mode: we're probably fine as it's unlikely
+ * that we were rescheduled because of TSC throttling or power
+ * management reasons, so just go ahead.
+ */
+ if (pGip->u32Mode == SUPGIPMODE_SYNC_TSC)
+ {
+ /* Probably ok, maybe we should retry once?. */
+ Assert(pGip->enmUseTscDelta == SUPGIPUSETSCDELTA_NOT_APPLICABLE);
+ }
+ /*
+ * If we're just doing the rough measurement, do the cross call and
+ * get on with things (we don't have deltas!).
+ */
+ else if (fRough)
+ fDoXCall = true;
+ /*
+ * Invariant TSC mode: It doesn't matter if we have delta available
+ * for both CPUs. That is not something we can assume at this point.
+ *
+ * Note! We cannot necessarily trust enmUseTscDelta here because it's
+ * downgraded after each delta calculation and the delta
+ * calculations may not be complete yet.
+ */
+ else if (pGip->u32Mode == SUPGIPMODE_INVARIANT_TSC)
+ {
+/** @todo This section of code is never reached atm, consider dropping it later on... */
+ if (pGip->enmUseTscDelta > SUPGIPUSETSCDELTA_ZERO_CLAIMED)
+ {
+ uint32_t iStartCpuSet = RTMpCpuIdToSetIndex(idCpuStart);
+ uint32_t iStopCpuSet = RTMpCpuIdToSetIndex(idCpuStop);
+ uint16_t iStartGipCpu = iStartCpuSet < RT_ELEMENTS(pGip->aiCpuFromCpuSetIdx)
+ ? pGip->aiCpuFromCpuSetIdx[iStartCpuSet] : UINT16_MAX;
+ uint16_t iStopGipCpu = iStopCpuSet < RT_ELEMENTS(pGip->aiCpuFromCpuSetIdx)
+ ? pGip->aiCpuFromCpuSetIdx[iStopCpuSet] : UINT16_MAX;
+ int64_t iStartTscDelta = iStartGipCpu < pGip->cCpus ? pGip->aCPUs[iStartGipCpu].i64TSCDelta : INT64_MAX;
+ int64_t iStopTscDelta = iStopGipCpu < pGip->cCpus ? pGip->aCPUs[iStopGipCpu].i64TSCDelta : INT64_MAX;
+ if (RT_LIKELY(iStartTscDelta != INT64_MAX && iStopTscDelta != INT64_MAX))
+ {
+ if (pGip->enmUseTscDelta > SUPGIPUSETSCDELTA_PRACTICALLY_ZERO)
+ {
+ uTscStart -= iStartTscDelta;
+ uTscStop -= iStopTscDelta;
+ }
+ }
+ /*
+ * Invalid CPU indexes are not caused by online/offline races, so
+ * we have to trigger driver load failure if that happens as GIP
+ * and IPRT assumptions are busted on this system.
+ */
+ else if (iStopGipCpu >= pGip->cCpus || iStartGipCpu >= pGip->cCpus)
+ {
+ SUPR0Printf("vboxdrv: Unexpected CPU index in supdrvGipInitMeasureTscFreq.\n");
+ SUPR0Printf("vboxdrv: start: %u, %u, %#llx stop: %u, %u, %#llx\n",
+ iStartCpuSet, iStartGipCpu, iStartTscDelta, iStopCpuSet, iStopGipCpu, iStopTscDelta);
+ return VERR_INVALID_CPU_INDEX;
+ }
+ /*
+ * No valid deltas. We retry, if we're on our last retry
+ * we do the cross call instead just to get a result. The
+ * frequency will be refined in a few seconds anyway.
+ */
+ else if (cTriesLeft > 0)
+ continue;
+ else
+ fDoXCall = true;
+ }
+ }
+ /*
+ * Asynchronous TSC mode: This is bad, as the reason we usually
+ * use this mode is to deal with variable TSC frequencies and
+ * deltas. So, we need to get the TSC from the same CPU as
+ * started it, we also need to keep that CPU busy. So, retry
+ * and fall back to the cross call on the last attempt.
+ */
+ else
+ {
+ Assert(pGip->u32Mode == SUPGIPMODE_ASYNC_TSC);
+ if (cTriesLeft > 0)
+ continue;
+ fDoXCall = true;
+ }
+
+ if (fDoXCall)
+ {
+ /*
+ * Try read the TSC and timestamp on the start CPU.
+ */
+ int rc = RTMpOnSpecific(idCpuStart, supdrvGipInitReadTscAndNanoTsOnCpu, &uTscStop, &nsStop);
+ if (RT_FAILURE(rc) && (!fRough || cTriesLeft > 0))
+ continue;
+ }
+ }
+
+ /*
+ * Calculate the TSC frequency and update it (shared with the refinement timer).
+ */
+ supdrvGipInitSetCpuFreq(pGip, nsStop - nsStart, uTscStop - uTscStart, 0);
+ return VINF_SUCCESS;
+ }
+
+ Assert(!fRough);
+ return VERR_SUPDRV_TSC_FREQ_MEASUREMENT_FAILED;
+}
+
+
+/**
+ * Finds our (@a idCpu) entry, or allocates a new one if not found.
+ *
+ * @returns Index of the CPU in the cache set.
+ * @param pGip The GIP.
+ * @param idCpu The CPU ID.
+ */
+static uint32_t supdrvGipFindOrAllocCpuIndexForCpuId(PSUPGLOBALINFOPAGE pGip, RTCPUID idCpu)
+{
+ uint32_t i, cTries;
+
+ /*
+ * ASSUMES that CPU IDs are constant.
+ */
+ for (i = 0; i < pGip->cCpus; i++)
+ if (pGip->aCPUs[i].idCpu == idCpu)
+ return i;
+
+ cTries = 0;
+ do
+ {
+ for (i = 0; i < pGip->cCpus; i++)
+ {
+ bool fRc;
+ ASMAtomicCmpXchgSize(&pGip->aCPUs[i].idCpu, idCpu, NIL_RTCPUID, fRc);
+ if (fRc)
+ return i;
+ }
+ } while (cTries++ < 32);
+ AssertReleaseFailed();
+ return i - 1;
+}
+
+
+/**
+ * The calling CPU should be accounted as online, update GIP accordingly.
+ *
+ * This is used by supdrvGipCreate() as well as supdrvGipMpEvent().
+ *
+ * @param pDevExt The device extension.
+ * @param idCpu The CPU ID.
+ */
+static void supdrvGipMpEventOnlineOrInitOnCpu(PSUPDRVDEVEXT pDevExt, RTCPUID idCpu)
+{
+ PSUPGLOBALINFOPAGE pGip = pDevExt->pGip;
+ int iCpuSet = 0;
+ uint32_t idApic;
+ uint32_t i = 0;
+ uint64_t u64NanoTS = 0;
+
+ AssertPtrReturnVoid(pGip);
+ Assert(!RTThreadPreemptIsEnabled(NIL_RTTHREAD));
+ AssertRelease(idCpu == RTMpCpuId());
+ Assert(pGip->cPossibleCpus == RTMpGetCount());
+
+ /*
+ * Do this behind a spinlock with interrupts disabled as this can fire
+ * on all CPUs simultaneously, see @bugref{6110}.
+ */
+ RTSpinlockAcquire(pDevExt->hGipSpinlock);
+
+ /*
+ * Update the globals.
+ */
+ ASMAtomicWriteU16(&pGip->cPresentCpus, RTMpGetPresentCount());
+ ASMAtomicWriteU16(&pGip->cOnlineCpus, RTMpGetOnlineCount());
+ iCpuSet = RTMpCpuIdToSetIndex(idCpu);
+ if (iCpuSet >= 0)
+ {
+ Assert(RTCpuSetIsMemberByIndex(&pGip->PossibleCpuSet, iCpuSet));
+ RTCpuSetAddByIndex(&pGip->OnlineCpuSet, iCpuSet);
+ RTCpuSetAddByIndex(&pGip->PresentCpuSet, iCpuSet);
+ }
+
+ /*
+ * Update the entry.
+ */
+ u64NanoTS = RTTimeSystemNanoTS() - pGip->u32UpdateIntervalNS;
+ i = supdrvGipFindOrAllocCpuIndexForCpuId(pGip, idCpu);
+
+ supdrvGipInitCpu(pGip, &pGip->aCPUs[i], u64NanoTS, pGip->u64CpuHz);
+
+ idApic = supdrvGipGetApicIdSlow();
+ ASMAtomicWriteU16(&pGip->aCPUs[i].idApic, idApic);
+ ASMAtomicWriteS16(&pGip->aCPUs[i].iCpuSet, (int16_t)iCpuSet);
+ ASMAtomicWriteSize(&pGip->aCPUs[i].idCpu, idCpu);
+
+ pGip->aCPUs[i].iCpuGroup = 0;
+ pGip->aCPUs[i].iCpuGroupMember = iCpuSet;
+#ifdef RT_OS_WINDOWS
+ supdrvOSGipInitGroupBitsForCpu(pDevExt, pGip, &pGip->aCPUs[i]);
+#endif
+
+ /*
+ * Update the APIC ID and CPU set index mappings.
+ */
+ if (idApic < RT_ELEMENTS(pGip->aiCpuFromApicId))
+ ASMAtomicWriteU16(&pGip->aiCpuFromApicId[idApic], i);
+ else
+ LogRelMax(64, ("supdrvGipMpEventOnlineOrInitOnCpu: idApic=%#x is out of bounds (%#zx, i=%u, iCpuSet=%d)\n",
+ idApic, RT_ELEMENTS(pGip->aiCpuFromApicId), i, iCpuSet));
+ if ((unsigned)iCpuSet < RT_ELEMENTS(pGip->aiCpuFromCpuSetIdx))
+ ASMAtomicWriteU16(&pGip->aiCpuFromCpuSetIdx[iCpuSet], i);
+ else
+ LogRelMax(64, ("supdrvGipMpEventOnlineOrInitOnCpu: iCpuSet=%d is out of bounds (%#zx, i=%u, idApic=%d)\n",
+ iCpuSet, RT_ELEMENTS(pGip->aiCpuFromApicId), i, idApic));
+
+ /* Add this CPU to this set of CPUs we need to calculate the TSC-delta for. */
+ RTCpuSetAddByIndex(&pDevExt->TscDeltaCpuSet, RTMpCpuIdToSetIndex(idCpu));
+
+ /* Update the Mp online/offline counter. */
+ ASMAtomicIncU32(&pDevExt->cMpOnOffEvents);
+
+ /* Commit it. */
+ ASMAtomicWriteSize(&pGip->aCPUs[i].enmState, SUPGIPCPUSTATE_ONLINE);
+
+ RTSpinlockRelease(pDevExt->hGipSpinlock);
+}
+
+
+/**
+ * RTMpOnSpecific callback wrapper for supdrvGipMpEventOnlineOrInitOnCpu().
+ *
+ * @param idCpu The CPU ID we are running on.
+ * @param pvUser1 Opaque pointer to the device instance data.
+ * @param pvUser2 Not used.
+ */
+static DECLCALLBACK(void) supdrvGipMpEventOnlineCallback(RTCPUID idCpu, void *pvUser1, void *pvUser2)
+{
+ PSUPDRVDEVEXT pDevExt = (PSUPDRVDEVEXT)pvUser1;
+ NOREF(pvUser2);
+ supdrvGipMpEventOnlineOrInitOnCpu(pDevExt, idCpu);
+}
+
+
+/**
+ * The CPU should be accounted as offline, update the GIP accordingly.
+ *
+ * This is used by supdrvGipMpEvent.
+ *
+ * @param pDevExt The device extension.
+ * @param idCpu The CPU ID.
+ */
+static void supdrvGipMpEventOffline(PSUPDRVDEVEXT pDevExt, RTCPUID idCpu)
+{
+ PSUPGLOBALINFOPAGE pGip = pDevExt->pGip;
+ int iCpuSet;
+ unsigned i;
+
+ AssertPtrReturnVoid(pGip);
+ RTSpinlockAcquire(pDevExt->hGipSpinlock);
+
+ iCpuSet = RTMpCpuIdToSetIndex(idCpu);
+ AssertReturnVoid(iCpuSet >= 0);
+
+ i = pGip->aiCpuFromCpuSetIdx[iCpuSet];
+ AssertReturnVoid(i < pGip->cCpus);
+ AssertReturnVoid(pGip->aCPUs[i].idCpu == idCpu);
+
+ Assert(RTCpuSetIsMemberByIndex(&pGip->PossibleCpuSet, iCpuSet));
+ RTCpuSetDelByIndex(&pGip->OnlineCpuSet, iCpuSet);
+
+ /* Update the Mp online/offline counter. */
+ ASMAtomicIncU32(&pDevExt->cMpOnOffEvents);
+
+ if (pGip->enmUseTscDelta > SUPGIPUSETSCDELTA_ZERO_CLAIMED)
+ {
+ /* Reset the TSC delta, we will recalculate it lazily. */
+ ASMAtomicWriteS64(&pGip->aCPUs[i].i64TSCDelta, INT64_MAX);
+ /* Remove this CPU from the set of CPUs that we have obtained the TSC deltas. */
+ RTCpuSetDelByIndex(&pDevExt->TscDeltaObtainedCpuSet, iCpuSet);
+ }
+
+ /* Commit it. */
+ ASMAtomicWriteSize(&pGip->aCPUs[i].enmState, SUPGIPCPUSTATE_OFFLINE);
+
+ RTSpinlockRelease(pDevExt->hGipSpinlock);
+}
+
+
+/**
+ * Multiprocessor event notification callback.
+ *
+ * This is used to make sure that the GIP master gets passed on to
+ * another CPU. It also updates the associated CPU data.
+ *
+ * @param enmEvent The event.
+ * @param idCpu The cpu it applies to.
+ * @param pvUser Pointer to the device extension.
+ */
+static DECLCALLBACK(void) supdrvGipMpEvent(RTMPEVENT enmEvent, RTCPUID idCpu, void *pvUser)
+{
+ PSUPDRVDEVEXT pDevExt = (PSUPDRVDEVEXT)pvUser;
+ PSUPGLOBALINFOPAGE pGip = pDevExt->pGip;
+
+ if (pGip)
+ {
+ RTTHREADPREEMPTSTATE PreemptState = RTTHREADPREEMPTSTATE_INITIALIZER;
+ switch (enmEvent)
+ {
+ case RTMPEVENT_ONLINE:
+ {
+ RTThreadPreemptDisable(&PreemptState);
+ if (idCpu == RTMpCpuId())
+ {
+ supdrvGipMpEventOnlineOrInitOnCpu(pDevExt, idCpu);
+ RTThreadPreemptRestore(&PreemptState);
+ }
+ else
+ {
+ RTThreadPreemptRestore(&PreemptState);
+ RTMpOnSpecific(idCpu, supdrvGipMpEventOnlineCallback, pDevExt, NULL /* pvUser2 */);
+ }
+
+ /*
+ * Recompute TSC-delta for the newly online'd CPU.
+ */
+ if (pGip->enmUseTscDelta > SUPGIPUSETSCDELTA_ZERO_CLAIMED)
+ {
+#ifdef SUPDRV_USE_TSC_DELTA_THREAD
+ supdrvTscDeltaThreadStartMeasurement(pDevExt, false /* fForceAll */);
+#else
+ uint32_t iCpu = supdrvGipFindOrAllocCpuIndexForCpuId(pGip, idCpu);
+ supdrvTscMeasureDeltaOne(pDevExt, iCpu);
+#endif
+ }
+ break;
+ }
+
+ case RTMPEVENT_OFFLINE:
+ supdrvGipMpEventOffline(pDevExt, idCpu);
+ break;
+ }
+ }
+
+ /*
+ * Make sure there is a master GIP.
+ */
+ if (enmEvent == RTMPEVENT_OFFLINE)
+ {
+ RTCPUID idGipMaster = ASMAtomicReadU32(&pDevExt->idGipMaster);
+ if (idGipMaster == idCpu)
+ {
+ /*
+ * The GIP master is going offline, find a new one.
+ */
+ bool fIgnored;
+ unsigned i;
+ RTCPUID idNewGipMaster = NIL_RTCPUID;
+ RTCPUSET OnlineCpus;
+ RTMpGetOnlineSet(&OnlineCpus);
+
+ for (i = 0; i < RTCPUSET_MAX_CPUS; i++)
+ if (RTCpuSetIsMemberByIndex(&OnlineCpus, i))
+ {
+ RTCPUID idCurCpu = RTMpCpuIdFromSetIndex(i);
+ if (idCurCpu != idGipMaster)
+ {
+ idNewGipMaster = idCurCpu;
+ break;
+ }
+ }
+
+ Log(("supdrvGipMpEvent: Gip master %#lx -> %#lx\n", (long)idGipMaster, (long)idNewGipMaster));
+ ASMAtomicCmpXchgSize(&pDevExt->idGipMaster, idNewGipMaster, idGipMaster, fIgnored);
+ NOREF(fIgnored);
+ }
+ }
+}
+
+
+/**
+ * On CPU initialization callback for RTMpOnAll.
+ *
+ * @param idCpu The CPU ID.
+ * @param pvUser1 The device extension.
+ * @param pvUser2 The GIP.
+ */
+static DECLCALLBACK(void) supdrvGipInitOnCpu(RTCPUID idCpu, void *pvUser1, void *pvUser2)
+{
+ /* This is good enough, even though it will update some of the globals a
+ bit to much. */
+ supdrvGipMpEventOnlineOrInitOnCpu((PSUPDRVDEVEXT)pvUser1, idCpu);
+ NOREF(pvUser2);
+}
+
+
+/**
+ * Callback used by supdrvDetermineAsyncTSC to read the TSC on a CPU.
+ *
+ * @param idCpu Ignored.
+ * @param pvUser1 Where to put the TSC.
+ * @param pvUser2 Ignored.
+ */
+static DECLCALLBACK(void) supdrvGipInitDetermineAsyncTscWorker(RTCPUID idCpu, void *pvUser1, void *pvUser2)
+{
+ Assert(RTMpCpuIdToSetIndex(idCpu) == (intptr_t)pvUser2);
+ ASMAtomicWriteU64((uint64_t volatile *)pvUser1, ASMReadTSC());
+ RT_NOREF2(idCpu, pvUser2);
+}
+
+
+/**
+ * Determine if Async GIP mode is required because of TSC drift.
+ *
+ * When using the default/normal timer code it is essential that the time stamp counter
+ * (TSC) runs never backwards, that is, a read operation to the counter should return
+ * a bigger value than any previous read operation. This is guaranteed by the latest
+ * AMD CPUs and by newer Intel CPUs which never enter the C2 state (P4). In any other
+ * case we have to choose the asynchronous timer mode.
+ *
+ * @param poffMin Pointer to the determined difference between different
+ * cores (optional, can be NULL).
+ * @return false if the time stamp counters appear to be synchronized, true otherwise.
+ */
+static bool supdrvGipInitDetermineAsyncTsc(uint64_t *poffMin)
+{
+ /*
+ * Just iterate all the cpus 8 times and make sure that the TSC is
+ * ever increasing. We don't bother taking TSC rollover into account.
+ */
+ int iEndCpu = RTMpGetArraySize();
+ int iCpu;
+ int cLoops = 8;
+ bool fAsync = false;
+ int rc = VINF_SUCCESS;
+ uint64_t offMax = 0;
+ uint64_t offMin = ~(uint64_t)0;
+ uint64_t PrevTsc = ASMReadTSC();
+
+ while (cLoops-- > 0)
+ {
+ for (iCpu = 0; iCpu < iEndCpu; iCpu++)
+ {
+ uint64_t CurTsc;
+ rc = RTMpOnSpecific(RTMpCpuIdFromSetIndex(iCpu), supdrvGipInitDetermineAsyncTscWorker,
+ &CurTsc, (void *)(uintptr_t)iCpu);
+ if (RT_SUCCESS(rc))
+ {
+ if (CurTsc <= PrevTsc)
+ {
+ fAsync = true;
+ offMin = offMax = PrevTsc - CurTsc;
+ Log(("supdrvGipInitDetermineAsyncTsc: iCpu=%d cLoops=%d CurTsc=%llx PrevTsc=%llx\n",
+ iCpu, cLoops, CurTsc, PrevTsc));
+ break;
+ }
+
+ /* Gather statistics (except the first time). */
+ if (iCpu != 0 || cLoops != 7)
+ {
+ uint64_t off = CurTsc - PrevTsc;
+ if (off < offMin)
+ offMin = off;
+ if (off > offMax)
+ offMax = off;
+ Log2(("%d/%d: off=%llx\n", cLoops, iCpu, off));
+ }
+
+ /* Next */
+ PrevTsc = CurTsc;
+ }
+ else if (rc == VERR_NOT_SUPPORTED)
+ break;
+ else
+ AssertMsg(rc == VERR_CPU_NOT_FOUND || rc == VERR_CPU_OFFLINE, ("%d\n", rc));
+ }
+
+ /* broke out of the loop. */
+ if (iCpu < iEndCpu)
+ break;
+ }
+
+ if (poffMin)
+ *poffMin = offMin; /* Almost RTMpOnSpecific profiling. */
+ Log(("supdrvGipInitDetermineAsyncTsc: returns %d; iEndCpu=%d rc=%d offMin=%llx offMax=%llx\n",
+ fAsync, iEndCpu, rc, offMin, offMax));
+#if !defined(RT_OS_SOLARIS) && !defined(RT_OS_OS2) && !defined(RT_OS_WINDOWS)
+ OSDBGPRINT(("vboxdrv: fAsync=%d offMin=%#lx offMax=%#lx\n", fAsync, (long)offMin, (long)offMax));
+#endif
+ return fAsync;
+}
+
+
+/**
+ * supdrvGipInit() worker that determines the GIP TSC mode.
+ *
+ * @returns The most suitable TSC mode.
+ * @param pDevExt Pointer to the device instance data.
+ */
+static SUPGIPMODE supdrvGipInitDetermineTscMode(PSUPDRVDEVEXT pDevExt)
+{
+ uint64_t u64DiffCoresIgnored;
+ uint32_t uEAX, uEBX, uECX, uEDX;
+
+ /*
+ * Establish whether the CPU advertises TSC as invariant, we need that in
+ * a couple of places below.
+ */
+ bool fInvariantTsc = false;
+ if (ASMHasCpuId())
+ {
+ uEAX = ASMCpuId_EAX(0x80000000);
+ if (RTX86IsValidExtRange(uEAX) && uEAX >= 0x80000007)
+ {
+ uEDX = ASMCpuId_EDX(0x80000007);
+ if (uEDX & X86_CPUID_AMD_ADVPOWER_EDX_TSCINVAR)
+ fInvariantTsc = true;
+ }
+ }
+
+ /*
+ * On single CPU systems, we don't need to consider ASYNC mode.
+ */
+ if (RTMpGetCount() <= 1)
+ return fInvariantTsc ? SUPGIPMODE_INVARIANT_TSC : SUPGIPMODE_SYNC_TSC;
+
+ /*
+ * Allow the user and/or OS specific bits to force async mode.
+ */
+ if (supdrvOSGetForcedAsyncTscMode(pDevExt))
+ return SUPGIPMODE_ASYNC_TSC;
+
+ /*
+ * Use invariant mode if the CPU says TSC is invariant.
+ */
+ if (fInvariantTsc)
+ return SUPGIPMODE_INVARIANT_TSC;
+
+ /*
+ * TSC is not invariant and we're on SMP, this presents two problems:
+ *
+ * (1) There might be a skew between the CPU, so that cpu0
+ * returns a TSC that is slightly different from cpu1.
+ * This screw may be due to (2), bad TSC initialization
+ * or slightly different TSC rates.
+ *
+ * (2) Power management (and other things) may cause the TSC
+ * to run at a non-constant speed, and cause the speed
+ * to be different on the cpus. This will result in (1).
+ *
+ * If any of the above is detected, we will have to use ASYNC mode.
+ */
+ /* (1). Try check for current differences between the cpus. */
+ if (supdrvGipInitDetermineAsyncTsc(&u64DiffCoresIgnored))
+ return SUPGIPMODE_ASYNC_TSC;
+
+ /* (2) If it's an AMD CPU with power management, we won't trust its TSC. */
+ ASMCpuId(0, &uEAX, &uEBX, &uECX, &uEDX);
+ if ( RTX86IsValidStdRange(uEAX)
+ && (RTX86IsAmdCpu(uEBX, uECX, uEDX) || RTX86IsHygonCpu(uEBX, uECX, uEDX)) )
+ {
+ /* Check for APM support. */
+ uEAX = ASMCpuId_EAX(0x80000000);
+ if (RTX86IsValidExtRange(uEAX) && uEAX >= 0x80000007)
+ {
+ uEDX = ASMCpuId_EDX(0x80000007);
+ if (uEDX & 0x3e) /* STC|TM|THERMTRIP|VID|FID. Ignore TS. */
+ return SUPGIPMODE_ASYNC_TSC;
+ }
+ }
+
+ return SUPGIPMODE_SYNC_TSC;
+}
+
+
+/**
+ * Initializes per-CPU GIP information.
+ *
+ * @param pGip Pointer to the GIP.
+ * @param pCpu Pointer to which GIP CPU to initialize.
+ * @param u64NanoTS The current nanosecond timestamp.
+ * @param uCpuHz The CPU frequency to set, 0 if the caller doesn't know.
+ */
+static void supdrvGipInitCpu(PSUPGLOBALINFOPAGE pGip, PSUPGIPCPU pCpu, uint64_t u64NanoTS, uint64_t uCpuHz)
+{
+ pCpu->u32TransactionId = 2;
+ pCpu->u64NanoTS = u64NanoTS;
+ pCpu->u64TSC = ASMReadTSC();
+ pCpu->u64TSCSample = GIP_TSC_DELTA_RSVD;
+ pCpu->i64TSCDelta = pGip->enmUseTscDelta > SUPGIPUSETSCDELTA_ZERO_CLAIMED ? INT64_MAX : 0;
+
+ ASMAtomicWriteSize(&pCpu->enmState, SUPGIPCPUSTATE_INVALID);
+ ASMAtomicWriteU32(&pCpu->idCpu, NIL_RTCPUID);
+ ASMAtomicWriteS16(&pCpu->iCpuSet, -1);
+ ASMAtomicWriteU16(&pCpu->iCpuGroup, 0);
+ ASMAtomicWriteU16(&pCpu->iCpuGroupMember, UINT16_MAX);
+ ASMAtomicWriteU16(&pCpu->idApic, UINT16_MAX);
+ ASMAtomicWriteU32(&pCpu->iReservedForNumaNode, 0);
+
+ /*
+ * The first time we're called, we don't have a CPU frequency handy,
+ * so pretend it's a 4 GHz CPU. On CPUs that are online, we'll get
+ * called again and at that point we have a more plausible CPU frequency
+ * value handy. The frequency history will also be adjusted again on
+ * the 2nd timer callout (maybe we can skip that now?).
+ */
+ if (!uCpuHz)
+ {
+ pCpu->u64CpuHz = _4G - 1;
+ pCpu->u32UpdateIntervalTSC = (uint32_t)((_4G - 1) / pGip->u32UpdateHz);
+ }
+ else
+ {
+ pCpu->u64CpuHz = uCpuHz;
+ pCpu->u32UpdateIntervalTSC = (uint32_t)(uCpuHz / pGip->u32UpdateHz);
+ }
+ pCpu->au32TSCHistory[0]
+ = pCpu->au32TSCHistory[1]
+ = pCpu->au32TSCHistory[2]
+ = pCpu->au32TSCHistory[3]
+ = pCpu->au32TSCHistory[4]
+ = pCpu->au32TSCHistory[5]
+ = pCpu->au32TSCHistory[6]
+ = pCpu->au32TSCHistory[7]
+ = pCpu->u32UpdateIntervalTSC;
+}
+
+
+/**
+ * Initializes the GIP data.
+ *
+ * @returns VBox status code.
+ * @param pDevExt Pointer to the device instance data.
+ * @param pGip Pointer to the read-write kernel mapping of the GIP.
+ * @param HCPhys The physical address of the GIP.
+ * @param u64NanoTS The current nanosecond timestamp.
+ * @param uUpdateHz The update frequency.
+ * @param uUpdateIntervalNS The update interval in nanoseconds.
+ * @param cCpus The CPU count.
+ * @param cbGipCpuGroups The supdrvOSGipGetGroupTableSize return value we
+ * used when allocating the GIP structure.
+ */
+static int supdrvGipInit(PSUPDRVDEVEXT pDevExt, PSUPGLOBALINFOPAGE pGip, RTHCPHYS HCPhys,
+ uint64_t u64NanoTS, unsigned uUpdateHz, unsigned uUpdateIntervalNS,
+ unsigned cCpus, size_t cbGipCpuGroups)
+{
+ size_t const cbGip = RT_ALIGN_Z(RT_UOFFSETOF_DYN(SUPGLOBALINFOPAGE, aCPUs[cCpus]) + cbGipCpuGroups, PAGE_SIZE);
+ unsigned i;
+#ifdef DEBUG_DARWIN_GIP
+ OSDBGPRINT(("supdrvGipInit: pGip=%p HCPhys=%lx u64NanoTS=%llu uUpdateHz=%d cCpus=%u\n", pGip, (long)HCPhys, u64NanoTS, uUpdateHz, cCpus));
+#else
+ LogFlow(("supdrvGipInit: pGip=%p HCPhys=%lx u64NanoTS=%llu uUpdateHz=%d cCpus=%u\n", pGip, (long)HCPhys, u64NanoTS, uUpdateHz, cCpus));
+#endif
+
+ /*
+ * Initialize the structure.
+ */
+ memset(pGip, 0, cbGip);
+
+ pGip->u32Magic = SUPGLOBALINFOPAGE_MAGIC;
+ pGip->u32Version = SUPGLOBALINFOPAGE_VERSION;
+ pGip->u32Mode = supdrvGipInitDetermineTscMode(pDevExt);
+ if ( pGip->u32Mode == SUPGIPMODE_INVARIANT_TSC
+ /*|| pGip->u32Mode == SUPGIPMODE_SYNC_TSC */)
+ pGip->enmUseTscDelta = supdrvOSAreTscDeltasInSync() /* Allow OS override (windows). */
+ ? SUPGIPUSETSCDELTA_ZERO_CLAIMED : SUPGIPUSETSCDELTA_PRACTICALLY_ZERO /* downgrade later */;
+ else
+ pGip->enmUseTscDelta = SUPGIPUSETSCDELTA_NOT_APPLICABLE;
+ pGip->cCpus = (uint16_t)cCpus;
+ pGip->cPages = (uint16_t)(cbGip / PAGE_SIZE);
+ pGip->u32UpdateHz = uUpdateHz;
+ pGip->u32UpdateIntervalNS = uUpdateIntervalNS;
+ pGip->fGetGipCpu = SUPGIPGETCPU_APIC_ID;
+ RTCpuSetEmpty(&pGip->OnlineCpuSet);
+ RTCpuSetEmpty(&pGip->PresentCpuSet);
+ RTMpGetSet(&pGip->PossibleCpuSet);
+ pGip->cOnlineCpus = RTMpGetOnlineCount();
+ pGip->cPresentCpus = RTMpGetPresentCount();
+ pGip->cPossibleCpus = RTMpGetCount();
+ pGip->cPossibleCpuGroups = 1;
+ pGip->idCpuMax = RTMpGetMaxCpuId();
+ for (i = 0; i < RT_ELEMENTS(pGip->aiCpuFromApicId); i++)
+ pGip->aiCpuFromApicId[i] = UINT16_MAX;
+ for (i = 0; i < RT_ELEMENTS(pGip->aiCpuFromCpuSetIdx); i++)
+ pGip->aiCpuFromCpuSetIdx[i] = UINT16_MAX;
+ for (i = 0; i < RT_ELEMENTS(pGip->aoffCpuGroup); i++)
+ pGip->aoffCpuGroup[i] = UINT32_MAX;
+ for (i = 0; i < cCpus; i++)
+ supdrvGipInitCpu(pGip, &pGip->aCPUs[i], u64NanoTS, 0 /*uCpuHz*/);
+#ifdef RT_OS_WINDOWS
+ int rc = supdrvOSInitGipGroupTable(pDevExt, pGip, cbGipCpuGroups);
+ AssertRCReturn(rc, rc);
+#endif
+
+ /*
+ * Link it to the device extension.
+ */
+ pDevExt->pGip = pGip;
+ pDevExt->HCPhysGip = HCPhys;
+ pDevExt->cGipUsers = 0;
+
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * Creates the GIP.
+ *
+ * @returns VBox status code.
+ * @param pDevExt Instance data. GIP stuff may be updated.
+ */
+int VBOXCALL supdrvGipCreate(PSUPDRVDEVEXT pDevExt)
+{
+ PSUPGLOBALINFOPAGE pGip;
+ size_t cbGip;
+ size_t cbGipCpuGroups;
+ RTHCPHYS HCPhysGip;
+ uint32_t u32SystemResolution;
+ uint32_t u32Interval;
+ uint32_t u32MinInterval;
+ uint32_t uMod;
+ unsigned cCpus;
+ int rc;
+
+ LogFlow(("supdrvGipCreate:\n"));
+
+ /*
+ * Assert order.
+ */
+ Assert(pDevExt->u32SystemTimerGranularityGrant == 0);
+ Assert(pDevExt->GipMemObj == NIL_RTR0MEMOBJ);
+ Assert(!pDevExt->pGipTimer);
+#ifdef SUPDRV_USE_MUTEX_FOR_GIP
+ Assert(pDevExt->mtxGip != NIL_RTSEMMUTEX);
+ Assert(pDevExt->mtxTscDelta != NIL_RTSEMMUTEX);
+#else
+ Assert(pDevExt->mtxGip != NIL_RTSEMFASTMUTEX);
+ Assert(pDevExt->mtxTscDelta != NIL_RTSEMFASTMUTEX);
+#endif
+
+ /*
+ * Check the CPU count.
+ */
+ cCpus = RTMpGetArraySize();
+ if (cCpus > RT_MIN(RTCPUSET_MAX_CPUS, RT_ELEMENTS(pGip->aiCpuFromApicId)))
+ {
+ SUPR0Printf("VBoxDrv: Too many CPUs (%u) for the GIP (max %u)\n", cCpus, RT_MIN(RTCPUSET_MAX_CPUS, RT_ELEMENTS(pGip->aiCpuFromApicId)));
+ return VERR_TOO_MANY_CPUS;
+ }
+
+ /*
+ * Allocate a contiguous set of pages with a default kernel mapping.
+ */
+#ifdef RT_OS_WINDOWS
+ cbGipCpuGroups = supdrvOSGipGetGroupTableSize(pDevExt);
+#else
+ cbGipCpuGroups = 0;
+#endif
+ cbGip = RT_UOFFSETOF_DYN(SUPGLOBALINFOPAGE, aCPUs[cCpus]) + cbGipCpuGroups;
+ rc = RTR0MemObjAllocCont(&pDevExt->GipMemObj, cbGip, false /*fExecutable*/);
+ if (RT_FAILURE(rc))
+ {
+ OSDBGPRINT(("supdrvGipCreate: failed to allocate the GIP page. rc=%d\n", rc));
+ return rc;
+ }
+ pGip = (PSUPGLOBALINFOPAGE)RTR0MemObjAddress(pDevExt->GipMemObj); AssertPtr(pGip);
+ HCPhysGip = RTR0MemObjGetPagePhysAddr(pDevExt->GipMemObj, 0); Assert(HCPhysGip != NIL_RTHCPHYS);
+
+ /*
+ * Find a reasonable update interval and initialize the structure.
+ */
+ supdrvGipRequestHigherTimerFrequencyFromSystem(pDevExt);
+ /** @todo figure out why using a 100Ms interval upsets timekeeping in VMs.
+ * See @bugref{6710}. */
+ u32MinInterval = RT_NS_10MS;
+ u32SystemResolution = RTTimerGetSystemGranularity();
+ u32Interval = u32MinInterval;
+ uMod = u32MinInterval % u32SystemResolution;
+ if (uMod)
+ u32Interval += u32SystemResolution - uMod;
+
+ rc = supdrvGipInit(pDevExt, pGip, HCPhysGip, RTTimeSystemNanoTS(), RT_NS_1SEC / u32Interval /*=Hz*/, u32Interval,
+ cCpus, cbGipCpuGroups);
+
+ /*
+ * Important sanity check... (Sets rc)
+ */
+ if (RT_UNLIKELY( pGip->enmUseTscDelta == SUPGIPUSETSCDELTA_ZERO_CLAIMED
+ && pGip->u32Mode == SUPGIPMODE_ASYNC_TSC
+ && !supdrvOSGetForcedAsyncTscMode(pDevExt)))
+ {
+ OSDBGPRINT(("supdrvGipCreate: Host-OS/user claims the TSC-deltas are zero but we detected async. TSC! Bad.\n"));
+ rc = VERR_INTERNAL_ERROR_2;
+ }
+
+ /* It doesn't make sense to do TSC-delta detection on systems we detect as async. */
+ AssertStmt( pGip->u32Mode != SUPGIPMODE_ASYNC_TSC
+ || pGip->enmUseTscDelta <= SUPGIPUSETSCDELTA_ZERO_CLAIMED,
+ rc = VERR_INTERNAL_ERROR_3);
+
+ /*
+ * Do the TSC frequency measurements.
+ *
+ * If we're in invariant TSC mode, just to a quick preliminary measurement
+ * that the TSC-delta measurement code can use to yield cross calls.
+ *
+ * If we're in any of the other two modes, neither which require MP init,
+ * notifications or deltas for the job, do the full measurement now so
+ * that supdrvGipInitOnCpu() can populate the TSC interval and history
+ * array with more reasonable values.
+ */
+ if (RT_SUCCESS(rc))
+ {
+ if (pGip->u32Mode == SUPGIPMODE_INVARIANT_TSC)
+ {
+ rc = supdrvGipInitMeasureTscFreq(pGip, true /*fRough*/); /* cannot fail */
+ supdrvGipInitStartTimerForRefiningInvariantTscFreq(pDevExt);
+ }
+ else
+ rc = supdrvGipInitMeasureTscFreq(pGip, false /*fRough*/);
+ if (RT_SUCCESS(rc))
+ {
+ /*
+ * Start TSC-delta measurement thread before we start getting MP
+ * events that will try kick it into action (includes the
+ * RTMpOnAll/supdrvGipInitOnCpu call below).
+ */
+ RTCpuSetEmpty(&pDevExt->TscDeltaCpuSet);
+ RTCpuSetEmpty(&pDevExt->TscDeltaObtainedCpuSet);
+#ifdef SUPDRV_USE_TSC_DELTA_THREAD
+ if (pGip->enmUseTscDelta > SUPGIPUSETSCDELTA_ZERO_CLAIMED)
+ rc = supdrvTscDeltaThreadInit(pDevExt);
+#endif
+ if (RT_SUCCESS(rc))
+ {
+ rc = RTMpNotificationRegister(supdrvGipMpEvent, pDevExt);
+ if (RT_SUCCESS(rc))
+ {
+ /*
+ * Do GIP initialization on all online CPUs. Wake up the
+ * TSC-delta thread afterwards.
+ */
+ rc = RTMpOnAll(supdrvGipInitOnCpu, pDevExt, pGip);
+ if (RT_SUCCESS(rc))
+ {
+#ifdef SUPDRV_USE_TSC_DELTA_THREAD
+ supdrvTscDeltaThreadStartMeasurement(pDevExt, true /* fForceAll */);
+#else
+ uint16_t iCpu;
+ if (pGip->enmUseTscDelta > SUPGIPUSETSCDELTA_ZERO_CLAIMED)
+ {
+ /*
+ * Measure the TSC deltas now that we have MP notifications.
+ */
+ int cTries = 5;
+ do
+ {
+ rc = supdrvTscMeasureInitialDeltas(pDevExt);
+ if ( rc != VERR_TRY_AGAIN
+ && rc != VERR_CPU_OFFLINE)
+ break;
+ } while (--cTries > 0);
+ for (iCpu = 0; iCpu < pGip->cCpus; iCpu++)
+ Log(("supdrvTscDeltaInit: cpu[%u] delta %lld\n", iCpu, pGip->aCPUs[iCpu].i64TSCDelta));
+ }
+ else
+ {
+ for (iCpu = 0; iCpu < pGip->cCpus; iCpu++)
+ AssertMsg(!pGip->aCPUs[iCpu].i64TSCDelta, ("iCpu=%u %lld mode=%d\n", iCpu, pGip->aCPUs[iCpu].i64TSCDelta, pGip->u32Mode));
+ }
+ if (RT_SUCCESS(rc))
+#endif
+ {
+ /*
+ * Create the timer.
+ * If CPU_ALL isn't supported we'll have to fall back to synchronous mode.
+ */
+ if (pGip->u32Mode == SUPGIPMODE_ASYNC_TSC)
+ {
+ rc = RTTimerCreateEx(&pDevExt->pGipTimer, u32Interval, RTTIMER_FLAGS_CPU_ALL,
+ supdrvGipAsyncTimer, pDevExt);
+ if (rc == VERR_NOT_SUPPORTED)
+ {
+ OSDBGPRINT(("supdrvGipCreate: omni timer not supported, falling back to synchronous mode\n"));
+ pGip->u32Mode = SUPGIPMODE_SYNC_TSC;
+ }
+ }
+ if (pGip->u32Mode != SUPGIPMODE_ASYNC_TSC)
+ rc = RTTimerCreateEx(&pDevExt->pGipTimer, u32Interval, 0 /* fFlags */,
+ supdrvGipSyncAndInvariantTimer, pDevExt);
+ if (RT_SUCCESS(rc))
+ {
+ /*
+ * We're good.
+ */
+ Log(("supdrvGipCreate: %u ns interval.\n", u32Interval));
+ supdrvGipReleaseHigherTimerFrequencyFromSystem(pDevExt);
+
+ g_pSUPGlobalInfoPage = pGip;
+ return VINF_SUCCESS;
+ }
+
+ OSDBGPRINT(("supdrvGipCreate: failed create GIP timer at %u ns interval. rc=%Rrc\n", u32Interval, rc));
+ Assert(!pDevExt->pGipTimer);
+ }
+ }
+ else
+ OSDBGPRINT(("supdrvGipCreate: RTMpOnAll failed. rc=%Rrc\n", rc));
+ }
+ else
+ OSDBGPRINT(("supdrvGipCreate: failed to register MP event notfication. rc=%Rrc\n", rc));
+ }
+ else
+ OSDBGPRINT(("supdrvGipCreate: supdrvTscDeltaInit failed. rc=%Rrc\n", rc));
+ }
+ else
+ OSDBGPRINT(("supdrvGipCreate: supdrvTscMeasureInitialDeltas failed. rc=%Rrc\n", rc));
+ }
+
+ /* Releases timer frequency increase too. */
+ supdrvGipDestroy(pDevExt);
+ return rc;
+}
+
+
+/**
+ * Invalidates the GIP data upon termination.
+ *
+ * @param pGip Pointer to the read-write kernel mapping of the GIP.
+ */
+static void supdrvGipTerm(PSUPGLOBALINFOPAGE pGip)
+{
+ unsigned i;
+ pGip->u32Magic = 0;
+ for (i = 0; i < pGip->cCpus; i++)
+ {
+ pGip->aCPUs[i].u64NanoTS = 0;
+ pGip->aCPUs[i].u64TSC = 0;
+ pGip->aCPUs[i].iTSCHistoryHead = 0;
+ pGip->aCPUs[i].u64TSCSample = 0;
+ pGip->aCPUs[i].i64TSCDelta = INT64_MAX;
+ }
+}
+
+
+/**
+ * Terminates the GIP.
+ *
+ * @param pDevExt Instance data. GIP stuff may be updated.
+ */
+void VBOXCALL supdrvGipDestroy(PSUPDRVDEVEXT pDevExt)
+{
+ int rc;
+#ifdef DEBUG_DARWIN_GIP
+ OSDBGPRINT(("supdrvGipDestroy: pDevExt=%p pGip=%p pGipTimer=%p GipMemObj=%p\n", pDevExt,
+ pDevExt->GipMemObj != NIL_RTR0MEMOBJ ? RTR0MemObjAddress(pDevExt->GipMemObj) : NULL,
+ pDevExt->pGipTimer, pDevExt->GipMemObj));
+#endif
+
+ /*
+ * Stop receiving MP notifications before tearing anything else down.
+ */
+ RTMpNotificationDeregister(supdrvGipMpEvent, pDevExt);
+
+#ifdef SUPDRV_USE_TSC_DELTA_THREAD
+ /*
+ * Terminate the TSC-delta measurement thread and resources.
+ */
+ supdrvTscDeltaTerm(pDevExt);
+#endif
+
+ /*
+ * Destroy the TSC-refinement timer.
+ */
+ if (pDevExt->pInvarTscRefineTimer)
+ {
+ RTTimerDestroy(pDevExt->pInvarTscRefineTimer);
+ pDevExt->pInvarTscRefineTimer = NULL;
+ }
+
+ /*
+ * Invalid the GIP data.
+ */
+ if (pDevExt->pGip)
+ {
+ supdrvGipTerm(pDevExt->pGip);
+ pDevExt->pGip = NULL;
+ }
+ g_pSUPGlobalInfoPage = NULL;
+
+ /*
+ * Destroy the timer and free the GIP memory object.
+ */
+ if (pDevExt->pGipTimer)
+ {
+ rc = RTTimerDestroy(pDevExt->pGipTimer); AssertRC(rc);
+ pDevExt->pGipTimer = NULL;
+ }
+
+ if (pDevExt->GipMemObj != NIL_RTR0MEMOBJ)
+ {
+ rc = RTR0MemObjFree(pDevExt->GipMemObj, true /* free mappings */); AssertRC(rc);
+ pDevExt->GipMemObj = NIL_RTR0MEMOBJ;
+ }
+
+ /*
+ * Finally, make sure we've release the system timer resolution request
+ * if one actually succeeded and is still pending.
+ */
+ supdrvGipReleaseHigherTimerFrequencyFromSystem(pDevExt);
+}
+
+
+
+
+/*
+ *
+ *
+ * GIP Update Timer Related Code
+ * GIP Update Timer Related Code
+ * GIP Update Timer Related Code
+ *
+ *
+ */
+
+
+/**
+ * Worker routine for supdrvGipUpdate() and supdrvGipUpdatePerCpu() that
+ * updates all the per cpu data except the transaction id.
+ *
+ * @param pDevExt The device extension.
+ * @param pGipCpu Pointer to the per cpu data.
+ * @param u64NanoTS The current time stamp.
+ * @param u64TSC The current TSC.
+ * @param iTick The current timer tick.
+ *
+ * @remarks Can be called with interrupts disabled!
+ */
+static void supdrvGipDoUpdateCpu(PSUPDRVDEVEXT pDevExt, PSUPGIPCPU pGipCpu, uint64_t u64NanoTS, uint64_t u64TSC, uint64_t iTick)
+{
+ uint64_t u64TSCDelta;
+ bool fUpdateCpuHz;
+ PSUPGLOBALINFOPAGE pGip = pDevExt->pGip;
+ AssertPtrReturnVoid(pGip);
+
+ /* Delta between this and the previous update. */
+ ASMAtomicUoWriteU32(&pGipCpu->u32PrevUpdateIntervalNS, (uint32_t)(u64NanoTS - pGipCpu->u64NanoTS));
+
+ /*
+ * Update the NanoTS.
+ */
+ ASMAtomicWriteU64(&pGipCpu->u64NanoTS, u64NanoTS);
+
+ /*
+ * Calc TSC delta.
+ */
+ u64TSCDelta = u64TSC - pGipCpu->u64TSC;
+ ASMAtomicWriteU64(&pGipCpu->u64TSC, u64TSC);
+
+ /*
+ * Determine if we need to update the CPU (TSC) frequency calculation.
+ *
+ * We don't need to keep recalculating the frequency when it's invariant,
+ * unless the special tstGIP-2 testing mode is enabled.
+ */
+ fUpdateCpuHz = pGip->u32Mode != SUPGIPMODE_INVARIANT_TSC;
+ if (!(pGip->fFlags & SUPGIP_FLAGS_TESTING))
+ { /* likely*/ }
+ else
+ {
+ uint32_t fGipFlags = pGip->fFlags;
+ if (fGipFlags & (SUPGIP_FLAGS_TESTING_ENABLE | SUPGIP_FLAGS_TESTING_START))
+ {
+ if (fGipFlags & SUPGIP_FLAGS_TESTING_START)
+ {
+ /* Cache the TSC frequency before forcing updates due to test mode. */
+ if (!fUpdateCpuHz)
+ pDevExt->uGipTestModeInvariantCpuHz = pGip->aCPUs[0].u64CpuHz;
+ ASMAtomicAndU32(&pGip->fFlags, ~SUPGIP_FLAGS_TESTING_START);
+ }
+ fUpdateCpuHz = true;
+ }
+ else if (fGipFlags & SUPGIP_FLAGS_TESTING_STOP)
+ {
+ /* Restore the cached TSC frequency if any. */
+ if (!fUpdateCpuHz)
+ {
+ Assert(pDevExt->uGipTestModeInvariantCpuHz);
+ ASMAtomicWriteU64(&pGip->aCPUs[0].u64CpuHz, pDevExt->uGipTestModeInvariantCpuHz);
+ }
+ ASMAtomicAndU32(&pGip->fFlags, ~(SUPGIP_FLAGS_TESTING_STOP | SUPGIP_FLAGS_TESTING));
+ }
+ }
+
+ /*
+ * Calculate the CPU (TSC) frequency if necessary.
+ */
+ if (fUpdateCpuHz)
+ {
+ uint64_t u64CpuHz;
+ uint32_t u32UpdateIntervalTSC;
+ uint32_t u32UpdateIntervalTSCSlack;
+ uint32_t u32TransactionId;
+ unsigned iTSCHistoryHead;
+
+ if (u64TSCDelta >> 32)
+ {
+ u64TSCDelta = pGipCpu->u32UpdateIntervalTSC;
+ pGipCpu->cErrors++;
+ }
+
+ /*
+ * On the 2nd and 3rd callout, reset the history with the current TSC
+ * interval since the values entered by supdrvGipInit are totally off.
+ * The interval on the 1st callout completely unreliable, the 2nd is a bit
+ * better, while the 3rd should be most reliable.
+ */
+ /** @todo Could we drop this now that we initializes the history
+ * with nominal TSC frequency values? */
+ u32TransactionId = pGipCpu->u32TransactionId;
+ if (RT_UNLIKELY( ( u32TransactionId == 5
+ || u32TransactionId == 7)
+ && ( iTick == 2
+ || iTick == 3) ))
+ {
+ unsigned i;
+ for (i = 0; i < RT_ELEMENTS(pGipCpu->au32TSCHistory); i++)
+ ASMAtomicUoWriteU32(&pGipCpu->au32TSCHistory[i], (uint32_t)u64TSCDelta);
+ }
+
+ /*
+ * Validate the NanoTS deltas between timer fires with an arbitrary threshold of 0.5%.
+ * Wait until we have at least one full history since the above history reset. The
+ * assumption is that the majority of the previous history values will be tolerable.
+ * See @bugref{6710#c67}.
+ */
+ /** @todo Could we drop the fudging there now that we initializes the history
+ * with nominal TSC frequency values? */
+ if ( u32TransactionId > 23 /* 7 + (8 * 2) */
+ && pGip->u32Mode != SUPGIPMODE_ASYNC_TSC)
+ {
+ uint32_t uNanoTsThreshold = pGip->u32UpdateIntervalNS / 200;
+ if ( pGipCpu->u32PrevUpdateIntervalNS > pGip->u32UpdateIntervalNS + uNanoTsThreshold
+ || pGipCpu->u32PrevUpdateIntervalNS < pGip->u32UpdateIntervalNS - uNanoTsThreshold)
+ {
+ uint32_t u32;
+ u32 = pGipCpu->au32TSCHistory[0];
+ u32 += pGipCpu->au32TSCHistory[1];
+ u32 += pGipCpu->au32TSCHistory[2];
+ u32 += pGipCpu->au32TSCHistory[3];
+ u32 >>= 2;
+ u64TSCDelta = pGipCpu->au32TSCHistory[4];
+ u64TSCDelta += pGipCpu->au32TSCHistory[5];
+ u64TSCDelta += pGipCpu->au32TSCHistory[6];
+ u64TSCDelta += pGipCpu->au32TSCHistory[7];
+ u64TSCDelta >>= 2;
+ u64TSCDelta += u32;
+ u64TSCDelta >>= 1;
+ }
+ }
+
+ /*
+ * TSC History.
+ */
+ Assert(RT_ELEMENTS(pGipCpu->au32TSCHistory) == 8);
+ iTSCHistoryHead = (pGipCpu->iTSCHistoryHead + 1) & 7;
+ ASMAtomicWriteU32(&pGipCpu->iTSCHistoryHead, iTSCHistoryHead);
+ ASMAtomicWriteU32(&pGipCpu->au32TSCHistory[iTSCHistoryHead], (uint32_t)u64TSCDelta);
+
+ /*
+ * UpdateIntervalTSC = average of last 8,2,1 intervals depending on update HZ.
+ *
+ * On Windows, we have an occasional (but recurring) sour value that messed up
+ * the history but taking only 1 interval reduces the precision overall.
+ */
+ if ( pGip->u32Mode == SUPGIPMODE_INVARIANT_TSC
+ || pGip->u32UpdateHz >= 1000)
+ {
+ uint32_t u32;
+ u32 = pGipCpu->au32TSCHistory[0];
+ u32 += pGipCpu->au32TSCHistory[1];
+ u32 += pGipCpu->au32TSCHistory[2];
+ u32 += pGipCpu->au32TSCHistory[3];
+ u32 >>= 2;
+ u32UpdateIntervalTSC = pGipCpu->au32TSCHistory[4];
+ u32UpdateIntervalTSC += pGipCpu->au32TSCHistory[5];
+ u32UpdateIntervalTSC += pGipCpu->au32TSCHistory[6];
+ u32UpdateIntervalTSC += pGipCpu->au32TSCHistory[7];
+ u32UpdateIntervalTSC >>= 2;
+ u32UpdateIntervalTSC += u32;
+ u32UpdateIntervalTSC >>= 1;
+
+ /* Value chosen for a 2GHz Athlon64 running linux 2.6.10/11. */
+ u32UpdateIntervalTSCSlack = u32UpdateIntervalTSC >> 14;
+ }
+ else if (pGip->u32UpdateHz >= 90)
+ {
+ u32UpdateIntervalTSC = (uint32_t)u64TSCDelta;
+ u32UpdateIntervalTSC += pGipCpu->au32TSCHistory[(iTSCHistoryHead - 1) & 7];
+ u32UpdateIntervalTSC >>= 1;
+
+ /* value chosen on a 2GHz thinkpad running windows */
+ u32UpdateIntervalTSCSlack = u32UpdateIntervalTSC >> 7;
+ }
+ else
+ {
+ u32UpdateIntervalTSC = (uint32_t)u64TSCDelta;
+
+ /* This value hasn't be checked yet.. waiting for OS/2 and 33Hz timers.. :-) */
+ u32UpdateIntervalTSCSlack = u32UpdateIntervalTSC >> 6;
+ }
+ ASMAtomicWriteU32(&pGipCpu->u32UpdateIntervalTSC, u32UpdateIntervalTSC + u32UpdateIntervalTSCSlack);
+
+ /*
+ * CpuHz.
+ */
+ u64CpuHz = ASMMult2xU32RetU64(u32UpdateIntervalTSC, RT_NS_1SEC);
+ u64CpuHz /= pGip->u32UpdateIntervalNS;
+ ASMAtomicWriteU64(&pGipCpu->u64CpuHz, u64CpuHz);
+ }
+}
+
+
+/**
+ * Updates the GIP.
+ *
+ * @param pDevExt The device extension.
+ * @param u64NanoTS The current nanosecond timestamp.
+ * @param u64TSC The current TSC timestamp.
+ * @param idCpu The CPU ID.
+ * @param iTick The current timer tick.
+ *
+ * @remarks Can be called with interrupts disabled!
+ */
+static void supdrvGipUpdate(PSUPDRVDEVEXT pDevExt, uint64_t u64NanoTS, uint64_t u64TSC, RTCPUID idCpu, uint64_t iTick)
+{
+ /*
+ * Determine the relevant CPU data.
+ */
+ PSUPGIPCPU pGipCpu;
+ PSUPGLOBALINFOPAGE pGip = pDevExt->pGip;
+ AssertPtrReturnVoid(pGip);
+
+ if (pGip->u32Mode != SUPGIPMODE_ASYNC_TSC)
+ pGipCpu = &pGip->aCPUs[0];
+ else
+ {
+ unsigned iCpu;
+ uint32_t idApic = supdrvGipGetApicId(pGip);
+ if (RT_LIKELY(idApic < RT_ELEMENTS(pGip->aiCpuFromApicId)))
+ { /* likely */ }
+ else
+ return;
+ iCpu = pGip->aiCpuFromApicId[idApic];
+ if (RT_LIKELY(iCpu < pGip->cCpus))
+ { /* likely */ }
+ else
+ return;
+ pGipCpu = &pGip->aCPUs[iCpu];
+ if (RT_LIKELY(pGipCpu->idCpu == idCpu))
+ { /* likely */ }
+ else
+ return;
+ }
+
+ /*
+ * Start update transaction.
+ */
+ if (!(ASMAtomicIncU32(&pGipCpu->u32TransactionId) & 1))
+ {
+ /* this can happen on win32 if we're taking to long and there are more CPUs around. shouldn't happen though. */
+ AssertMsgFailed(("Invalid transaction id, %#x, not odd!\n", pGipCpu->u32TransactionId));
+ ASMAtomicIncU32(&pGipCpu->u32TransactionId);
+ pGipCpu->cErrors++;
+ return;
+ }
+
+ /*
+ * Recalc the update frequency every 0x800th time.
+ */
+ if ( pGip->u32Mode != SUPGIPMODE_INVARIANT_TSC /* cuz we're not recalculating the frequency on invariant hosts. */
+ && !(pGipCpu->u32TransactionId & (GIP_UPDATEHZ_RECALC_FREQ * 2 - 2)))
+ {
+ if (pGip->u64NanoTSLastUpdateHz)
+ {
+#ifdef RT_ARCH_AMD64 /** @todo fix 64-bit div here to work on x86 linux. */
+ uint64_t u64Delta = u64NanoTS - pGip->u64NanoTSLastUpdateHz;
+ uint32_t u32UpdateHz = (uint32_t)((RT_NS_1SEC_64 * GIP_UPDATEHZ_RECALC_FREQ) / u64Delta);
+ if (u32UpdateHz <= 2000 && u32UpdateHz >= 30)
+ {
+ /** @todo r=ramshankar: Changing u32UpdateHz might screw up TSC frequency
+ * calculation on non-invariant hosts if it changes the history decision
+ * taken in supdrvGipDoUpdateCpu(). */
+ uint64_t u64Interval = u64Delta / GIP_UPDATEHZ_RECALC_FREQ;
+ ASMAtomicWriteU32(&pGip->u32UpdateHz, u32UpdateHz);
+ ASMAtomicWriteU32(&pGip->u32UpdateIntervalNS, (uint32_t)u64Interval);
+ }
+#endif
+ }
+ ASMAtomicWriteU64(&pGip->u64NanoTSLastUpdateHz, u64NanoTS | 1);
+ }
+
+ /*
+ * Update the data.
+ */
+ supdrvGipDoUpdateCpu(pDevExt, pGipCpu, u64NanoTS, u64TSC, iTick);
+
+ /*
+ * Complete transaction.
+ */
+ ASMAtomicIncU32(&pGipCpu->u32TransactionId);
+}
+
+
+/**
+ * Updates the per cpu GIP data for the calling cpu.
+ *
+ * @param pDevExt The device extension.
+ * @param u64NanoTS The current nanosecond timestamp.
+ * @param u64TSC The current TSC timesaver.
+ * @param idCpu The CPU ID.
+ * @param idApic The APIC id for the CPU index.
+ * @param iTick The current timer tick.
+ *
+ * @remarks Can be called with interrupts disabled!
+ */
+static void supdrvGipUpdatePerCpu(PSUPDRVDEVEXT pDevExt, uint64_t u64NanoTS, uint64_t u64TSC,
+ RTCPUID idCpu, uint8_t idApic, uint64_t iTick)
+{
+ uint32_t iCpu;
+ PSUPGLOBALINFOPAGE pGip = pDevExt->pGip;
+
+ /*
+ * Avoid a potential race when a CPU online notification doesn't fire on
+ * the onlined CPU but the tick creeps in before the event notification is
+ * run.
+ */
+ if (RT_LIKELY(iTick != 1))
+ { /* likely*/ }
+ else
+ {
+ iCpu = supdrvGipFindOrAllocCpuIndexForCpuId(pGip, idCpu);
+ if (pGip->aCPUs[iCpu].enmState == SUPGIPCPUSTATE_OFFLINE)
+ supdrvGipMpEventOnlineOrInitOnCpu(pDevExt, idCpu);
+ }
+
+ iCpu = pGip->aiCpuFromApicId[idApic];
+ if (RT_LIKELY(iCpu < pGip->cCpus))
+ {
+ PSUPGIPCPU pGipCpu = &pGip->aCPUs[iCpu];
+ if (pGipCpu->idCpu == idCpu)
+ {
+ /*
+ * Start update transaction.
+ */
+ if (!(ASMAtomicIncU32(&pGipCpu->u32TransactionId) & 1))
+ {
+ AssertMsgFailed(("Invalid transaction id, %#x, not odd!\n", pGipCpu->u32TransactionId));
+ ASMAtomicIncU32(&pGipCpu->u32TransactionId);
+ pGipCpu->cErrors++;
+ return;
+ }
+
+ /*
+ * Update the data.
+ */
+ supdrvGipDoUpdateCpu(pDevExt, pGipCpu, u64NanoTS, u64TSC, iTick);
+
+ /*
+ * Complete transaction.
+ */
+ ASMAtomicIncU32(&pGipCpu->u32TransactionId);
+ }
+ }
+}
+
+
+/**
+ * Timer callback function for the sync and invariant GIP modes.
+ *
+ * @param pTimer The timer.
+ * @param pvUser Opaque pointer to the device extension.
+ * @param iTick The timer tick.
+ */
+static DECLCALLBACK(void) supdrvGipSyncAndInvariantTimer(PRTTIMER pTimer, void *pvUser, uint64_t iTick)
+{
+ PSUPDRVDEVEXT pDevExt = (PSUPDRVDEVEXT)pvUser;
+ PSUPGLOBALINFOPAGE pGip = pDevExt->pGip;
+ RTCCUINTREG fEFlags = ASMIntDisableFlags(); /* No interruptions please (real problem on S10). */
+ uint64_t u64TSC = ASMReadTSC();
+ uint64_t u64NanoTS = RTTimeSystemNanoTS();
+ RT_NOREF1(pTimer);
+
+ if (pGip->enmUseTscDelta > SUPGIPUSETSCDELTA_PRACTICALLY_ZERO)
+ {
+ /*
+ * The calculations in supdrvGipUpdate() is somewhat timing sensitive,
+ * missing timer ticks is not an option for GIP because the GIP users
+ * will end up incrementing the time in 1ns per time getter call until
+ * there is a complete timer update. So, if the delta has yet to be
+ * calculated, we just pretend it is zero for now (the GIP users
+ * probably won't have it for a wee while either and will do the same).
+ *
+ * We could maybe on some platforms try cross calling a CPU with a
+ * working delta here, but it's not worth the hassle since the
+ * likelihood of this happening is really low. On Windows, Linux, and
+ * Solaris timers fire on the CPU they were registered/started on.
+ * Darwin timers doesn't necessarily (they are high priority threads).
+ */
+ uint32_t iCpuSet = RTMpCpuIdToSetIndex(RTMpCpuId());
+ uint16_t iGipCpu = RT_LIKELY(iCpuSet < RT_ELEMENTS(pGip->aiCpuFromCpuSetIdx))
+ ? pGip->aiCpuFromCpuSetIdx[iCpuSet] : UINT16_MAX;
+ Assert(!ASMIntAreEnabled());
+ if (RT_LIKELY(iGipCpu < pGip->cCpus))
+ {
+ int64_t iTscDelta = pGip->aCPUs[iGipCpu].i64TSCDelta;
+ if (iTscDelta != INT64_MAX)
+ u64TSC -= iTscDelta;
+ }
+ }
+
+ supdrvGipUpdate(pDevExt, u64NanoTS, u64TSC, NIL_RTCPUID, iTick);
+
+ ASMSetFlags(fEFlags);
+}
+
+
+/**
+ * Timer callback function for async GIP mode.
+ * @param pTimer The timer.
+ * @param pvUser Opaque pointer to the device extension.
+ * @param iTick The timer tick.
+ */
+static DECLCALLBACK(void) supdrvGipAsyncTimer(PRTTIMER pTimer, void *pvUser, uint64_t iTick)
+{
+ PSUPDRVDEVEXT pDevExt = (PSUPDRVDEVEXT)pvUser;
+ RTCCUINTREG fEFlags = ASMIntDisableFlags(); /* No interruptions please (real problem on S10). */
+ RTCPUID idCpu = RTMpCpuId();
+ uint64_t u64TSC = ASMReadTSC();
+ uint64_t NanoTS = RTTimeSystemNanoTS();
+ RT_NOREF1(pTimer);
+
+ /** @todo reset the transaction number and whatnot when iTick == 1. */
+ if (pDevExt->idGipMaster == idCpu)
+ supdrvGipUpdate(pDevExt, NanoTS, u64TSC, idCpu, iTick);
+ else
+ supdrvGipUpdatePerCpu(pDevExt, NanoTS, u64TSC, idCpu, supdrvGipGetApicId(pDevExt->pGip), iTick);
+
+ ASMSetFlags(fEFlags);
+}
+
+
+
+
+/*
+ *
+ *
+ * TSC Delta Measurements And Related Code
+ * TSC Delta Measurements And Related Code
+ * TSC Delta Measurements And Related Code
+ *
+ *
+ */
+
+
+/*
+ * Select TSC delta measurement algorithm.
+ */
+#if 0
+# define GIP_TSC_DELTA_METHOD_1
+#else
+# define GIP_TSC_DELTA_METHOD_2
+#endif
+
+/** For padding variables to keep them away from other cache lines. Better too
+ * large than too small!
+ * @remarks Current AMD64 and x86 CPUs seems to use 64 bytes. There are claims
+ * that NetBurst had 128 byte cache lines while the 486 thru Pentium
+ * III had 32 bytes cache lines. */
+#define GIP_TSC_DELTA_CACHE_LINE_SIZE 128
+
+
+/**
+ * TSC delta measurement algorithm \#2 result entry.
+ */
+typedef struct SUPDRVTSCDELTAMETHOD2ENTRY
+{
+ uint32_t iSeqMine;
+ uint32_t iSeqOther;
+ uint64_t uTsc;
+} SUPDRVTSCDELTAMETHOD2ENTRY;
+
+/**
+ * TSC delta measurement algorithm \#2 Data.
+ */
+typedef struct SUPDRVTSCDELTAMETHOD2
+{
+ /** Padding to make sure the iCurSeqNo is in its own cache line. */
+ uint64_t au64CacheLinePaddingBefore[GIP_TSC_DELTA_CACHE_LINE_SIZE / sizeof(uint64_t)];
+ /** The current sequence number of this worker. */
+ uint32_t volatile iCurSeqNo;
+ /** Padding to make sure the iCurSeqNo is in its own cache line. */
+ uint32_t au64CacheLinePaddingAfter[GIP_TSC_DELTA_CACHE_LINE_SIZE / sizeof(uint32_t) - 1];
+ /** Result table. */
+ SUPDRVTSCDELTAMETHOD2ENTRY aResults[64];
+} SUPDRVTSCDELTAMETHOD2;
+/** Pointer to the data for TSC delta measurement algorithm \#2 .*/
+typedef SUPDRVTSCDELTAMETHOD2 *PSUPDRVTSCDELTAMETHOD2;
+
+
+/**
+ * The TSC delta synchronization struct, version 2.
+ *
+ * The synchronization variable is completely isolated in its own cache line
+ * (provided our max cache line size estimate is correct).
+ */
+typedef struct SUPTSCDELTASYNC2
+{
+ /** Padding to make sure the uVar1 is in its own cache line. */
+ uint64_t au64CacheLinePaddingBefore[GIP_TSC_DELTA_CACHE_LINE_SIZE / sizeof(uint64_t)];
+
+ /** The synchronization variable, holds values GIP_TSC_DELTA_SYNC_*. */
+ volatile uint32_t uSyncVar;
+ /** Sequence synchronizing variable used for post 'GO' synchronization. */
+ volatile uint32_t uSyncSeq;
+
+ /** Padding to make sure the uVar1 is in its own cache line. */
+ uint64_t au64CacheLinePaddingAfter[GIP_TSC_DELTA_CACHE_LINE_SIZE / sizeof(uint64_t) - 2];
+
+ /** Start RDTSC value. Put here mainly to save stack space. */
+ uint64_t uTscStart;
+ /** Copy of SUPDRVGIPTSCDELTARGS::cMaxTscTicks. */
+ uint64_t cMaxTscTicks;
+} SUPTSCDELTASYNC2;
+AssertCompileSize(SUPTSCDELTASYNC2, GIP_TSC_DELTA_CACHE_LINE_SIZE * 2 + sizeof(uint64_t));
+typedef SUPTSCDELTASYNC2 *PSUPTSCDELTASYNC2;
+
+/** Prestart wait. */
+#define GIP_TSC_DELTA_SYNC2_PRESTART_WAIT UINT32_C(0x0ffe)
+/** Prestart aborted. */
+#define GIP_TSC_DELTA_SYNC2_PRESTART_ABORT UINT32_C(0x0fff)
+/** Ready (on your mark). */
+#define GIP_TSC_DELTA_SYNC2_READY UINT32_C(0x1000)
+/** Steady (get set). */
+#define GIP_TSC_DELTA_SYNC2_STEADY UINT32_C(0x1001)
+/** Go! */
+#define GIP_TSC_DELTA_SYNC2_GO UINT32_C(0x1002)
+/** Used by the verification test. */
+#define GIP_TSC_DELTA_SYNC2_GO_GO UINT32_C(0x1003)
+
+/** We reached the time limit. */
+#define GIP_TSC_DELTA_SYNC2_TIMEOUT UINT32_C(0x1ffe)
+/** The other party won't touch the sync struct ever again. */
+#define GIP_TSC_DELTA_SYNC2_FINAL UINT32_C(0x1fff)
+
+
+/**
+ * Argument package/state passed by supdrvTscMeasureDeltaOne() to the RTMpOn
+ * callback worker.
+ * @todo add
+ */
+typedef struct SUPDRVGIPTSCDELTARGS
+{
+ /** The device extension. */
+ PSUPDRVDEVEXT pDevExt;
+ /** Pointer to the GIP CPU array entry for the worker. */
+ PSUPGIPCPU pWorker;
+ /** Pointer to the GIP CPU array entry for the master. */
+ PSUPGIPCPU pMaster;
+ /** The maximum number of ticks to spend in supdrvTscMeasureDeltaCallback.
+ * (This is what we need a rough TSC frequency for.) */
+ uint64_t cMaxTscTicks;
+ /** Used to abort synchronization setup. */
+ bool volatile fAbortSetup;
+
+ /** Padding to make sure the master variables live in its own cache lines. */
+ uint64_t au64CacheLinePaddingBefore[GIP_TSC_DELTA_CACHE_LINE_SIZE / sizeof(uint64_t)];
+
+ /** @name Master
+ * @{ */
+ /** The time the master spent in the MP worker. */
+ uint64_t cElapsedMasterTscTicks;
+ /** The iTry value when stopped at. */
+ uint32_t iTry;
+ /** Set if the run timed out. */
+ bool volatile fTimedOut;
+ /** Pointer to the master's synchronization struct (on stack). */
+ PSUPTSCDELTASYNC2 volatile pSyncMaster;
+ /** Master data union. */
+ union
+ {
+ /** Data (master) for delta verification. */
+ struct
+ {
+ /** Verification test TSC values for the master. */
+ uint64_t volatile auTscs[32];
+ } Verify;
+ /** Data (master) for measurement method \#2. */
+ struct
+ {
+ /** Data and sequence number. */
+ SUPDRVTSCDELTAMETHOD2 Data;
+ /** The lag setting for the next run. */
+ bool fLag;
+ /** Number of hits. */
+ uint32_t cHits;
+ } M2;
+ } uMaster;
+ /** The verifier verdict, VINF_SUCCESS if ok, VERR_OUT_OF_RANGE if not,
+ * VERR_TRY_AGAIN on timeout. */
+ int32_t rcVerify;
+#ifdef TSCDELTA_VERIFY_WITH_STATS
+ /** The maximum difference between TSC read during delta verification. */
+ int64_t cMaxVerifyTscTicks;
+ /** The minimum difference between two TSC reads during verification. */
+ int64_t cMinVerifyTscTicks;
+ /** The bad TSC diff, worker relative to master (= worker - master).
+ * Negative value means the worker is behind the master. */
+ int64_t iVerifyBadTscDiff;
+#endif
+ /** @} */
+
+ /** Padding to make sure the worker variables live is in its own cache line. */
+ uint64_t au64CacheLinePaddingBetween[GIP_TSC_DELTA_CACHE_LINE_SIZE / sizeof(uint64_t)];
+
+ /** @name Proletarian
+ * @{ */
+ /** Pointer to the worker's synchronization struct (on stack). */
+ PSUPTSCDELTASYNC2 volatile pSyncWorker;
+ /** The time the worker spent in the MP worker. */
+ uint64_t cElapsedWorkerTscTicks;
+ /** Worker data union. */
+ union
+ {
+ /** Data (worker) for delta verification. */
+ struct
+ {
+ /** Verification test TSC values for the worker. */
+ uint64_t volatile auTscs[32];
+ } Verify;
+ /** Data (worker) for measurement method \#2. */
+ struct
+ {
+ /** Data and sequence number. */
+ SUPDRVTSCDELTAMETHOD2 Data;
+ /** The lag setting for the next run (set by master). */
+ bool fLag;
+ } M2;
+ } uWorker;
+ /** @} */
+
+ /** Padding to make sure the above is in its own cache line. */
+ uint64_t au64CacheLinePaddingAfter[GIP_TSC_DELTA_CACHE_LINE_SIZE / sizeof(uint64_t)];
+} SUPDRVGIPTSCDELTARGS;
+typedef SUPDRVGIPTSCDELTARGS *PSUPDRVGIPTSCDELTARGS;
+
+
+/** @name Macros that implements the basic synchronization steps common to
+ * the algorithms.
+ *
+ * Must be used from loop as the timeouts are implemented via 'break' statements
+ * at the moment.
+ *
+ * @{
+ */
+#if defined(DEBUG_bird) /* || defined(VBOX_STRICT) */
+# define TSCDELTA_DBG_VARS() uint32_t iDbgCounter
+# define TSCDELTA_DBG_START_LOOP() do { iDbgCounter = 0; } while (0)
+# define TSCDELTA_DBG_CHECK_LOOP() \
+ do { iDbgCounter++; if ((iDbgCounter & UINT32_C(0x01ffffff)) == 0) RT_BREAKPOINT(); } while (0)
+#else
+# define TSCDELTA_DBG_VARS() ((void)0)
+# define TSCDELTA_DBG_START_LOOP() ((void)0)
+# define TSCDELTA_DBG_CHECK_LOOP() ((void)0)
+#endif
+#if 0
+# define TSCDELTA_DBG_SYNC_MSG(a_Args) SUPR0Printf a_Args
+#else
+# define TSCDELTA_DBG_SYNC_MSG(a_Args) ((void)0)
+#endif
+#if 0
+# define TSCDELTA_DBG_SYNC_MSG2(a_Args) SUPR0Printf a_Args
+#else
+# define TSCDELTA_DBG_SYNC_MSG2(a_Args) ((void)0)
+#endif
+#if 0
+# define TSCDELTA_DBG_SYNC_MSG9(a_Args) SUPR0Printf a_Args
+#else
+# define TSCDELTA_DBG_SYNC_MSG9(a_Args) ((void)0)
+#endif
+
+
+static bool supdrvTscDeltaSync2_Before(PSUPTSCDELTASYNC2 pMySync, PSUPTSCDELTASYNC2 pOtherSync,
+ bool fIsMaster, PRTCCUINTREG pfEFlags, PSUPDRVGIPTSCDELTARGS pArgs)
+{
+ uint32_t iMySeq = fIsMaster ? 0 : 256;
+ uint32_t const iMaxSeq = iMySeq + 16; /* For the last loop, darn linux/freebsd C-ishness. */
+ uint32_t u32Tmp;
+ uint32_t iSync2Loops = 0;
+ RTCCUINTREG fEFlags;
+ TSCDELTA_DBG_VARS();
+
+ *pfEFlags = X86_EFL_IF | X86_EFL_1; /* should shut up most nagging compilers. */
+
+ /*
+ * The master tells the worker to get on it's mark.
+ */
+ if (fIsMaster)
+ {
+ if (RT_LIKELY(ASMAtomicCmpXchgU32(&pOtherSync->uSyncVar, GIP_TSC_DELTA_SYNC2_STEADY, GIP_TSC_DELTA_SYNC2_READY)))
+ { /* likely*/ }
+ else
+ {
+ TSCDELTA_DBG_SYNC_MSG(("sync/before/%s: #1 uSyncVar=%#x\n", fIsMaster ? "master" : "worker", pOtherSync->uSyncVar));
+ return false;
+ }
+ }
+
+ /*
+ * Wait for the on your mark signal (ack in the master case). We process timeouts here.
+ */
+ ASMAtomicWriteU32(&(pMySync)->uSyncSeq, 0);
+ for (;;)
+ {
+ fEFlags = ASMIntDisableFlags();
+ u32Tmp = ASMAtomicReadU32(&pMySync->uSyncVar);
+ if (u32Tmp == GIP_TSC_DELTA_SYNC2_STEADY)
+ break;
+ ASMSetFlags(fEFlags);
+ ASMNopPause();
+
+ /* Abort? */
+ if (u32Tmp != GIP_TSC_DELTA_SYNC2_READY)
+ {
+ TSCDELTA_DBG_SYNC_MSG(("sync/before/%s: #2 u32Tmp=%#x\n", fIsMaster ? "master" : "worker", u32Tmp));
+ return false;
+ }
+
+ /* Check for timeouts every so often (not every loop in case RDTSC is
+ trapping or something). Must check the first time around. */
+#if 0 /* For debugging the timeout paths. */
+ static uint32_t volatile xxx;
+#endif
+ if ( ( (iSync2Loops & 0x3ff) == 0
+ && ASMReadTSC() - pMySync->uTscStart > pMySync->cMaxTscTicks)
+#if 0 /* This is crazy, I know, but enable this code and the results are markedly better when enabled on the 1.4GHz AMD (debug). */
+ || (!fIsMaster && (++xxx & 0xf) == 0)
+#endif
+ )
+ {
+ /* Try switch our own state into timeout mode so the master cannot tell us to 'GO',
+ ignore the timeout if we've got the go ahead already (simpler). */
+ if (ASMAtomicCmpXchgU32(&pMySync->uSyncVar, GIP_TSC_DELTA_SYNC2_TIMEOUT, GIP_TSC_DELTA_SYNC2_READY))
+ {
+ TSCDELTA_DBG_SYNC_MSG(("sync/before/%s: timeout\n", fIsMaster ? "master" : "worker"));
+ ASMAtomicCmpXchgU32(&pOtherSync->uSyncVar, GIP_TSC_DELTA_SYNC2_TIMEOUT, GIP_TSC_DELTA_SYNC2_STEADY);
+ ASMAtomicWriteBool(&pArgs->fTimedOut, true);
+ return false;
+ }
+ }
+ iSync2Loops++;
+ }
+
+ /*
+ * Interrupts are now disabled and will remain disabled until we do
+ * TSCDELTA_MASTER_SYNC_AFTER / TSCDELTA_OTHER_SYNC_AFTER.
+ */
+ *pfEFlags = fEFlags;
+
+ /*
+ * The worker tells the master that it is on its mark and that the master
+ * need to get into position as well.
+ */
+ if (!fIsMaster)
+ {
+ if (RT_LIKELY(ASMAtomicCmpXchgU32(&pOtherSync->uSyncVar, GIP_TSC_DELTA_SYNC2_STEADY, GIP_TSC_DELTA_SYNC2_READY)))
+ { /* likely */ }
+ else
+ {
+ ASMSetFlags(fEFlags);
+ TSCDELTA_DBG_SYNC_MSG(("sync/before/%s: #3 uSyncVar=%#x\n", fIsMaster ? "master" : "worker", pOtherSync->uSyncVar));
+ return false;
+ }
+ }
+
+ /*
+ * The master sends the 'go' to the worker and wait for ACK.
+ */
+ if (fIsMaster)
+ {
+ if (RT_LIKELY(ASMAtomicCmpXchgU32(&pOtherSync->uSyncVar, GIP_TSC_DELTA_SYNC2_GO, GIP_TSC_DELTA_SYNC2_STEADY)))
+ { /* likely */ }
+ else
+ {
+ ASMSetFlags(fEFlags);
+ TSCDELTA_DBG_SYNC_MSG(("sync/before/%s: #4 uSyncVar=%#x\n", fIsMaster ? "master" : "worker", pOtherSync->uSyncVar));
+ return false;
+ }
+ }
+
+ /*
+ * Wait for the 'go' signal (ack in the master case).
+ */
+ TSCDELTA_DBG_START_LOOP();
+ for (;;)
+ {
+ u32Tmp = ASMAtomicReadU32(&pMySync->uSyncVar);
+ if (u32Tmp == GIP_TSC_DELTA_SYNC2_GO)
+ break;
+ if (RT_LIKELY(u32Tmp == GIP_TSC_DELTA_SYNC2_STEADY))
+ { /* likely */ }
+ else
+ {
+ ASMSetFlags(fEFlags);
+ TSCDELTA_DBG_SYNC_MSG(("sync/before/%s: #5 u32Tmp=%#x\n", fIsMaster ? "master" : "worker", u32Tmp));
+ return false;
+ }
+
+ TSCDELTA_DBG_CHECK_LOOP();
+ ASMNopPause();
+ }
+
+ /*
+ * The worker acks the 'go' (shouldn't fail).
+ */
+ if (!fIsMaster)
+ {
+ if (RT_LIKELY(ASMAtomicCmpXchgU32(&pOtherSync->uSyncVar, GIP_TSC_DELTA_SYNC2_GO, GIP_TSC_DELTA_SYNC2_STEADY)))
+ { /* likely */ }
+ else
+ {
+ ASMSetFlags(fEFlags);
+ TSCDELTA_DBG_SYNC_MSG(("sync/before/%s: #6 uSyncVar=%#x\n", fIsMaster ? "master" : "worker", pOtherSync->uSyncVar));
+ return false;
+ }
+ }
+
+ /*
+ * Try enter mostly lockstep execution with it.
+ */
+ for (;;)
+ {
+ uint32_t iOtherSeq1, iOtherSeq2;
+ ASMCompilerBarrier();
+ ASMSerializeInstruction();
+
+ ASMAtomicWriteU32(&pMySync->uSyncSeq, iMySeq);
+ ASMNopPause();
+ iOtherSeq1 = ASMAtomicXchgU32(&pOtherSync->uSyncSeq, iMySeq);
+ ASMNopPause();
+ iOtherSeq2 = ASMAtomicReadU32(&pMySync->uSyncSeq);
+
+ ASMCompilerBarrier();
+ if (iOtherSeq1 == iOtherSeq2)
+ return true;
+
+ /* Did the other guy give up? Should we give up? */
+ if ( iOtherSeq1 == UINT32_MAX
+ || iOtherSeq2 == UINT32_MAX)
+ return true;
+ if (++iMySeq >= iMaxSeq)
+ {
+ ASMAtomicWriteU32(&pMySync->uSyncSeq, UINT32_MAX);
+ return true;
+ }
+ ASMNopPause();
+ }
+}
+
+#define TSCDELTA_MASTER_SYNC_BEFORE(a_pMySync, a_pOtherSync, a_pfEFlags, a_pArgs) \
+ if (RT_LIKELY(supdrvTscDeltaSync2_Before(a_pMySync, a_pOtherSync, true /*fIsMaster*/, a_pfEFlags, a_pArgs))) \
+ { /*likely*/ } \
+ else if (true) \
+ { \
+ TSCDELTA_DBG_SYNC_MSG9(("sync/before/master: #89\n")); \
+ break; \
+ } else do {} while (0)
+#define TSCDELTA_OTHER_SYNC_BEFORE(a_pMySync, a_pOtherSync, a_pfEFlags, a_pArgs) \
+ if (RT_LIKELY(supdrvTscDeltaSync2_Before(a_pMySync, a_pOtherSync, false /*fIsMaster*/, a_pfEFlags, a_pArgs))) \
+ { /*likely*/ } \
+ else if (true) \
+ { \
+ TSCDELTA_DBG_SYNC_MSG9(("sync/before/other: #89\n")); \
+ break; \
+ } else do {} while (0)
+
+
+static bool supdrvTscDeltaSync2_After(PSUPTSCDELTASYNC2 pMySync, PSUPTSCDELTASYNC2 pOtherSync,
+ bool fIsMaster, RTCCUINTREG fEFlags)
+{
+ TSCDELTA_DBG_VARS();
+ RT_NOREF1(pOtherSync);
+
+ /*
+ * Wait for the 'ready' signal. In the master's case, this means the
+ * worker has completed its data collection, while in the worker's case it
+ * means the master is done processing the data and it's time for the next
+ * loop iteration (or whatever).
+ */
+ ASMSetFlags(fEFlags);
+ TSCDELTA_DBG_START_LOOP();
+ for (;;)
+ {
+ uint32_t u32Tmp = ASMAtomicReadU32(&pMySync->uSyncVar);
+ if ( u32Tmp == GIP_TSC_DELTA_SYNC2_READY
+ || (u32Tmp == GIP_TSC_DELTA_SYNC2_STEADY && !fIsMaster) /* kicked twice => race */ )
+ return true;
+ ASMNopPause();
+ if (RT_LIKELY(u32Tmp == GIP_TSC_DELTA_SYNC2_GO))
+ { /* likely */}
+ else
+ {
+ TSCDELTA_DBG_SYNC_MSG(("sync/after/other: #1 u32Tmp=%#x\n", u32Tmp));
+ return false; /* shouldn't ever happen! */
+ }
+ TSCDELTA_DBG_CHECK_LOOP();
+ ASMNopPause();
+ }
+}
+
+#define TSCDELTA_MASTER_SYNC_AFTER(a_pMySync, a_pOtherSync, a_fEFlags) \
+ if (RT_LIKELY(supdrvTscDeltaSync2_After(a_pMySync, a_pOtherSync, true /*fIsMaster*/, a_fEFlags))) \
+ { /* likely */ } \
+ else if (true) \
+ { \
+ TSCDELTA_DBG_SYNC_MSG9(("sync/after/master: #97\n")); \
+ break; \
+ } else do {} while (0)
+
+#define TSCDELTA_MASTER_KICK_OTHER_OUT_OF_AFTER(a_pMySync, a_pOtherSync) \
+ /* \
+ * Tell the worker that we're done processing the data and ready for the next round. \
+ */ \
+ if (RT_LIKELY(ASMAtomicCmpXchgU32(&(a_pOtherSync)->uSyncVar, GIP_TSC_DELTA_SYNC2_READY, GIP_TSC_DELTA_SYNC2_GO))) \
+ { /* likely */ } \
+ else if (true)\
+ { \
+ TSCDELTA_DBG_SYNC_MSG(("sync/after/master: #99 uSyncVar=%#x\n", (a_pOtherSync)->uSyncVar)); \
+ break; \
+ } else do {} while (0)
+
+#define TSCDELTA_OTHER_SYNC_AFTER(a_pMySync, a_pOtherSync, a_fEFlags) \
+ if (true) { \
+ /* \
+ * Tell the master that we're done collecting data and wait for the next round to start. \
+ */ \
+ if (RT_LIKELY(ASMAtomicCmpXchgU32(&(a_pOtherSync)->uSyncVar, GIP_TSC_DELTA_SYNC2_READY, GIP_TSC_DELTA_SYNC2_GO))) \
+ { /* likely */ } \
+ else \
+ { \
+ ASMSetFlags(a_fEFlags); \
+ TSCDELTA_DBG_SYNC_MSG(("sync/after/other: #0 uSyncVar=%#x\n", (a_pOtherSync)->uSyncVar)); \
+ break; \
+ } \
+ if (RT_LIKELY(supdrvTscDeltaSync2_After(a_pMySync, a_pOtherSync, false /*fIsMaster*/, a_fEFlags))) \
+ { /* likely */ } \
+ else \
+ { \
+ TSCDELTA_DBG_SYNC_MSG9(("sync/after/other: #98\n")); \
+ break; \
+ } \
+ } else do {} while (0)
+/** @} */
+
+
+#ifdef GIP_TSC_DELTA_METHOD_1
+/**
+ * TSC delta measurement algorithm \#1 (GIP_TSC_DELTA_METHOD_1).
+ *
+ *
+ * We ignore the first few runs of the loop in order to prime the
+ * cache. Also, we need to be careful about using 'pause' instruction
+ * in critical busy-wait loops in this code - it can cause undesired
+ * behaviour with hyperthreading.
+ *
+ * We try to minimize the measurement error by computing the minimum
+ * read time of the compare statement in the worker by taking TSC
+ * measurements across it.
+ *
+ * It must be noted that the computed minimum read time is mostly to
+ * eliminate huge deltas when the worker is too early and doesn't by
+ * itself help produce more accurate deltas. We allow two times the
+ * computed minimum as an arbitrary acceptable threshold. Therefore,
+ * it is still possible to get negative deltas where there are none
+ * when the worker is earlier. As long as these occasional negative
+ * deltas are lower than the time it takes to exit guest-context and
+ * the OS to reschedule EMT on a different CPU, we won't expose a TSC
+ * that jumped backwards. It is due to the existence of the negative
+ * deltas that we don't recompute the delta with the master and
+ * worker interchanged to eliminate the remaining measurement error.
+ *
+ *
+ * @param pArgs The argument/state data.
+ * @param pMySync My synchronization structure.
+ * @param pOtherSync My partner's synchronization structure.
+ * @param fIsMaster Set if master, clear if worker.
+ * @param iTry The attempt number.
+ */
+static void supdrvTscDeltaMethod1Loop(PSUPDRVGIPTSCDELTARGS pArgs, PSUPTSCDELTASYNC2 pMySync, PSUPTSCDELTASYNC2 pOtherSync,
+ bool fIsMaster, uint32_t iTry)
+{
+ PSUPGIPCPU pGipCpuWorker = pArgs->pWorker;
+ PSUPGIPCPU pGipCpuMaster = pArgs->pMaster;
+ uint64_t uMinCmpReadTime = UINT64_MAX;
+ unsigned iLoop;
+ NOREF(iTry);
+
+ for (iLoop = 0; iLoop < GIP_TSC_DELTA_LOOPS; iLoop++)
+ {
+ RTCCUINTREG fEFlags;
+ if (fIsMaster)
+ {
+ /*
+ * The master.
+ */
+ AssertMsg(pGipCpuMaster->u64TSCSample == GIP_TSC_DELTA_RSVD,
+ ("%#llx idMaster=%#x idWorker=%#x (idGipMaster=%#x)\n",
+ pGipCpuMaster->u64TSCSample, pGipCpuMaster->idCpu, pGipCpuWorker->idCpu, pArgs->pDevExt->idGipMaster));
+ TSCDELTA_MASTER_SYNC_BEFORE(pMySync, pOtherSync, &fEFlags, pArgs);
+
+ do
+ {
+ ASMSerializeInstruction();
+ ASMAtomicWriteU64(&pGipCpuMaster->u64TSCSample, ASMReadTSC());
+ } while (pGipCpuMaster->u64TSCSample == GIP_TSC_DELTA_RSVD);
+
+ TSCDELTA_MASTER_SYNC_AFTER(pMySync, pOtherSync, fEFlags);
+
+ /* Process the data. */
+ if (iLoop > GIP_TSC_DELTA_PRIMER_LOOPS + GIP_TSC_DELTA_READ_TIME_LOOPS)
+ {
+ if (pGipCpuWorker->u64TSCSample != GIP_TSC_DELTA_RSVD)
+ {
+ int64_t iDelta = pGipCpuWorker->u64TSCSample
+ - (pGipCpuMaster->u64TSCSample - pGipCpuMaster->i64TSCDelta);
+ if ( iDelta >= GIP_TSC_DELTA_INITIAL_MASTER_VALUE
+ ? iDelta < pGipCpuWorker->i64TSCDelta
+ : iDelta > pGipCpuWorker->i64TSCDelta || pGipCpuWorker->i64TSCDelta == INT64_MAX)
+ pGipCpuWorker->i64TSCDelta = iDelta;
+ }
+ }
+
+ /* Reset our TSC sample and tell the worker to move on. */
+ ASMAtomicWriteU64(&pGipCpuMaster->u64TSCSample, GIP_TSC_DELTA_RSVD);
+ TSCDELTA_MASTER_KICK_OTHER_OUT_OF_AFTER(pMySync, pOtherSync);
+ }
+ else
+ {
+ /*
+ * The worker.
+ */
+ uint64_t uTscWorker;
+ uint64_t uTscWorkerFlushed;
+ uint64_t uCmpReadTime;
+
+ ASMAtomicReadU64(&pGipCpuMaster->u64TSCSample); /* Warm the cache line. */
+ TSCDELTA_OTHER_SYNC_BEFORE(pMySync, pOtherSync, &fEFlags, pArgs);
+
+ /*
+ * Keep reading the TSC until we notice that the master has read his. Reading
+ * the TSC -after- the master has updated the memory is way too late. We thus
+ * compensate by trying to measure how long it took for the worker to notice
+ * the memory flushed from the master.
+ */
+ do
+ {
+ ASMSerializeInstruction();
+ uTscWorker = ASMReadTSC();
+ } while (pGipCpuMaster->u64TSCSample == GIP_TSC_DELTA_RSVD);
+ ASMSerializeInstruction();
+ uTscWorkerFlushed = ASMReadTSC();
+
+ uCmpReadTime = uTscWorkerFlushed - uTscWorker;
+ if (iLoop > GIP_TSC_DELTA_PRIMER_LOOPS + GIP_TSC_DELTA_READ_TIME_LOOPS)
+ {
+ /* This is totally arbitrary a.k.a I don't like it but I have no better ideas for now. */
+ if (uCmpReadTime < (uMinCmpReadTime << 1))
+ {
+ ASMAtomicWriteU64(&pGipCpuWorker->u64TSCSample, uTscWorker);
+ if (uCmpReadTime < uMinCmpReadTime)
+ uMinCmpReadTime = uCmpReadTime;
+ }
+ else
+ ASMAtomicWriteU64(&pGipCpuWorker->u64TSCSample, GIP_TSC_DELTA_RSVD);
+ }
+ else if (iLoop > GIP_TSC_DELTA_PRIMER_LOOPS)
+ {
+ if (uCmpReadTime < uMinCmpReadTime)
+ uMinCmpReadTime = uCmpReadTime;
+ }
+
+ TSCDELTA_OTHER_SYNC_AFTER(pMySync, pOtherSync, fEFlags);
+ }
+ }
+
+ TSCDELTA_DBG_SYNC_MSG9(("sync/method1loop/%s: #92 iLoop=%u MyState=%#x\n", fIsMaster ? "master" : "worker", iLoop,
+ pMySync->uSyncVar));
+
+ /*
+ * We must reset the worker TSC sample value in case it gets picked as a
+ * GIP master later on (it's trashed above, naturally).
+ */
+ if (!fIsMaster)
+ ASMAtomicWriteU64(&pGipCpuWorker->u64TSCSample, GIP_TSC_DELTA_RSVD);
+}
+#endif /* GIP_TSC_DELTA_METHOD_1 */
+
+
+#ifdef GIP_TSC_DELTA_METHOD_2
+/*
+ * TSC delta measurement algorithm \#2 configuration and code - Experimental!!
+ */
+
+# define GIP_TSC_DELTA_M2_LOOPS (7 + GIP_TSC_DELTA_M2_PRIMER_LOOPS)
+# define GIP_TSC_DELTA_M2_PRIMER_LOOPS 0
+
+
+static void supdrvTscDeltaMethod2ProcessDataOnMaster(PSUPDRVGIPTSCDELTARGS pArgs)
+{
+ int64_t iMasterTscDelta = pArgs->pMaster->i64TSCDelta;
+ int64_t iBestDelta = pArgs->pWorker->i64TSCDelta;
+ uint32_t idxResult;
+ uint32_t cHits = 0;
+
+ /*
+ * Look for matching entries in the master and worker tables.
+ */
+ for (idxResult = 0; idxResult < RT_ELEMENTS(pArgs->uMaster.M2.Data.aResults); idxResult++)
+ {
+ uint32_t idxOther = pArgs->uMaster.M2.Data.aResults[idxResult].iSeqOther;
+ if (idxOther & 1)
+ {
+ idxOther >>= 1;
+ if (idxOther < RT_ELEMENTS(pArgs->uWorker.M2.Data.aResults))
+ {
+ if (pArgs->uWorker.M2.Data.aResults[idxOther].iSeqOther == pArgs->uMaster.M2.Data.aResults[idxResult].iSeqMine)
+ {
+ int64_t iDelta;
+ iDelta = pArgs->uWorker.M2.Data.aResults[idxOther].uTsc
+ - (pArgs->uMaster.M2.Data.aResults[idxResult].uTsc - iMasterTscDelta);
+ if ( iDelta >= GIP_TSC_DELTA_INITIAL_MASTER_VALUE
+ ? iDelta < iBestDelta
+ : iDelta > iBestDelta || iBestDelta == INT64_MAX)
+ iBestDelta = iDelta;
+ cHits++;
+ }
+ }
+ }
+ }
+
+ /*
+ * Save the results.
+ */
+ if (cHits > 2)
+ pArgs->pWorker->i64TSCDelta = iBestDelta;
+ pArgs->uMaster.M2.cHits += cHits;
+}
+
+
+/**
+ * The core function of the 2nd TSC delta measurement algorithm.
+ *
+ * The idea here is that we have the two CPUs execute the exact same code
+ * collecting a largish set of TSC samples. The code has one data dependency on
+ * the other CPU which intention it is to synchronize the execution as well as
+ * help cross references the two sets of TSC samples (the sequence numbers).
+ *
+ * The @a fLag parameter is used to modify the execution a tiny bit on one or
+ * both of the CPUs. When @a fLag differs between the CPUs, it is thought that
+ * it will help with making the CPUs enter lock step execution occasionally.
+ *
+ */
+static void supdrvTscDeltaMethod2CollectData(PSUPDRVTSCDELTAMETHOD2 pMyData, uint32_t volatile *piOtherSeqNo, bool fLag)
+{
+ SUPDRVTSCDELTAMETHOD2ENTRY *pEntry = &pMyData->aResults[0];
+ uint32_t cLeft = RT_ELEMENTS(pMyData->aResults);
+
+ ASMAtomicWriteU32(&pMyData->iCurSeqNo, 0);
+ ASMSerializeInstruction();
+ while (cLeft-- > 0)
+ {
+ uint64_t uTsc;
+ uint32_t iSeqMine = ASMAtomicIncU32(&pMyData->iCurSeqNo);
+ uint32_t iSeqOther = ASMAtomicReadU32(piOtherSeqNo);
+ ASMCompilerBarrier();
+ ASMSerializeInstruction(); /* Way better result than with ASMMemoryFenceSSE2() in this position! */
+ uTsc = ASMReadTSC();
+ ASMAtomicIncU32(&pMyData->iCurSeqNo);
+ ASMCompilerBarrier();
+ ASMSerializeInstruction();
+ pEntry->iSeqMine = iSeqMine;
+ pEntry->iSeqOther = iSeqOther;
+ pEntry->uTsc = uTsc;
+ pEntry++;
+ ASMSerializeInstruction();
+ if (fLag)
+ ASMNopPause();
+ }
+}
+
+
+/**
+ * TSC delta measurement algorithm \#2 (GIP_TSC_DELTA_METHOD_2).
+ *
+ * See supdrvTscDeltaMethod2CollectData for algorithm details.
+ *
+ * @param pArgs The argument/state data.
+ * @param pMySync My synchronization structure.
+ * @param pOtherSync My partner's synchronization structure.
+ * @param fIsMaster Set if master, clear if worker.
+ * @param iTry The attempt number.
+ */
+static void supdrvTscDeltaMethod2Loop(PSUPDRVGIPTSCDELTARGS pArgs, PSUPTSCDELTASYNC2 pMySync, PSUPTSCDELTASYNC2 pOtherSync,
+ bool fIsMaster, uint32_t iTry)
+{
+ unsigned iLoop;
+ RT_NOREF1(iTry);
+
+ for (iLoop = 0; iLoop < GIP_TSC_DELTA_M2_LOOPS; iLoop++)
+ {
+ RTCCUINTREG fEFlags;
+ if (fIsMaster)
+ {
+ /*
+ * Adjust the loop lag fudge.
+ */
+# if GIP_TSC_DELTA_M2_PRIMER_LOOPS > 0
+ if (iLoop < GIP_TSC_DELTA_M2_PRIMER_LOOPS)
+ {
+ /* Lag during the priming to be nice to everyone.. */
+ pArgs->uMaster.M2.fLag = true;
+ pArgs->uWorker.M2.fLag = true;
+ }
+ else
+# endif
+ if (iLoop < (GIP_TSC_DELTA_M2_LOOPS - GIP_TSC_DELTA_M2_PRIMER_LOOPS) / 4)
+ {
+ /* 25 % of the body without lagging. */
+ pArgs->uMaster.M2.fLag = false;
+ pArgs->uWorker.M2.fLag = false;
+ }
+ else if (iLoop < (GIP_TSC_DELTA_M2_LOOPS - GIP_TSC_DELTA_M2_PRIMER_LOOPS) / 4 * 2)
+ {
+ /* 25 % of the body with both lagging. */
+ pArgs->uMaster.M2.fLag = true;
+ pArgs->uWorker.M2.fLag = true;
+ }
+ else
+ {
+ /* 50% of the body with alternating lag. */
+ pArgs->uMaster.M2.fLag = (iLoop & 1) == 0;
+ pArgs->uWorker.M2.fLag= (iLoop & 1) == 1;
+ }
+
+ /*
+ * Sync up with the worker and collect data.
+ */
+ TSCDELTA_MASTER_SYNC_BEFORE(pMySync, pOtherSync, &fEFlags, pArgs);
+ supdrvTscDeltaMethod2CollectData(&pArgs->uMaster.M2.Data, &pArgs->uWorker.M2.Data.iCurSeqNo, pArgs->uMaster.M2.fLag);
+ TSCDELTA_MASTER_SYNC_AFTER(pMySync, pOtherSync, fEFlags);
+
+ /*
+ * Process the data.
+ */
+# if GIP_TSC_DELTA_M2_PRIMER_LOOPS > 0
+ if (iLoop >= GIP_TSC_DELTA_M2_PRIMER_LOOPS)
+# endif
+ supdrvTscDeltaMethod2ProcessDataOnMaster(pArgs);
+
+ TSCDELTA_MASTER_KICK_OTHER_OUT_OF_AFTER(pMySync, pOtherSync);
+ }
+ else
+ {
+ /*
+ * The worker.
+ */
+ TSCDELTA_OTHER_SYNC_BEFORE(pMySync, pOtherSync, &fEFlags, pArgs);
+ supdrvTscDeltaMethod2CollectData(&pArgs->uWorker.M2.Data, &pArgs->uMaster.M2.Data.iCurSeqNo, pArgs->uWorker.M2.fLag);
+ TSCDELTA_OTHER_SYNC_AFTER(pMySync, pOtherSync, fEFlags);
+ }
+ }
+}
+
+#endif /* GIP_TSC_DELTA_METHOD_2 */
+
+
+
+static int supdrvTscDeltaVerify(PSUPDRVGIPTSCDELTARGS pArgs, PSUPTSCDELTASYNC2 pMySync,
+ PSUPTSCDELTASYNC2 pOtherSync, bool fIsMaster, int64_t iWorkerTscDelta)
+{
+ /*PSUPGIPCPU pGipCpuWorker = pArgs->pWorker; - unused */
+ PSUPGIPCPU pGipCpuMaster = pArgs->pMaster;
+ uint32_t i;
+ TSCDELTA_DBG_VARS();
+
+ for (;;)
+ {
+ RTCCUINTREG fEFlags;
+ AssertCompile((RT_ELEMENTS(pArgs->uMaster.Verify.auTscs) & 1) == 0);
+ AssertCompile(RT_ELEMENTS(pArgs->uMaster.Verify.auTscs) == RT_ELEMENTS(pArgs->uWorker.Verify.auTscs));
+
+ if (fIsMaster)
+ {
+ uint64_t uTscWorker;
+ TSCDELTA_MASTER_SYNC_BEFORE(pMySync, pOtherSync, &fEFlags, pArgs);
+
+ /*
+ * Collect TSC, master goes first.
+ */
+ for (i = 0; i < RT_ELEMENTS(pArgs->uMaster.Verify.auTscs); i += 2)
+ {
+ /* Read, kick & wait #1. */
+ uint64_t uTsc = ASMReadTSC();
+ ASMAtomicWriteU32(&pOtherSync->uSyncVar, GIP_TSC_DELTA_SYNC2_GO_GO);
+ ASMSerializeInstruction();
+ pArgs->uMaster.Verify.auTscs[i] = uTsc;
+ TSCDELTA_DBG_START_LOOP();
+ while (ASMAtomicReadU32(&pMySync->uSyncVar) == GIP_TSC_DELTA_SYNC2_GO)
+ {
+ TSCDELTA_DBG_CHECK_LOOP();
+ ASMNopPause();
+ }
+
+ /* Read, kick & wait #2. */
+ uTsc = ASMReadTSC();
+ ASMAtomicWriteU32(&pOtherSync->uSyncVar, GIP_TSC_DELTA_SYNC2_GO);
+ ASMSerializeInstruction();
+ pArgs->uMaster.Verify.auTscs[i + 1] = uTsc;
+ TSCDELTA_DBG_START_LOOP();
+ while (ASMAtomicReadU32(&pMySync->uSyncVar) == GIP_TSC_DELTA_SYNC2_GO_GO)
+ {
+ TSCDELTA_DBG_CHECK_LOOP();
+ ASMNopPause();
+ }
+ }
+
+ TSCDELTA_MASTER_SYNC_AFTER(pMySync, pOtherSync, fEFlags);
+
+ /*
+ * Process the data.
+ */
+#ifdef TSCDELTA_VERIFY_WITH_STATS
+ pArgs->cMaxVerifyTscTicks = INT64_MIN;
+ pArgs->cMinVerifyTscTicks = INT64_MAX;
+ pArgs->iVerifyBadTscDiff = 0;
+#endif
+ ASMAtomicWriteS32(&pArgs->rcVerify, VINF_SUCCESS);
+ uTscWorker = 0;
+ for (i = 0; i < RT_ELEMENTS(pArgs->uMaster.Verify.auTscs); i++)
+ {
+ /* Master vs previous worker entry. */
+ uint64_t uTscMaster = pArgs->uMaster.Verify.auTscs[i] - pGipCpuMaster->i64TSCDelta;
+ int64_t iDiff;
+ if (i > 0)
+ {
+ iDiff = uTscMaster - uTscWorker;
+#ifdef TSCDELTA_VERIFY_WITH_STATS
+ if (iDiff > pArgs->cMaxVerifyTscTicks)
+ pArgs->cMaxVerifyTscTicks = iDiff;
+ if (iDiff < pArgs->cMinVerifyTscTicks)
+ pArgs->cMinVerifyTscTicks = iDiff;
+#endif
+ if (iDiff < 0)
+ {
+#ifdef TSCDELTA_VERIFY_WITH_STATS
+ pArgs->iVerifyBadTscDiff = -iDiff;
+#endif
+ ASMAtomicWriteS32(&pArgs->rcVerify, VERR_OUT_OF_RANGE);
+ break;
+ }
+ }
+
+ /* Worker vs master. */
+ uTscWorker = pArgs->uWorker.Verify.auTscs[i] - iWorkerTscDelta;
+ iDiff = uTscWorker - uTscMaster;
+#ifdef TSCDELTA_VERIFY_WITH_STATS
+ if (iDiff > pArgs->cMaxVerifyTscTicks)
+ pArgs->cMaxVerifyTscTicks = iDiff;
+ if (iDiff < pArgs->cMinVerifyTscTicks)
+ pArgs->cMinVerifyTscTicks = iDiff;
+#endif
+ if (iDiff < 0)
+ {
+#ifdef TSCDELTA_VERIFY_WITH_STATS
+ pArgs->iVerifyBadTscDiff = iDiff;
+#endif
+ ASMAtomicWriteS32(&pArgs->rcVerify, VERR_OUT_OF_RANGE);
+ break;
+ }
+ }
+
+ /* Done. */
+ TSCDELTA_MASTER_KICK_OTHER_OUT_OF_AFTER(pMySync, pOtherSync);
+ }
+ else
+ {
+ /*
+ * The worker, master leads.
+ */
+ TSCDELTA_OTHER_SYNC_BEFORE(pMySync, pOtherSync, &fEFlags, pArgs);
+
+ for (i = 0; i < RT_ELEMENTS(pArgs->uWorker.Verify.auTscs); i += 2)
+ {
+ uint64_t uTsc;
+
+ /* Wait, Read and Kick #1. */
+ TSCDELTA_DBG_START_LOOP();
+ while (ASMAtomicReadU32(&pMySync->uSyncVar) == GIP_TSC_DELTA_SYNC2_GO)
+ {
+ TSCDELTA_DBG_CHECK_LOOP();
+ ASMNopPause();
+ }
+ uTsc = ASMReadTSC();
+ ASMAtomicWriteU32(&pOtherSync->uSyncVar, GIP_TSC_DELTA_SYNC2_GO_GO);
+ ASMSerializeInstruction();
+ pArgs->uWorker.Verify.auTscs[i] = uTsc;
+
+ /* Wait, Read and Kick #2. */
+ TSCDELTA_DBG_START_LOOP();
+ while (ASMAtomicReadU32(&pMySync->uSyncVar) == GIP_TSC_DELTA_SYNC2_GO_GO)
+ {
+ TSCDELTA_DBG_CHECK_LOOP();
+ ASMNopPause();
+ }
+ uTsc = ASMReadTSC();
+ ASMAtomicWriteU32(&pOtherSync->uSyncVar, GIP_TSC_DELTA_SYNC2_GO);
+ ASMSerializeInstruction();
+ pArgs->uWorker.Verify.auTscs[i + 1] = uTsc;
+ }
+
+ TSCDELTA_OTHER_SYNC_AFTER(pMySync, pOtherSync, fEFlags);
+ }
+ return pArgs->rcVerify;
+ }
+
+ /*
+ * Timed out, please retry.
+ */
+ ASMAtomicWriteS32(&pArgs->rcVerify, VERR_TRY_AGAIN);
+ return VERR_TIMEOUT;
+}
+
+
+
+/**
+ * Handles the special abort procedure during synchronization setup in
+ * supdrvTscMeasureDeltaCallbackUnwrapped().
+ *
+ * @returns 0 (dummy, ignored)
+ * @param pArgs Pointer to argument/state data.
+ * @param pMySync Pointer to my sync structure.
+ * @param fIsMaster Set if we're the master, clear if worker.
+ * @param fTimeout Set if it's a timeout.
+ */
+DECL_NO_INLINE(static, int)
+supdrvTscMeasureDeltaCallbackAbortSyncSetup(PSUPDRVGIPTSCDELTARGS pArgs, PSUPTSCDELTASYNC2 pMySync, bool fIsMaster, bool fTimeout)
+{
+ PSUPTSCDELTASYNC2 volatile *ppMySync = fIsMaster ? &pArgs->pSyncMaster : &pArgs->pSyncWorker;
+ PSUPTSCDELTASYNC2 volatile *ppOtherSync = fIsMaster ? &pArgs->pSyncWorker : &pArgs->pSyncMaster;
+ TSCDELTA_DBG_VARS();
+ RT_NOREF1(pMySync);
+
+ /*
+ * Clear our sync pointer and make sure the abort flag is set.
+ */
+ ASMAtomicWriteNullPtr(ppMySync);
+ ASMAtomicWriteBool(&pArgs->fAbortSetup, true);
+ if (fTimeout)
+ ASMAtomicWriteBool(&pArgs->fTimedOut, true);
+
+ /*
+ * Make sure the other party is out of there and won't be touching our
+ * sync state again (would cause stack corruption).
+ */
+ TSCDELTA_DBG_START_LOOP();
+ while (ASMAtomicReadPtrT(ppOtherSync, PSUPTSCDELTASYNC2) != NULL)
+ {
+ ASMNopPause();
+ ASMNopPause();
+ ASMNopPause();
+ TSCDELTA_DBG_CHECK_LOOP();
+ }
+
+ return 0;
+}
+
+
+/**
+ * This is used by supdrvTscMeasureInitialDeltas() to read the TSC on two CPUs
+ * and compute the delta between them.
+ *
+ * To reduce code size a good when timeout handling was added, a dummy return
+ * value had to be added (saves 1-3 lines per timeout case), thus this
+ * 'Unwrapped' function and the dummy 0 return value.
+ *
+ * @returns 0 (dummy, ignored)
+ * @param idCpu The CPU we are current scheduled on.
+ * @param pArgs Pointer to a parameter package.
+ *
+ * @remarks Measuring TSC deltas between the CPUs is tricky because we need to
+ * read the TSC at exactly the same time on both the master and the
+ * worker CPUs. Due to DMA, bus arbitration, cache locality,
+ * contention, SMI, pipelining etc. there is no guaranteed way of
+ * doing this on x86 CPUs.
+ */
+static int supdrvTscMeasureDeltaCallbackUnwrapped(RTCPUID idCpu, PSUPDRVGIPTSCDELTARGS pArgs)
+{
+ PSUPDRVDEVEXT pDevExt = pArgs->pDevExt;
+ PSUPGIPCPU pGipCpuWorker = pArgs->pWorker;
+ PSUPGIPCPU pGipCpuMaster = pArgs->pMaster;
+ bool const fIsMaster = idCpu == pGipCpuMaster->idCpu;
+ uint32_t iTry;
+ PSUPTSCDELTASYNC2 volatile *ppMySync = fIsMaster ? &pArgs->pSyncMaster : &pArgs->pSyncWorker;
+ PSUPTSCDELTASYNC2 volatile *ppOtherSync = fIsMaster ? &pArgs->pSyncWorker : &pArgs->pSyncMaster;
+ SUPTSCDELTASYNC2 MySync;
+ PSUPTSCDELTASYNC2 pOtherSync;
+ int rc;
+ TSCDELTA_DBG_VARS();
+
+ /* A bit of paranoia first. */
+ if (!pGipCpuMaster || !pGipCpuWorker)
+ return 0;
+
+ /*
+ * If the CPU isn't part of the measurement, return immediately.
+ */
+ if ( !fIsMaster
+ && idCpu != pGipCpuWorker->idCpu)
+ return 0;
+
+ /*
+ * Set up my synchronization stuff and wait for the other party to show up.
+ *
+ * We don't wait forever since the other party may be off fishing (offline,
+ * spinning with ints disables, whatever), we must play nice to the rest of
+ * the system as this context generally isn't one in which we will get
+ * preempted and we may hold up a number of lower priority interrupts.
+ */
+ ASMAtomicWriteU32(&MySync.uSyncVar, GIP_TSC_DELTA_SYNC2_PRESTART_WAIT);
+ ASMAtomicWritePtr(ppMySync, &MySync);
+ MySync.uTscStart = ASMReadTSC();
+ MySync.cMaxTscTicks = pArgs->cMaxTscTicks;
+
+ /* Look for the partner, might not be here yet... Special abort considerations. */
+ iTry = 0;
+ TSCDELTA_DBG_START_LOOP();
+ while ((pOtherSync = ASMAtomicReadPtrT(ppOtherSync, PSUPTSCDELTASYNC2)) == NULL)
+ {
+ ASMNopPause();
+ if ( ASMAtomicReadBool(&pArgs->fAbortSetup)
+ || !RTMpIsCpuOnline(fIsMaster ? pGipCpuWorker->idCpu : pGipCpuMaster->idCpu) )
+ return supdrvTscMeasureDeltaCallbackAbortSyncSetup(pArgs, &MySync, fIsMaster, false /*fTimeout*/);
+ if ( (iTry++ & 0xff) == 0
+ && ASMReadTSC() - MySync.uTscStart > pArgs->cMaxTscTicks)
+ return supdrvTscMeasureDeltaCallbackAbortSyncSetup(pArgs, &MySync, fIsMaster, true /*fTimeout*/);
+ TSCDELTA_DBG_CHECK_LOOP();
+ ASMNopPause();
+ }
+
+ /* I found my partner, waiting to be found... Special abort considerations. */
+ if (fIsMaster)
+ if (!ASMAtomicCmpXchgU32(&pOtherSync->uSyncVar, GIP_TSC_DELTA_SYNC2_READY, GIP_TSC_DELTA_SYNC2_PRESTART_WAIT)) /* parnaoia */
+ return supdrvTscMeasureDeltaCallbackAbortSyncSetup(pArgs, &MySync, fIsMaster, false /*fTimeout*/);
+
+ iTry = 0;
+ TSCDELTA_DBG_START_LOOP();
+ while (ASMAtomicReadU32(&MySync.uSyncVar) == GIP_TSC_DELTA_SYNC2_PRESTART_WAIT)
+ {
+ ASMNopPause();
+ if (ASMAtomicReadBool(&pArgs->fAbortSetup))
+ return supdrvTscMeasureDeltaCallbackAbortSyncSetup(pArgs, &MySync, fIsMaster, false /*fTimeout*/);
+ if ( (iTry++ & 0xff) == 0
+ && ASMReadTSC() - MySync.uTscStart > pArgs->cMaxTscTicks)
+ {
+ if ( fIsMaster
+ && !ASMAtomicCmpXchgU32(&MySync.uSyncVar, GIP_TSC_DELTA_SYNC2_PRESTART_ABORT, GIP_TSC_DELTA_SYNC2_PRESTART_WAIT))
+ break; /* race #1: slave has moved on, handle timeout in loop instead. */
+ return supdrvTscMeasureDeltaCallbackAbortSyncSetup(pArgs, &MySync, fIsMaster, true /*fTimeout*/);
+ }
+ TSCDELTA_DBG_CHECK_LOOP();
+ }
+
+ if (!fIsMaster)
+ if (!ASMAtomicCmpXchgU32(&pOtherSync->uSyncVar, GIP_TSC_DELTA_SYNC2_READY, GIP_TSC_DELTA_SYNC2_PRESTART_WAIT)) /* race #1 */
+ return supdrvTscMeasureDeltaCallbackAbortSyncSetup(pArgs, &MySync, fIsMaster, false /*fTimeout*/);
+
+/** @todo Add a resumable state to pArgs so we don't waste time if we time
+ * out or something. Timeouts are legit, any of the two CPUs may get
+ * interrupted. */
+
+ /*
+ * Start by seeing if we have a zero delta between the two CPUs.
+ * This should normally be the case.
+ */
+ rc = supdrvTscDeltaVerify(pArgs, &MySync, pOtherSync, fIsMaster, GIP_TSC_DELTA_INITIAL_MASTER_VALUE);
+ if (RT_SUCCESS(rc))
+ {
+ if (fIsMaster)
+ {
+ ASMAtomicWriteS64(&pGipCpuWorker->i64TSCDelta, GIP_TSC_DELTA_INITIAL_MASTER_VALUE);
+ RTCpuSetDelByIndex(&pDevExt->TscDeltaCpuSet, pGipCpuWorker->iCpuSet);
+ RTCpuSetAddByIndex(&pDevExt->TscDeltaObtainedCpuSet, pGipCpuWorker->iCpuSet);
+ }
+ }
+ /*
+ * If the verification didn't time out, do regular delta measurements.
+ * We retry this until we get a reasonable value.
+ */
+ else if (rc != VERR_TIMEOUT)
+ {
+ Assert(pGipCpuWorker->i64TSCDelta == INT64_MAX);
+ for (iTry = 0; iTry < 12; iTry++)
+ {
+ /*
+ * Check the state before we start.
+ */
+ uint32_t u32Tmp = ASMAtomicReadU32(&MySync.uSyncVar);
+ if ( u32Tmp != GIP_TSC_DELTA_SYNC2_READY
+ && (fIsMaster || u32Tmp != GIP_TSC_DELTA_SYNC2_STEADY) /* worker may be late prepping for the next round */ )
+ {
+ TSCDELTA_DBG_SYNC_MSG(("sync/loop/%s: #0 iTry=%u MyState=%#x\n", fIsMaster ? "master" : "worker", iTry, u32Tmp));
+ break;
+ }
+
+ /*
+ * Do the measurements.
+ */
+#ifdef GIP_TSC_DELTA_METHOD_1
+ supdrvTscDeltaMethod1Loop(pArgs, &MySync, pOtherSync, fIsMaster, iTry);
+#elif defined(GIP_TSC_DELTA_METHOD_2)
+ supdrvTscDeltaMethod2Loop(pArgs, &MySync, pOtherSync, fIsMaster, iTry);
+#else
+# error "huh??"
+#endif
+
+ /*
+ * Check the state.
+ */
+ u32Tmp = ASMAtomicReadU32(&MySync.uSyncVar);
+ if ( u32Tmp != GIP_TSC_DELTA_SYNC2_READY
+ && (fIsMaster || u32Tmp != GIP_TSC_DELTA_SYNC2_STEADY) /* worker may be late prepping for the next round */ )
+ {
+ if (fIsMaster)
+ TSCDELTA_DBG_SYNC_MSG(("sync/loop/master: #1 iTry=%u MyState=%#x\n", iTry, u32Tmp));
+ else
+ TSCDELTA_DBG_SYNC_MSG2(("sync/loop/worker: #1 iTry=%u MyState=%#x\n", iTry, u32Tmp));
+ break;
+ }
+
+ /*
+ * Success? If so, stop trying. Master decides.
+ */
+ if (fIsMaster)
+ {
+ if (pGipCpuWorker->i64TSCDelta != INT64_MAX)
+ {
+ RTCpuSetDelByIndex(&pDevExt->TscDeltaCpuSet, pGipCpuWorker->iCpuSet);
+ RTCpuSetAddByIndex(&pDevExt->TscDeltaObtainedCpuSet, pGipCpuWorker->iCpuSet);
+ TSCDELTA_DBG_SYNC_MSG2(("sync/loop/master: #9 iTry=%u MyState=%#x\n", iTry, MySync.uSyncVar));
+ break;
+ }
+ }
+ }
+ if (fIsMaster)
+ pArgs->iTry = iTry;
+ }
+
+ /*
+ * End the synchronization dance. We tell the other that we're done,
+ * then wait for the same kind of reply.
+ */
+ ASMAtomicWriteU32(&pOtherSync->uSyncVar, GIP_TSC_DELTA_SYNC2_FINAL);
+ ASMAtomicWriteNullPtr(ppMySync);
+ iTry = 0;
+ TSCDELTA_DBG_START_LOOP();
+ while (ASMAtomicReadU32(&MySync.uSyncVar) != GIP_TSC_DELTA_SYNC2_FINAL)
+ {
+ iTry++;
+ if ( iTry == 0
+ && !RTMpIsCpuOnline(fIsMaster ? pGipCpuWorker->idCpu : pGipCpuMaster->idCpu))
+ break; /* this really shouldn't happen. */
+ TSCDELTA_DBG_CHECK_LOOP();
+ ASMNopPause();
+ }
+
+ /*
+ * Collect some runtime stats.
+ */
+ if (fIsMaster)
+ pArgs->cElapsedMasterTscTicks = ASMReadTSC() - MySync.uTscStart;
+ else
+ pArgs->cElapsedWorkerTscTicks = ASMReadTSC() - MySync.uTscStart;
+ return 0;
+}
+
+/**
+ * Callback used by supdrvTscMeasureInitialDeltas() to read the TSC on two CPUs
+ * and compute the delta between them.
+ *
+ * @param idCpu The CPU we are current scheduled on.
+ * @param pvUser1 Pointer to a parameter package (SUPDRVGIPTSCDELTARGS).
+ * @param pvUser2 Unused.
+ */
+static DECLCALLBACK(void) supdrvTscMeasureDeltaCallback(RTCPUID idCpu, void *pvUser1, void *pvUser2)
+{
+ supdrvTscMeasureDeltaCallbackUnwrapped(idCpu, (PSUPDRVGIPTSCDELTARGS)pvUser1);
+ RT_NOREF1(pvUser2);
+}
+
+
+/**
+ * Measures the TSC delta between the master GIP CPU and one specified worker
+ * CPU.
+ *
+ * @returns VBox status code.
+ * @retval VERR_SUPDRV_TSC_DELTA_MEASUREMENT_FAILED on pure measurement
+ * failure.
+ * @param pDevExt Pointer to the device instance data.
+ * @param idxWorker The index of the worker CPU from the GIP's array of
+ * CPUs.
+ *
+ * @remarks This must be called with preemption enabled!
+ */
+static int supdrvTscMeasureDeltaOne(PSUPDRVDEVEXT pDevExt, uint32_t idxWorker)
+{
+ int rc;
+ int rc2;
+ PSUPGLOBALINFOPAGE pGip = pDevExt->pGip;
+ RTCPUID idMaster = pDevExt->idGipMaster;
+ PSUPGIPCPU pGipCpuWorker = &pGip->aCPUs[idxWorker];
+ PSUPGIPCPU pGipCpuMaster;
+ uint32_t iGipCpuMaster;
+ uint32_t u32Tmp;
+
+ /* Validate input a bit. */
+ AssertReturn(pGip, VERR_INVALID_PARAMETER);
+ Assert(pGip->enmUseTscDelta > SUPGIPUSETSCDELTA_ZERO_CLAIMED);
+ Assert(RTThreadPreemptIsEnabled(NIL_RTTHREAD));
+
+ /*
+ * Don't attempt measuring the delta for the GIP master.
+ */
+ if (pGipCpuWorker->idCpu == idMaster)
+ {
+ if (pGipCpuWorker->i64TSCDelta == INT64_MAX) /* This shouldn't happen, but just in case. */
+ ASMAtomicWriteS64(&pGipCpuWorker->i64TSCDelta, GIP_TSC_DELTA_INITIAL_MASTER_VALUE);
+ return VINF_SUCCESS;
+ }
+
+ /*
+ * One measurement at a time, at least for now. We might be using
+ * broadcast IPIs so, so be nice to the rest of the system.
+ */
+#ifdef SUPDRV_USE_MUTEX_FOR_GIP
+ rc = RTSemMutexRequest(pDevExt->mtxTscDelta, RT_INDEFINITE_WAIT);
+#else
+ rc = RTSemFastMutexRequest(pDevExt->mtxTscDelta);
+#endif
+ if (RT_FAILURE(rc))
+ return rc;
+
+ /*
+ * If the CPU has hyper-threading and the APIC IDs of the master and worker are adjacent,
+ * try pick a different master. (This fudge only works with multi core systems.)
+ * ASSUMES related threads have adjacent APIC IDs. ASSUMES two threads per core.
+ *
+ * We skip this on AMDs for now as their HTT is different from Intel's and
+ * it doesn't seem to have any favorable effect on the results.
+ *
+ * If the master is offline, we need a new master too, so share the code.
+ */
+ iGipCpuMaster = supdrvGipFindCpuIndexForCpuId(pGip, idMaster);
+ AssertReturn(iGipCpuMaster < pGip->cCpus, VERR_INVALID_CPU_ID);
+ pGipCpuMaster = &pGip->aCPUs[iGipCpuMaster];
+ if ( ( (pGipCpuMaster->idApic & ~1) == (pGipCpuWorker->idApic & ~1)
+ && pGip->cOnlineCpus > 2
+ && ASMHasCpuId()
+ && RTX86IsValidStdRange(ASMCpuId_EAX(0))
+ && (ASMCpuId_EDX(1) & X86_CPUID_FEATURE_EDX_HTT)
+ && ( !ASMIsAmdCpu()
+ || RTX86GetCpuFamily(u32Tmp = ASMCpuId_EAX(1)) > 0x15
+ || ( RTX86GetCpuFamily(u32Tmp) == 0x15 /* Piledriver+, not bulldozer (FX-4150 didn't like it). */
+ && RTX86GetCpuModelAMD(u32Tmp) >= 0x02) ) )
+ || !RTMpIsCpuOnline(idMaster) )
+ {
+ uint32_t i;
+ for (i = 0; i < pGip->cCpus; i++)
+ if ( i != iGipCpuMaster
+ && i != idxWorker
+ && pGip->aCPUs[i].enmState == SUPGIPCPUSTATE_ONLINE
+ && pGip->aCPUs[i].i64TSCDelta != INT64_MAX
+ && pGip->aCPUs[i].idCpu != NIL_RTCPUID
+ && pGip->aCPUs[i].idCpu != idMaster /* paranoia starts here... */
+ && pGip->aCPUs[i].idCpu != pGipCpuWorker->idCpu
+ && pGip->aCPUs[i].idApic != pGipCpuWorker->idApic
+ && pGip->aCPUs[i].idApic != pGipCpuMaster->idApic
+ && RTMpIsCpuOnline(pGip->aCPUs[i].idCpu))
+ {
+ iGipCpuMaster = i;
+ pGipCpuMaster = &pGip->aCPUs[i];
+ idMaster = pGipCpuMaster->idCpu;
+ break;
+ }
+ }
+
+ if (RTCpuSetIsMemberByIndex(&pGip->OnlineCpuSet, pGipCpuWorker->iCpuSet))
+ {
+ /*
+ * Initialize data package for the RTMpOnPair callback.
+ */
+ PSUPDRVGIPTSCDELTARGS pArgs = (PSUPDRVGIPTSCDELTARGS)RTMemAllocZ(sizeof(*pArgs));
+ if (pArgs)
+ {
+ pArgs->pWorker = pGipCpuWorker;
+ pArgs->pMaster = pGipCpuMaster;
+ pArgs->pDevExt = pDevExt;
+ pArgs->pSyncMaster = NULL;
+ pArgs->pSyncWorker = NULL;
+ pArgs->cMaxTscTicks = ASMAtomicReadU64(&pGip->u64CpuHz) / 512; /* 1953 us */
+
+ /*
+ * Do the RTMpOnPair call. We reset i64TSCDelta first so we
+ * and supdrvTscMeasureDeltaCallback can use it as a success check.
+ */
+ /** @todo Store the i64TSCDelta result in pArgs first? Perhaps deals with
+ * that when doing the restart loop reorg. */
+ ASMAtomicWriteS64(&pGipCpuWorker->i64TSCDelta, INT64_MAX);
+ rc = RTMpOnPair(pGipCpuMaster->idCpu, pGipCpuWorker->idCpu, RTMPON_F_CONCURRENT_EXEC,
+ supdrvTscMeasureDeltaCallback, pArgs, NULL);
+ if (RT_SUCCESS(rc))
+ {
+#if 0
+ SUPR0Printf("mponpair ticks: %9llu %9llu max: %9llu iTry: %u%s\n", pArgs->cElapsedMasterTscTicks,
+ pArgs->cElapsedWorkerTscTicks, pArgs->cMaxTscTicks, pArgs->iTry,
+ pArgs->fTimedOut ? " timed out" :"");
+#endif
+#if 0
+ SUPR0Printf("rcVerify=%d iVerifyBadTscDiff=%lld cMinVerifyTscTicks=%lld cMaxVerifyTscTicks=%lld\n",
+ pArgs->rcVerify, pArgs->iVerifyBadTscDiff, pArgs->cMinVerifyTscTicks, pArgs->cMaxVerifyTscTicks);
+#endif
+ if (RT_LIKELY(pGipCpuWorker->i64TSCDelta != INT64_MAX))
+ {
+ /*
+ * Work the TSC delta applicability rating. It starts
+ * optimistic in supdrvGipInit, we downgrade it here.
+ */
+ SUPGIPUSETSCDELTA enmRating;
+ if ( pGipCpuWorker->i64TSCDelta > GIP_TSC_DELTA_THRESHOLD_ROUGHLY_ZERO
+ || pGipCpuWorker->i64TSCDelta < -GIP_TSC_DELTA_THRESHOLD_ROUGHLY_ZERO)
+ enmRating = SUPGIPUSETSCDELTA_NOT_ZERO;
+ else if ( pGipCpuWorker->i64TSCDelta > GIP_TSC_DELTA_THRESHOLD_PRACTICALLY_ZERO
+ || pGipCpuWorker->i64TSCDelta < -GIP_TSC_DELTA_THRESHOLD_PRACTICALLY_ZERO)
+ enmRating = SUPGIPUSETSCDELTA_ROUGHLY_ZERO;
+ else
+ enmRating = SUPGIPUSETSCDELTA_PRACTICALLY_ZERO;
+ if (pGip->enmUseTscDelta < enmRating)
+ {
+ AssertCompile(sizeof(pGip->enmUseTscDelta) == sizeof(uint32_t));
+ ASMAtomicWriteU32((uint32_t volatile *)&pGip->enmUseTscDelta, enmRating);
+ }
+ }
+ else
+ rc = VERR_SUPDRV_TSC_DELTA_MEASUREMENT_FAILED;
+ }
+ /** @todo return try-again if we get an offline CPU error. */
+
+ RTMemFree(pArgs);
+ }
+ else
+ rc = VERR_NO_MEMORY;
+ }
+ else
+ rc = VERR_CPU_OFFLINE;
+
+ /*
+ * We're done now.
+ */
+#ifdef SUPDRV_USE_MUTEX_FOR_GIP
+ rc2 = RTSemMutexRelease(pDevExt->mtxTscDelta); AssertRC(rc2);
+#else
+ rc2 = RTSemFastMutexRelease(pDevExt->mtxTscDelta); AssertRC(rc2);
+#endif
+ return rc;
+}
+
+
+/**
+ * Resets the TSC-delta related TSC samples and optionally the deltas
+ * themselves.
+ *
+ * @param pDevExt Pointer to the device instance data.
+ * @param fResetTscDeltas Whether the TSC-deltas are also to be reset.
+ *
+ * @remarks This might be called while holding a spinlock!
+ */
+static void supdrvTscResetSamples(PSUPDRVDEVEXT pDevExt, bool fResetTscDeltas)
+{
+ unsigned iCpu;
+ PSUPGLOBALINFOPAGE pGip = pDevExt->pGip;
+ for (iCpu = 0; iCpu < pGip->cCpus; iCpu++)
+ {
+ PSUPGIPCPU pGipCpu = &pGip->aCPUs[iCpu];
+ ASMAtomicWriteU64(&pGipCpu->u64TSCSample, GIP_TSC_DELTA_RSVD);
+ if (fResetTscDeltas)
+ {
+ RTCpuSetDelByIndex(&pDevExt->TscDeltaObtainedCpuSet, pGipCpu->iCpuSet);
+ ASMAtomicWriteS64(&pGipCpu->i64TSCDelta, INT64_MAX);
+ }
+ }
+}
+
+
+/**
+ * Picks an online CPU as the master TSC for TSC-delta computations.
+ *
+ * @returns VBox status code.
+ * @param pDevExt Pointer to the device instance data.
+ * @param pidxMaster Where to store the CPU array index of the chosen
+ * master. Optional, can be NULL.
+ */
+static int supdrvTscPickMaster(PSUPDRVDEVEXT pDevExt, uint32_t *pidxMaster)
+{
+ /*
+ * Pick the first CPU online as the master TSC and make it the new GIP master based
+ * on the APIC ID.
+ *
+ * Technically we can simply use "idGipMaster" but doing this gives us master as CPU 0
+ * in most cases making it nicer/easier for comparisons. It is safe to update the GIP
+ * master as this point since the sync/async timer isn't created yet.
+ */
+ unsigned iCpu;
+ uint32_t idxMaster = UINT32_MAX;
+ PSUPGLOBALINFOPAGE pGip = pDevExt->pGip;
+ for (iCpu = 0; iCpu < RT_ELEMENTS(pGip->aiCpuFromApicId); iCpu++)
+ {
+ uint16_t idxCpu = pGip->aiCpuFromApicId[iCpu];
+ if (idxCpu != UINT16_MAX)
+ {
+ PSUPGIPCPU pGipCpu = &pGip->aCPUs[idxCpu];
+ if (RTCpuSetIsMemberByIndex(&pGip->OnlineCpuSet, pGipCpu->iCpuSet))
+ {
+ idxMaster = idxCpu;
+ pGipCpu->i64TSCDelta = GIP_TSC_DELTA_INITIAL_MASTER_VALUE;
+ ASMAtomicWriteSize(&pDevExt->idGipMaster, pGipCpu->idCpu);
+ if (pidxMaster)
+ *pidxMaster = idxMaster;
+ return VINF_SUCCESS;
+ }
+ }
+ }
+ return VERR_CPU_OFFLINE;
+}
+
+
+/**
+ * Performs the initial measurements of the TSC deltas between CPUs.
+ *
+ * This is called by supdrvGipCreate(), supdrvGipPowerNotificationCallback() or
+ * triggered by it if threaded.
+ *
+ * @returns VBox status code.
+ * @param pDevExt Pointer to the device instance data.
+ *
+ * @remarks Must be called only after supdrvGipInitOnCpu() as this function uses
+ * idCpu, GIP's online CPU set which are populated in
+ * supdrvGipInitOnCpu().
+ */
+static int supdrvTscMeasureInitialDeltas(PSUPDRVDEVEXT pDevExt)
+{
+ PSUPGIPCPU pGipCpuMaster;
+ unsigned iCpu;
+ unsigned iOddEven;
+ PSUPGLOBALINFOPAGE pGip = pDevExt->pGip;
+ uint32_t idxMaster = UINT32_MAX;
+ uint32_t cMpOnOffEvents = ASMAtomicReadU32(&pDevExt->cMpOnOffEvents);
+
+ Assert(pGip->enmUseTscDelta > SUPGIPUSETSCDELTA_ZERO_CLAIMED);
+ supdrvTscResetSamples(pDevExt, true /* fClearDeltas */);
+ int rc = supdrvTscPickMaster(pDevExt, &idxMaster);
+ if (RT_FAILURE(rc))
+ {
+ SUPR0Printf("Failed to pick a CPU master for TSC-delta measurements rc=%Rrc\n", rc);
+ return rc;
+ }
+ AssertReturn(idxMaster < pGip->cCpus, VERR_INVALID_CPU_INDEX);
+ pGipCpuMaster = &pGip->aCPUs[idxMaster];
+ Assert(pDevExt->idGipMaster == pGipCpuMaster->idCpu);
+
+ /*
+ * If there is only a single CPU online we have nothing to do.
+ */
+ if (pGip->cOnlineCpus <= 1)
+ {
+ AssertReturn(pGip->cOnlineCpus > 0, VERR_INTERNAL_ERROR_5);
+ return VINF_SUCCESS;
+ }
+
+ /*
+ * Loop thru the GIP CPU array and get deltas for each CPU (except the
+ * master). We do the CPUs with the even numbered APIC IDs first so that
+ * we've got alternative master CPUs to pick from on hyper-threaded systems.
+ */
+ for (iOddEven = 0; iOddEven < 2; iOddEven++)
+ {
+ for (iCpu = 0; iCpu < pGip->cCpus; iCpu++)
+ {
+ PSUPGIPCPU pGipCpuWorker = &pGip->aCPUs[iCpu];
+ if ( iCpu != idxMaster
+ && (iOddEven > 0 || (pGipCpuWorker->idApic & 1) == 0)
+ && RTCpuSetIsMemberByIndex(&pDevExt->TscDeltaCpuSet, pGipCpuWorker->iCpuSet))
+ {
+ rc = supdrvTscMeasureDeltaOne(pDevExt, iCpu);
+ if (RT_FAILURE(rc))
+ {
+ SUPR0Printf("supdrvTscMeasureDeltaOne failed. rc=%d CPU[%u].idCpu=%u Master[%u].idCpu=%u\n", rc, iCpu,
+ pGipCpuWorker->idCpu, idxMaster, pDevExt->idGipMaster, pGipCpuMaster->idCpu);
+ break;
+ }
+
+ if (ASMAtomicReadU32(&pDevExt->cMpOnOffEvents) != cMpOnOffEvents)
+ {
+ SUPR0Printf("One or more CPUs transitioned between online & offline states. I'm confused, retry...\n");
+ rc = VERR_TRY_AGAIN;
+ break;
+ }
+ }
+ }
+ }
+
+ return rc;
+}
+
+
+#ifdef SUPDRV_USE_TSC_DELTA_THREAD
+
+/**
+ * Switches the TSC-delta measurement thread into the butchered state.
+ *
+ * @returns VBox status code.
+ * @param pDevExt Pointer to the device instance data.
+ * @param fSpinlockHeld Whether the TSC-delta spinlock is held or not.
+ * @param pszFailed An error message to log.
+ * @param rcFailed The error code to exit the thread with.
+ */
+static int supdrvTscDeltaThreadButchered(PSUPDRVDEVEXT pDevExt, bool fSpinlockHeld, const char *pszFailed, int rcFailed)
+{
+ if (!fSpinlockHeld)
+ RTSpinlockAcquire(pDevExt->hTscDeltaSpinlock);
+
+ pDevExt->enmTscDeltaThreadState = kTscDeltaThreadState_Butchered;
+ RTSpinlockRelease(pDevExt->hTscDeltaSpinlock);
+ OSDBGPRINT(("supdrvTscDeltaThreadButchered: %s. rc=%Rrc\n", pszFailed, rcFailed));
+ return rcFailed;
+}
+
+
+/**
+ * The TSC-delta measurement thread.
+ *
+ * @returns VBox status code.
+ * @param hThread The thread handle.
+ * @param pvUser Opaque pointer to the device instance data.
+ */
+static DECLCALLBACK(int) supdrvTscDeltaThread(RTTHREAD hThread, void *pvUser)
+{
+ PSUPDRVDEVEXT pDevExt = (PSUPDRVDEVEXT)pvUser;
+ int rc = VERR_INTERNAL_ERROR_2;
+ for (;;)
+ {
+ /*
+ * Switch on the current state.
+ */
+ SUPDRVTSCDELTATHREADSTATE enmState;
+ RTSpinlockAcquire(pDevExt->hTscDeltaSpinlock);
+ enmState = pDevExt->enmTscDeltaThreadState;
+ switch (enmState)
+ {
+ case kTscDeltaThreadState_Creating:
+ {
+ pDevExt->enmTscDeltaThreadState = kTscDeltaThreadState_Listening;
+ rc = RTSemEventSignal(pDevExt->hTscDeltaEvent);
+ if (RT_FAILURE(rc))
+ return supdrvTscDeltaThreadButchered(pDevExt, true /* fSpinlockHeld */, "RTSemEventSignal", rc);
+ RT_FALL_THRU();
+ }
+
+ case kTscDeltaThreadState_Listening:
+ {
+ RTSpinlockRelease(pDevExt->hTscDeltaSpinlock);
+
+ /*
+ * Linux counts uninterruptible sleeps as load, hence we shall do a
+ * regular, interruptible sleep here and ignore wake ups due to signals.
+ * See task_contributes_to_load() in include/linux/sched.h in the Linux sources.
+ */
+ rc = RTThreadUserWaitNoResume(hThread, pDevExt->cMsTscDeltaTimeout);
+ if ( RT_FAILURE(rc)
+ && rc != VERR_TIMEOUT
+ && rc != VERR_INTERRUPTED)
+ return supdrvTscDeltaThreadButchered(pDevExt, false /* fSpinlockHeld */, "RTThreadUserWait", rc);
+ RTThreadUserReset(hThread);
+ break;
+ }
+
+ case kTscDeltaThreadState_WaitAndMeasure:
+ {
+ pDevExt->enmTscDeltaThreadState = kTscDeltaThreadState_Measuring;
+ rc = RTSemEventSignal(pDevExt->hTscDeltaEvent); /* (Safe on windows as long as spinlock isn't IRQ safe.) */
+ if (RT_FAILURE(rc))
+ return supdrvTscDeltaThreadButchered(pDevExt, true /* fSpinlockHeld */, "RTSemEventSignal", rc);
+ RTSpinlockRelease(pDevExt->hTscDeltaSpinlock);
+ RTThreadSleep(1);
+ RT_FALL_THRU();
+ }
+
+ case kTscDeltaThreadState_Measuring:
+ {
+ if (pDevExt->fTscThreadRecomputeAllDeltas)
+ {
+ int cTries = 8;
+ int cMsWaitPerTry = 10;
+ PSUPGLOBALINFOPAGE pGip = pDevExt->pGip;
+ Assert(pGip);
+ do
+ {
+ RTCpuSetCopy(&pDevExt->TscDeltaCpuSet, &pGip->OnlineCpuSet);
+ rc = supdrvTscMeasureInitialDeltas(pDevExt);
+ if ( RT_SUCCESS(rc)
+ || ( RT_FAILURE(rc)
+ && rc != VERR_TRY_AGAIN
+ && rc != VERR_CPU_OFFLINE))
+ {
+ break;
+ }
+ RTThreadSleep(cMsWaitPerTry);
+ } while (cTries-- > 0);
+ pDevExt->fTscThreadRecomputeAllDeltas = false;
+ }
+ else
+ {
+ PSUPGLOBALINFOPAGE pGip = pDevExt->pGip;
+ unsigned iCpu;
+
+ /* Measure TSC-deltas only for the CPUs that are in the set. */
+ rc = VINF_SUCCESS;
+ for (iCpu = 0; iCpu < pGip->cCpus; iCpu++)
+ {
+ PSUPGIPCPU pGipCpuWorker = &pGip->aCPUs[iCpu];
+ if (RTCpuSetIsMemberByIndex(&pDevExt->TscDeltaCpuSet, pGipCpuWorker->iCpuSet))
+ {
+ if (pGipCpuWorker->i64TSCDelta == INT64_MAX)
+ {
+ int rc2 = supdrvTscMeasureDeltaOne(pDevExt, iCpu);
+ if (RT_FAILURE(rc2) && RT_SUCCESS(rc))
+ rc = rc2;
+ }
+ else
+ {
+ /*
+ * The thread/someone must've called SUPR0TscDeltaMeasureBySetIndex(),
+ * mark the delta as fine to get the timer thread off our back.
+ */
+ RTCpuSetDelByIndex(&pDevExt->TscDeltaCpuSet, pGipCpuWorker->iCpuSet);
+ RTCpuSetAddByIndex(&pDevExt->TscDeltaObtainedCpuSet, pGipCpuWorker->iCpuSet);
+ }
+ }
+ }
+ }
+ RTSpinlockAcquire(pDevExt->hTscDeltaSpinlock);
+ if (pDevExt->enmTscDeltaThreadState == kTscDeltaThreadState_Measuring)
+ pDevExt->enmTscDeltaThreadState = kTscDeltaThreadState_Listening;
+ RTSpinlockRelease(pDevExt->hTscDeltaSpinlock);
+ Assert(rc != VERR_NOT_AVAILABLE); /* VERR_NOT_AVAILABLE is used as init value, see supdrvTscDeltaThreadInit(). */
+ ASMAtomicWriteS32(&pDevExt->rcTscDelta, rc);
+ break;
+ }
+
+ case kTscDeltaThreadState_Terminating:
+ pDevExt->enmTscDeltaThreadState = kTscDeltaThreadState_Destroyed;
+ RTSpinlockRelease(pDevExt->hTscDeltaSpinlock);
+ return VINF_SUCCESS;
+
+ case kTscDeltaThreadState_Butchered:
+ default:
+ return supdrvTscDeltaThreadButchered(pDevExt, true /* fSpinlockHeld */, "Invalid state", VERR_INVALID_STATE);
+ }
+ }
+ /* not reached */
+}
+
+
+/**
+ * Waits for the TSC-delta measurement thread to respond to a state change.
+ *
+ * @returns VINF_SUCCESS on success, VERR_TIMEOUT if it doesn't respond in time,
+ * other error code on internal error.
+ *
+ * @param pDevExt The device instance data.
+ * @param enmCurState The current state.
+ * @param enmNewState The new state we're waiting for it to enter.
+ */
+static int supdrvTscDeltaThreadWait(PSUPDRVDEVEXT pDevExt, SUPDRVTSCDELTATHREADSTATE enmCurState,
+ SUPDRVTSCDELTATHREADSTATE enmNewState)
+{
+ SUPDRVTSCDELTATHREADSTATE enmActualState;
+ int rc;
+
+ /*
+ * Wait a short while for the expected state transition.
+ */
+ RTSemEventWait(pDevExt->hTscDeltaEvent, RT_MS_1SEC);
+ RTSpinlockAcquire(pDevExt->hTscDeltaSpinlock);
+ enmActualState = pDevExt->enmTscDeltaThreadState;
+ if (enmActualState == enmNewState)
+ {
+ RTSpinlockRelease(pDevExt->hTscDeltaSpinlock);
+ rc = VINF_SUCCESS;
+ }
+ else if (enmActualState == enmCurState)
+ {
+ /*
+ * Wait longer if the state has not yet transitioned to the one we want.
+ */
+ RTSpinlockRelease(pDevExt->hTscDeltaSpinlock);
+ rc = RTSemEventWait(pDevExt->hTscDeltaEvent, 50 * RT_MS_1SEC);
+ if ( RT_SUCCESS(rc)
+ || rc == VERR_TIMEOUT)
+ {
+ /*
+ * Check the state whether we've succeeded.
+ */
+ RTSpinlockAcquire(pDevExt->hTscDeltaSpinlock);
+ enmActualState = pDevExt->enmTscDeltaThreadState;
+ RTSpinlockRelease(pDevExt->hTscDeltaSpinlock);
+ if (enmActualState == enmNewState)
+ rc = VINF_SUCCESS;
+ else if (enmActualState == enmCurState)
+ {
+ rc = VERR_TIMEOUT;
+ OSDBGPRINT(("supdrvTscDeltaThreadWait: timed out state transition. enmActualState=%d enmNewState=%d\n",
+ enmActualState, enmNewState));
+ }
+ else
+ {
+ rc = VERR_INTERNAL_ERROR;
+ OSDBGPRINT(("supdrvTscDeltaThreadWait: invalid state transition from %d to %d, expected %d\n", enmCurState,
+ enmActualState, enmNewState));
+ }
+ }
+ else
+ OSDBGPRINT(("supdrvTscDeltaThreadWait: RTSemEventWait failed. rc=%Rrc\n", rc));
+ }
+ else
+ {
+ RTSpinlockRelease(pDevExt->hTscDeltaSpinlock);
+ OSDBGPRINT(("supdrvTscDeltaThreadWait: invalid state %d when transitioning from %d to %d\n",
+ enmActualState, enmCurState, enmNewState));
+ rc = VERR_INTERNAL_ERROR;
+ }
+
+ return rc;
+}
+
+
+/**
+ * Signals the TSC-delta thread to start measuring TSC-deltas.
+ *
+ * @param pDevExt Pointer to the device instance data.
+ * @param fForceAll Force re-calculating TSC-deltas on all CPUs.
+ */
+static void supdrvTscDeltaThreadStartMeasurement(PSUPDRVDEVEXT pDevExt, bool fForceAll)
+{
+ if (pDevExt->hTscDeltaThread != NIL_RTTHREAD)
+ {
+ RTSpinlockAcquire(pDevExt->hTscDeltaSpinlock);
+ if ( pDevExt->enmTscDeltaThreadState == kTscDeltaThreadState_Listening
+ || pDevExt->enmTscDeltaThreadState == kTscDeltaThreadState_Measuring)
+ {
+ pDevExt->enmTscDeltaThreadState = kTscDeltaThreadState_WaitAndMeasure;
+ if (fForceAll)
+ pDevExt->fTscThreadRecomputeAllDeltas = true;
+ }
+ else if ( pDevExt->enmTscDeltaThreadState == kTscDeltaThreadState_WaitAndMeasure
+ && fForceAll)
+ pDevExt->fTscThreadRecomputeAllDeltas = true;
+ RTSpinlockRelease(pDevExt->hTscDeltaSpinlock);
+ RTThreadUserSignal(pDevExt->hTscDeltaThread);
+ }
+}
+
+
+/**
+ * Terminates the actual thread running supdrvTscDeltaThread().
+ *
+ * This is an internal worker function for supdrvTscDeltaThreadInit() and
+ * supdrvTscDeltaTerm().
+ *
+ * @param pDevExt Pointer to the device instance data.
+ */
+static void supdrvTscDeltaThreadTerminate(PSUPDRVDEVEXT pDevExt)
+{
+ int rc;
+ RTSpinlockAcquire(pDevExt->hTscDeltaSpinlock);
+ pDevExt->enmTscDeltaThreadState = kTscDeltaThreadState_Terminating;
+ RTSpinlockRelease(pDevExt->hTscDeltaSpinlock);
+ RTThreadUserSignal(pDevExt->hTscDeltaThread);
+ rc = RTThreadWait(pDevExt->hTscDeltaThread, 50 * RT_MS_1SEC, NULL /* prc */);
+ if (RT_FAILURE(rc))
+ {
+ /* Signal a few more times before giving up. */
+ int cTriesLeft = 5;
+ while (--cTriesLeft > 0)
+ {
+ RTThreadUserSignal(pDevExt->hTscDeltaThread);
+ rc = RTThreadWait(pDevExt->hTscDeltaThread, 2 * RT_MS_1SEC, NULL /* prc */);
+ if (rc != VERR_TIMEOUT)
+ break;
+ }
+ }
+}
+
+
+/**
+ * Initializes and spawns the TSC-delta measurement thread.
+ *
+ * A thread is required for servicing re-measurement requests from events like
+ * CPUs coming online, suspend/resume etc. as it cannot be done synchronously
+ * under all contexts on all OSs.
+ *
+ * @returns VBox status code.
+ * @param pDevExt Pointer to the device instance data.
+ *
+ * @remarks Must only be called -after- initializing GIP and setting up MP
+ * notifications!
+ */
+static int supdrvTscDeltaThreadInit(PSUPDRVDEVEXT pDevExt)
+{
+ int rc;
+ Assert(pDevExt->pGip->enmUseTscDelta > SUPGIPUSETSCDELTA_ZERO_CLAIMED);
+ rc = RTSpinlockCreate(&pDevExt->hTscDeltaSpinlock, RTSPINLOCK_FLAGS_INTERRUPT_UNSAFE, "VBoxTscSpnLck");
+ if (RT_SUCCESS(rc))
+ {
+ rc = RTSemEventCreate(&pDevExt->hTscDeltaEvent);
+ if (RT_SUCCESS(rc))
+ {
+ pDevExt->enmTscDeltaThreadState = kTscDeltaThreadState_Creating;
+ pDevExt->cMsTscDeltaTimeout = 60000;
+ rc = RTThreadCreate(&pDevExt->hTscDeltaThread, supdrvTscDeltaThread, pDevExt, 0 /* cbStack */,
+ RTTHREADTYPE_DEFAULT, RTTHREADFLAGS_WAITABLE, "VBoxTscThread");
+ if (RT_SUCCESS(rc))
+ {
+ rc = supdrvTscDeltaThreadWait(pDevExt, kTscDeltaThreadState_Creating, kTscDeltaThreadState_Listening);
+ if (RT_SUCCESS(rc))
+ {
+ ASMAtomicWriteS32(&pDevExt->rcTscDelta, VERR_NOT_AVAILABLE);
+ return rc;
+ }
+
+ OSDBGPRINT(("supdrvTscDeltaInit: supdrvTscDeltaThreadWait failed. rc=%Rrc\n", rc));
+ supdrvTscDeltaThreadTerminate(pDevExt);
+ }
+ else
+ OSDBGPRINT(("supdrvTscDeltaInit: RTThreadCreate failed. rc=%Rrc\n", rc));
+ RTSemEventDestroy(pDevExt->hTscDeltaEvent);
+ pDevExt->hTscDeltaEvent = NIL_RTSEMEVENT;
+ }
+ else
+ OSDBGPRINT(("supdrvTscDeltaInit: RTSemEventCreate failed. rc=%Rrc\n", rc));
+ RTSpinlockDestroy(pDevExt->hTscDeltaSpinlock);
+ pDevExt->hTscDeltaSpinlock = NIL_RTSPINLOCK;
+ }
+ else
+ OSDBGPRINT(("supdrvTscDeltaInit: RTSpinlockCreate failed. rc=%Rrc\n", rc));
+
+ return rc;
+}
+
+
+/**
+ * Terminates the TSC-delta measurement thread and cleanup.
+ *
+ * @param pDevExt Pointer to the device instance data.
+ */
+static void supdrvTscDeltaTerm(PSUPDRVDEVEXT pDevExt)
+{
+ if ( pDevExt->hTscDeltaSpinlock != NIL_RTSPINLOCK
+ && pDevExt->hTscDeltaEvent != NIL_RTSEMEVENT)
+ {
+ supdrvTscDeltaThreadTerminate(pDevExt);
+ }
+
+ if (pDevExt->hTscDeltaSpinlock != NIL_RTSPINLOCK)
+ {
+ RTSpinlockDestroy(pDevExt->hTscDeltaSpinlock);
+ pDevExt->hTscDeltaSpinlock = NIL_RTSPINLOCK;
+ }
+
+ if (pDevExt->hTscDeltaEvent != NIL_RTSEMEVENT)
+ {
+ RTSemEventDestroy(pDevExt->hTscDeltaEvent);
+ pDevExt->hTscDeltaEvent = NIL_RTSEMEVENT;
+ }
+
+ ASMAtomicWriteS32(&pDevExt->rcTscDelta, VERR_NOT_AVAILABLE);
+}
+
+#endif /* SUPDRV_USE_TSC_DELTA_THREAD */
+
+/**
+ * Measure the TSC delta for the CPU given by its CPU set index.
+ *
+ * @returns VBox status code.
+ * @retval VERR_INTERRUPTED if interrupted while waiting.
+ * @retval VERR_SUPDRV_TSC_DELTA_MEASUREMENT_FAILED if we were unable to get a
+ * measurement.
+ * @retval VERR_CPU_OFFLINE if the specified CPU is offline.
+ *
+ * @param pSession The caller's session. GIP must've been mapped.
+ * @param iCpuSet The CPU set index of the CPU to measure.
+ * @param fFlags Flags, SUP_TSCDELTA_MEASURE_F_XXX.
+ * @param cMsWaitRetry Number of milliseconds to wait between each retry.
+ * @param cMsWaitThread Number of milliseconds to wait for the thread to get
+ * ready.
+ * @param cTries Number of times to try, pass 0 for the default.
+ */
+SUPR0DECL(int) SUPR0TscDeltaMeasureBySetIndex(PSUPDRVSESSION pSession, uint32_t iCpuSet, uint32_t fFlags,
+ RTMSINTERVAL cMsWaitRetry, RTMSINTERVAL cMsWaitThread, uint32_t cTries)
+{
+ PSUPDRVDEVEXT pDevExt;
+ PSUPGLOBALINFOPAGE pGip;
+ uint16_t iGipCpu;
+ int rc;
+#ifdef SUPDRV_USE_TSC_DELTA_THREAD
+ uint64_t msTsStartWait;
+ uint32_t iWaitLoop;
+#endif
+
+ /*
+ * Validate and adjust the input.
+ */
+ AssertReturn(SUP_IS_SESSION_VALID(pSession), VERR_INVALID_PARAMETER);
+ if (!pSession->fGipReferenced)
+ return VERR_WRONG_ORDER;
+
+ pDevExt = pSession->pDevExt;
+ AssertReturn(SUP_IS_DEVEXT_VALID(pDevExt), VERR_INVALID_PARAMETER);
+
+ pGip = pDevExt->pGip;
+ AssertPtrReturn(pGip, VERR_INTERNAL_ERROR_2);
+
+ AssertReturn(iCpuSet < RTCPUSET_MAX_CPUS, VERR_INVALID_CPU_INDEX);
+ AssertReturn(iCpuSet < RT_ELEMENTS(pGip->aiCpuFromCpuSetIdx), VERR_INVALID_CPU_INDEX);
+ iGipCpu = pGip->aiCpuFromCpuSetIdx[iCpuSet];
+ AssertReturn(iGipCpu < pGip->cCpus, VERR_INVALID_CPU_INDEX);
+
+ if (fFlags & ~SUP_TSCDELTA_MEASURE_F_VALID_MASK)
+ return VERR_INVALID_FLAGS;
+
+ /*
+ * The request is a noop if the TSC delta isn't being used.
+ */
+ if (pGip->enmUseTscDelta <= SUPGIPUSETSCDELTA_ZERO_CLAIMED)
+ return VINF_SUCCESS;
+
+ if (cTries == 0)
+ cTries = 12;
+ else if (cTries > 256)
+ cTries = 256;
+
+ if (cMsWaitRetry == 0)
+ cMsWaitRetry = 2;
+ else if (cMsWaitRetry > 1000)
+ cMsWaitRetry = 1000;
+
+#ifdef SUPDRV_USE_TSC_DELTA_THREAD
+ /*
+ * Has the TSC already been measured and we're not forced to redo it?
+ */
+ if ( pGip->aCPUs[iGipCpu].i64TSCDelta != INT64_MAX
+ && !(fFlags & SUP_TSCDELTA_MEASURE_F_FORCE))
+ return VINF_SUCCESS;
+
+ /*
+ * Asynchronous request? Forward it to the thread, no waiting.
+ */
+ if (fFlags & SUP_TSCDELTA_MEASURE_F_ASYNC)
+ {
+ /** @todo Async. doesn't implement options like retries, waiting. We'll need
+ * to pass those options to the thread somehow and implement it in the
+ * thread. Check if anyone uses/needs fAsync before implementing this. */
+ RTSpinlockAcquire(pDevExt->hTscDeltaSpinlock);
+ RTCpuSetAddByIndex(&pDevExt->TscDeltaCpuSet, iCpuSet);
+ if ( pDevExt->enmTscDeltaThreadState == kTscDeltaThreadState_Listening
+ || pDevExt->enmTscDeltaThreadState == kTscDeltaThreadState_Measuring)
+ {
+ pDevExt->enmTscDeltaThreadState = kTscDeltaThreadState_WaitAndMeasure;
+ rc = VINF_SUCCESS;
+ }
+ else if (pDevExt->enmTscDeltaThreadState != kTscDeltaThreadState_WaitAndMeasure)
+ rc = VERR_THREAD_IS_DEAD;
+ RTSpinlockRelease(pDevExt->hTscDeltaSpinlock);
+ RTThreadUserSignal(pDevExt->hTscDeltaThread);
+ return VINF_SUCCESS;
+ }
+
+ /*
+ * If a TSC-delta measurement request is already being serviced by the thread,
+ * wait 'cTries' times if a retry-timeout is provided, otherwise bail as busy.
+ */
+ msTsStartWait = RTTimeSystemMilliTS();
+ for (iWaitLoop = 0;; iWaitLoop++)
+ {
+ uint64_t cMsElapsed;
+ SUPDRVTSCDELTATHREADSTATE enmState;
+ RTSpinlockAcquire(pDevExt->hTscDeltaSpinlock);
+ enmState = pDevExt->enmTscDeltaThreadState;
+ RTSpinlockRelease(pDevExt->hTscDeltaSpinlock);
+
+ if (enmState == kTscDeltaThreadState_Measuring)
+ { /* Must wait, the thread is busy. */ }
+ else if (enmState == kTscDeltaThreadState_WaitAndMeasure)
+ { /* Must wait, this state only says what will happen next. */ }
+ else if (enmState == kTscDeltaThreadState_Terminating)
+ { /* Must wait, this state only says what should happen next. */ }
+ else
+ break; /* All other states, the thread is either idly listening or dead. */
+
+ /* Wait or fail. */
+ if (cMsWaitThread == 0)
+ return VERR_SUPDRV_TSC_DELTA_MEASUREMENT_BUSY;
+ cMsElapsed = RTTimeSystemMilliTS() - msTsStartWait;
+ if (cMsElapsed >= cMsWaitThread)
+ return VERR_SUPDRV_TSC_DELTA_MEASUREMENT_BUSY;
+
+ rc = RTThreadSleep(RT_MIN((RTMSINTERVAL)(cMsWaitThread - cMsElapsed), RT_MIN(iWaitLoop + 1, 10)));
+ if (rc == VERR_INTERRUPTED)
+ return rc;
+ }
+#endif /* SUPDRV_USE_TSC_DELTA_THREAD */
+
+ /*
+ * Try measure the TSC delta the given number of times.
+ */
+ for (;;)
+ {
+ /* Unless we're forced to measure the delta, check whether it's done already. */
+ if ( !(fFlags & SUP_TSCDELTA_MEASURE_F_FORCE)
+ && pGip->aCPUs[iGipCpu].i64TSCDelta != INT64_MAX)
+ {
+ rc = VINF_SUCCESS;
+ break;
+ }
+
+ /* Measure it. */
+ rc = supdrvTscMeasureDeltaOne(pDevExt, iGipCpu);
+ if (rc != VERR_SUPDRV_TSC_DELTA_MEASUREMENT_FAILED)
+ {
+ Assert(pGip->aCPUs[iGipCpu].i64TSCDelta != INT64_MAX || RT_FAILURE_NP(rc));
+ break;
+ }
+
+ /* Retry? */
+ if (cTries <= 1)
+ break;
+ cTries--;
+
+ /* Always delay between retries (be nice to the rest of the system
+ and avoid the BSOD hounds). */
+ rc = RTThreadSleep(cMsWaitRetry);
+ if (rc == VERR_INTERRUPTED)
+ break;
+ }
+
+ return rc;
+}
+SUPR0_EXPORT_SYMBOL(SUPR0TscDeltaMeasureBySetIndex);
+
+
+/**
+ * Service a TSC-delta measurement request.
+ *
+ * @returns VBox status code.
+ * @param pDevExt Pointer to the device instance data.
+ * @param pSession The support driver session.
+ * @param pReq Pointer to the TSC-delta measurement request.
+ */
+int VBOXCALL supdrvIOCtl_TscDeltaMeasure(PSUPDRVDEVEXT pDevExt, PSUPDRVSESSION pSession, PSUPTSCDELTAMEASURE pReq)
+{
+ uint32_t cTries;
+ uint32_t iCpuSet;
+ uint32_t fFlags;
+ RTMSINTERVAL cMsWaitRetry;
+ RT_NOREF1(pDevExt);
+
+ /*
+ * Validate and adjust/resolve the input so they can be passed onto SUPR0TscDeltaMeasureBySetIndex.
+ */
+ AssertPtr(pDevExt); AssertPtr(pSession); AssertPtr(pReq); /* paranoia^2 */
+
+ if (pReq->u.In.idCpu == NIL_RTCPUID)
+ return VERR_INVALID_CPU_ID;
+ iCpuSet = RTMpCpuIdToSetIndex(pReq->u.In.idCpu);
+ if (iCpuSet >= RTCPUSET_MAX_CPUS)
+ return VERR_INVALID_CPU_ID;
+
+ cTries = pReq->u.In.cRetries == 0 ? 0 : (uint32_t)pReq->u.In.cRetries + 1;
+
+ cMsWaitRetry = RT_MAX(pReq->u.In.cMsWaitRetry, 5);
+
+ fFlags = 0;
+ if (pReq->u.In.fAsync)
+ fFlags |= SUP_TSCDELTA_MEASURE_F_ASYNC;
+ if (pReq->u.In.fForce)
+ fFlags |= SUP_TSCDELTA_MEASURE_F_FORCE;
+
+ return SUPR0TscDeltaMeasureBySetIndex(pSession, iCpuSet, fFlags, cMsWaitRetry,
+ cTries == 0 ? 5 * RT_MS_1SEC : cMsWaitRetry * cTries /*cMsWaitThread*/,
+ cTries);
+}
+
+
+/**
+ * Reads TSC with delta applied.
+ *
+ * Will try to resolve delta value INT64_MAX before applying it. This is the
+ * main purpose of this function, to handle the case where the delta needs to be
+ * determined.
+ *
+ * @returns VBox status code.
+ * @param pDevExt Pointer to the device instance data.
+ * @param pSession The support driver session.
+ * @param pReq Pointer to the TSC-read request.
+ */
+int VBOXCALL supdrvIOCtl_TscRead(PSUPDRVDEVEXT pDevExt, PSUPDRVSESSION pSession, PSUPTSCREAD pReq)
+{
+ PSUPGLOBALINFOPAGE pGip;
+ int rc;
+
+ /*
+ * Validate. We require the client to have mapped GIP (no asserting on
+ * ring-3 preconditions).
+ */
+ AssertPtr(pDevExt); AssertPtr(pReq); AssertPtr(pSession); /* paranoia^2 */
+ if (pSession->GipMapObjR3 == NIL_RTR0MEMOBJ)
+ return VERR_WRONG_ORDER;
+ pGip = pDevExt->pGip;
+ AssertReturn(pGip, VERR_INTERNAL_ERROR_2);
+
+ /*
+ * We're usually here because we need to apply delta, but we shouldn't be
+ * upset if the GIP is some different mode.
+ */
+ if (pGip->enmUseTscDelta > SUPGIPUSETSCDELTA_ZERO_CLAIMED)
+ {
+ uint32_t cTries = 0;
+ for (;;)
+ {
+ /*
+ * Start by gathering the data, using CLI for disabling preemption
+ * while we do that.
+ */
+ RTCCUINTREG fEFlags = ASMIntDisableFlags();
+ int iCpuSet = RTMpCpuIdToSetIndex(RTMpCpuId());
+ int iGipCpu = 0; /* gcc maybe used uninitialized */
+ if (RT_LIKELY( (unsigned)iCpuSet < RT_ELEMENTS(pGip->aiCpuFromCpuSetIdx)
+ && (iGipCpu = pGip->aiCpuFromCpuSetIdx[iCpuSet]) < pGip->cCpus ))
+ {
+ int64_t i64Delta = pGip->aCPUs[iGipCpu].i64TSCDelta;
+ pReq->u.Out.idApic = pGip->aCPUs[iGipCpu].idApic;
+ pReq->u.Out.u64AdjustedTsc = ASMReadTSC();
+ ASMSetFlags(fEFlags);
+
+ /*
+ * If we're lucky we've got a delta, but no predictions here
+ * as this I/O control is normally only used when the TSC delta
+ * is set to INT64_MAX.
+ */
+ if (i64Delta != INT64_MAX)
+ {
+ pReq->u.Out.u64AdjustedTsc -= i64Delta;
+ rc = VINF_SUCCESS;
+ break;
+ }
+
+ /* Give up after a few times. */
+ if (cTries >= 4)
+ {
+ rc = VWRN_SUPDRV_TSC_DELTA_MEASUREMENT_FAILED;
+ break;
+ }
+
+ /* Need to measure the delta an try again. */
+ rc = supdrvTscMeasureDeltaOne(pDevExt, iGipCpu);
+ Assert(pGip->aCPUs[iGipCpu].i64TSCDelta != INT64_MAX || RT_FAILURE_NP(rc));
+ /** @todo should probably delay on failure... dpc watchdogs */
+ }
+ else
+ {
+ /* This really shouldn't happen. */
+ AssertMsgFailed(("idCpu=%#x iCpuSet=%#x (%d)\n", RTMpCpuId(), iCpuSet, iCpuSet));
+ pReq->u.Out.idApic = supdrvGipGetApicIdSlow();
+ pReq->u.Out.u64AdjustedTsc = ASMReadTSC();
+ ASMSetFlags(fEFlags);
+ rc = VERR_INTERNAL_ERROR_5; /** @todo change to warning. */
+ break;
+ }
+ }
+ }
+ else
+ {
+ /*
+ * No delta to apply. Easy. Deal with preemption the lazy way.
+ */
+ RTCCUINTREG fEFlags = ASMIntDisableFlags();
+ int iCpuSet = RTMpCpuIdToSetIndex(RTMpCpuId());
+ int iGipCpu = 0; /* gcc may be used uninitialized */
+ if (RT_LIKELY( (unsigned)iCpuSet < RT_ELEMENTS(pGip->aiCpuFromCpuSetIdx)
+ && (iGipCpu = pGip->aiCpuFromCpuSetIdx[iCpuSet]) < pGip->cCpus ))
+ pReq->u.Out.idApic = pGip->aCPUs[iGipCpu].idApic;
+ else
+ pReq->u.Out.idApic = supdrvGipGetApicIdSlow();
+ pReq->u.Out.u64AdjustedTsc = ASMReadTSC();
+ ASMSetFlags(fEFlags);
+ rc = VINF_SUCCESS;
+ }
+
+ return rc;
+}
+
+
+/**
+ * Worker for supdrvIOCtl_GipSetFlags.
+ *
+ * @returns VBox status code.
+ * @retval VERR_WRONG_ORDER if an enable-once-per-session flag is set again for
+ * a session.
+ *
+ * @param pDevExt Pointer to the device instance data.
+ * @param pSession The support driver session.
+ * @param fOrMask The OR mask of the GIP flags, see SUPGIP_FLAGS_XXX.
+ * @param fAndMask The AND mask of the GIP flags, see SUPGIP_FLAGS_XXX.
+ *
+ * @remarks Caller must own the GIP mutex.
+ *
+ * @remarks This function doesn't validate any of the flags.
+ */
+static int supdrvGipSetFlags(PSUPDRVDEVEXT pDevExt, PSUPDRVSESSION pSession, uint32_t fOrMask, uint32_t fAndMask)
+{
+ uint32_t cRefs;
+ PSUPGLOBALINFOPAGE pGip = pDevExt->pGip;
+ AssertMsg((fOrMask & fAndMask) == fOrMask, ("%#x & %#x\n", fOrMask, fAndMask)); /* ASSUMED by code below */
+
+ /*
+ * Compute GIP test-mode flags.
+ */
+ if (fOrMask & SUPGIP_FLAGS_TESTING_ENABLE)
+ {
+ if (!pSession->fGipTestMode)
+ {
+ Assert(pDevExt->cGipTestModeRefs < _64K);
+ pSession->fGipTestMode = true;
+ cRefs = ++pDevExt->cGipTestModeRefs;
+ if (cRefs == 1)
+ {
+ fOrMask |= SUPGIP_FLAGS_TESTING | SUPGIP_FLAGS_TESTING_START;
+ fAndMask &= ~SUPGIP_FLAGS_TESTING_STOP;
+ }
+ }
+ else
+ {
+ LogRelMax(10, ("supdrvGipSetFlags: SUPGIP_FLAGS_TESTING_ENABLE already set for this session\n"));
+ return VERR_WRONG_ORDER;
+ }
+ }
+ else if ( !(fAndMask & SUPGIP_FLAGS_TESTING_ENABLE)
+ && pSession->fGipTestMode)
+ {
+ Assert(pDevExt->cGipTestModeRefs > 0);
+ Assert(pDevExt->cGipTestModeRefs < _64K);
+ pSession->fGipTestMode = false;
+ cRefs = --pDevExt->cGipTestModeRefs;
+ if (!cRefs)
+ fOrMask |= SUPGIP_FLAGS_TESTING_STOP;
+ else
+ fAndMask |= SUPGIP_FLAGS_TESTING_ENABLE;
+ }
+
+ /*
+ * Commit the flags. This should be done as atomically as possible
+ * since the flag consumers won't be holding the GIP mutex.
+ */
+ ASMAtomicOrU32(&pGip->fFlags, fOrMask);
+ ASMAtomicAndU32(&pGip->fFlags, fAndMask);
+
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * Sets GIP test mode parameters.
+ *
+ * @returns VBox status code.
+ * @param pDevExt Pointer to the device instance data.
+ * @param pSession The support driver session.
+ * @param fOrMask The OR mask of the GIP flags, see SUPGIP_FLAGS_XXX.
+ * @param fAndMask The AND mask of the GIP flags, see SUPGIP_FLAGS_XXX.
+ */
+int VBOXCALL supdrvIOCtl_GipSetFlags(PSUPDRVDEVEXT pDevExt, PSUPDRVSESSION pSession, uint32_t fOrMask, uint32_t fAndMask)
+{
+ PSUPGLOBALINFOPAGE pGip;
+ int rc;
+
+ /*
+ * Validate. We require the client to have mapped GIP (no asserting on
+ * ring-3 preconditions).
+ */
+ AssertPtr(pDevExt); AssertPtr(pSession); /* paranoia^2 */
+ if (pSession->GipMapObjR3 == NIL_RTR0MEMOBJ)
+ return VERR_WRONG_ORDER;
+ pGip = pDevExt->pGip;
+ AssertReturn(pGip, VERR_INTERNAL_ERROR_3);
+
+ if (fOrMask & ~SUPGIP_FLAGS_VALID_MASK)
+ return VERR_INVALID_PARAMETER;
+ if ((fAndMask & ~SUPGIP_FLAGS_VALID_MASK) != ~SUPGIP_FLAGS_VALID_MASK)
+ return VERR_INVALID_PARAMETER;
+
+ /*
+ * Don't confuse supdrvGipSetFlags or anyone else by both setting
+ * and clearing the same flags. AND takes precedence.
+ */
+ fOrMask &= fAndMask;
+
+ /*
+ * Take the loader lock to avoid having to think about races between two
+ * clients changing the flags at the same time (state is not simple).
+ */
+#ifdef SUPDRV_USE_MUTEX_FOR_GIP
+ RTSemMutexRequest(pDevExt->mtxGip, RT_INDEFINITE_WAIT);
+#else
+ RTSemFastMutexRequest(pDevExt->mtxGip);
+#endif
+
+ rc = supdrvGipSetFlags(pDevExt, pSession, fOrMask, fAndMask);
+
+#ifdef SUPDRV_USE_MUTEX_FOR_GIP
+ RTSemMutexRelease(pDevExt->mtxGip);
+#else
+ RTSemFastMutexRelease(pDevExt->mtxGip);
+#endif
+ return rc;
+}
+
diff --git a/src/VBox/HostDrivers/Support/SUPDrvIDC.h b/src/VBox/HostDrivers/Support/SUPDrvIDC.h
new file mode 100644
index 00000000..5e27c396
--- /dev/null
+++ b/src/VBox/HostDrivers/Support/SUPDrvIDC.h
@@ -0,0 +1,288 @@
+/* $Id: SUPDrvIDC.h $ */
+/** @file
+ * VirtualBox Support Driver - Inter-Driver Communication (IDC) definitions.
+ */
+
+/*
+ * Copyright (C) 2008-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+#ifndef VBOX_INCLUDED_SRC_Support_SUPDrvIDC_h
+#define VBOX_INCLUDED_SRC_Support_SUPDrvIDC_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+#include <VBox/types.h>
+
+/** @def SUP_IDC_CODE
+ * Creates IDC function code.
+ *
+ * @param Function The function number to encode, 1..255.
+ *
+ * @remarks We can take a slightly more relaxed attitude wrt to size encoding
+ * here since only windows will use standard I/O control function code.
+ *
+ * @{
+ */
+
+#ifdef RT_OS_WINDOWS
+# define SUP_IDC_CODE(Function) CTL_CODE(FILE_DEVICE_UNKNOWN, (Function) + 2542, METHOD_BUFFERED, FILE_WRITE_ACCESS)
+#else
+# define SUP_IDC_CODE(Function) ( UINT32_C(0xc0ffee00) | (uint32_t)(0x000000ff & (Function)) )
+#endif
+
+
+#ifdef RT_ARCH_AMD64
+# pragma pack(8) /* paranoia. */
+#else
+# pragma pack(4) /* paranoia. */
+#endif
+
+
+/**
+ * An IDC request packet header.
+ *
+ * The main purpose of this header is to pass the session handle
+ * and status code in a generic manner in order to make things
+ * easier on the receiving end.
+ */
+typedef struct SUPDRVIDCREQHDR
+{
+ /** IN: The size of the request. */
+ uint32_t cb;
+ /** OUT: Status code of the request. */
+ int32_t rc;
+ /** IN: Pointer to the session handle. */
+ PSUPDRVSESSION pSession;
+#if ARCH_BITS == 32
+ /** Padding the structure to 16-bytes. */
+ uint32_t u32Padding;
+#endif
+} SUPDRVIDCREQHDR;
+/** Pointer to an IDC request packet header. */
+typedef SUPDRVIDCREQHDR *PSUPDRVIDCREQHDR;
+/** Pointer to a const IDC request packet header. */
+typedef SUPDRVIDCREQHDR const *PCSUPDRVIDCREQHDR;
+
+
+/**
+ * SUPDRV IDC: Connect request.
+ * This request takes a SUPDRVIDCREQCONNECT packet.
+ */
+#define SUPDRV_IDC_REQ_CONNECT SUP_IDC_CODE(1)
+/** A SUPDRV IDC connect request packet. */
+typedef struct SUPDRVIDCREQCONNECT
+{
+ /** The request header. */
+ SUPDRVIDCREQHDR Hdr;
+ /** The payload union. */
+ union
+ {
+ /** The input. */
+ struct SUPDRVIDCREQCONNECTIN
+ {
+ /** The magic cookie (SUPDRVIDCREQ_CONNECT_MAGIC_COOKIE). */
+ uint32_t u32MagicCookie;
+ /** The desired version of the IDC interface. */
+ uint32_t uReqVersion;
+ /** The minimum version of the IDC interface. */
+ uint32_t uMinVersion;
+ } In;
+
+ /** The output. */
+ struct SUPDRVIDCREQCONNECTOUT
+ {
+ /** The support driver session. (An opaque.) */
+ PSUPDRVSESSION pSession;
+ /** The version of the IDC interface for this session. */
+ uint32_t uSessionVersion;
+ /** The version of the IDC interface . */
+ uint32_t uDriverVersion;
+ /** The SVN revision of the driver.
+ * This will be set to 0 if not compiled into the driver. */
+ uint32_t uDriverRevision;
+ } Out;
+ } u;
+} SUPDRVIDCREQCONNECT;
+/** Pointer to a SUPDRV IDC connect request. */
+typedef SUPDRVIDCREQCONNECT *PSUPDRVIDCREQCONNECT;
+/** Magic cookie value (SUPDRVIDCREQCONNECT::In.u32MagicCookie). ('tori') */
+#define SUPDRVIDCREQ_CONNECT_MAGIC_COOKIE UINT32_C(0x69726f74)
+
+
+/**
+ * SUPDRV IDC: Disconnect request.
+ * This request only requires a SUPDRVIDCREQHDR.
+ */
+#define SUPDRV_IDC_REQ_DISCONNECT SUP_IDC_CODE(2)
+
+
+/**
+ * SUPDRV IDC: Query a symbol address.
+ * This request takes a SUPDRVIDCREQGETSYM packet.
+ */
+#define SUPDRV_IDC_REQ_GET_SYMBOL SUP_IDC_CODE(3)
+/** A SUPDRV IDC get symbol request packet. */
+typedef struct SUPDRVIDCREQGETSYM
+{
+ /** The request header. */
+ SUPDRVIDCREQHDR Hdr;
+ /** The payload union. */
+ union
+ {
+ /** The input. */
+ struct SUPDRVIDCREQGETSYMIN
+ {
+ /** The module name.
+ * NULL is an alias for the support driver. */
+ const char *pszModule;
+ /** The symbol name. */
+ const char *pszSymbol;
+ } In;
+
+ /** The output. */
+ struct SUPDRVIDCREQGETSYMOUT
+ {
+ /** The symbol address. */
+ PFNRT pfnSymbol;
+ } Out;
+ } u;
+} SUPDRVIDCREQGETSYM;
+/** Pointer to a SUPDRV IDC get symbol request. */
+typedef SUPDRVIDCREQGETSYM *PSUPDRVIDCREQGETSYM;
+
+
+/**
+ * SUPDRV IDC: Request the registration of a component factory.
+ * This request takes a SUPDRVIDCREQCOMPREGFACTORY packet.
+ */
+#define SUPDRV_IDC_REQ_COMPONENT_REGISTER_FACTORY SUP_IDC_CODE(10)
+/** A SUPDRV IDC register component factory request packet. */
+typedef struct SUPDRVIDCREQCOMPREGFACTORY
+{
+ /** The request header. */
+ SUPDRVIDCREQHDR Hdr;
+ /** The payload union. */
+ union
+ {
+ /** The input. */
+ struct SUPDRVIDCREQCOMPREGFACTORYIN
+ {
+ /** Pointer to the factory. */
+ PCSUPDRVFACTORY pFactory;
+ } In;
+ } u;
+} SUPDRVIDCREQCOMPREGFACTORY;
+/** Pointer to a SUPDRV IDC register component factory request. */
+typedef SUPDRVIDCREQCOMPREGFACTORY *PSUPDRVIDCREQCOMPREGFACTORY;
+
+
+/**
+ * SUPDRV IDC: Deregister a component factory.
+ * This request takes a SUPDRVIDCREQCOMPDEREGFACTORY packet.
+ */
+#define SUPDRV_IDC_REQ_COMPONENT_DEREGISTER_FACTORY SUP_IDC_CODE(11)
+/** A SUPDRV IDC deregister component factory request packet. */
+typedef struct SUPDRVIDCREQCOMPDEREGFACTORY
+{
+ /** The request header. */
+ SUPDRVIDCREQHDR Hdr;
+ /** The payload union. */
+ union
+ {
+ /** The input. */
+ struct SUPDRVIDCREQCOMPDEREGFACTORYIN
+ {
+ /** Pointer to the factory. */
+ PCSUPDRVFACTORY pFactory;
+ } In;
+ } u;
+} SUPDRVIDCREQCOMPDEREGFACTORY;
+/** Pointer to a SUPDRV IDC deregister component factory request. */
+typedef SUPDRVIDCREQCOMPDEREGFACTORY *PSUPDRVIDCREQCOMPDEREGFACTORY;
+
+
+/*
+ * The OS specific prototypes.
+ * Most OSes uses
+ */
+RT_C_DECLS_BEGIN
+
+#if defined(RT_OS_DARWIN)
+# ifdef IN_SUP_R0
+extern DECLEXPORT(int) VBOXCALL SUPDrvDarwinIDC(uint32_t iReq, PSUPDRVIDCREQHDR pReq);
+# else
+extern DECLIMPORT(int) VBOXCALL SUPDrvDarwinIDC(uint32_t iReq, PSUPDRVIDCREQHDR pReq);
+# endif
+
+#elif defined(RT_OS_FREEBSD)
+extern int VBOXCALL SUPDrvFreeBSDIDC(uint32_t iReq, PSUPDRVIDCREQHDR pReq);
+
+#elif defined(RT_OS_LINUX)
+extern int VBOXCALL SUPDrvLinuxIDC(uint32_t iReq, PSUPDRVIDCREQHDR pReq);
+
+#elif defined(RT_OS_OS2)
+/** @todo Port to OS/2. */
+
+#elif defined(RT_OS_SOLARIS)
+extern int VBOXCALL SUPDrvSolarisIDC(uint32_t iReq, PSUPDRVIDCREQHDR pReq);
+
+#elif defined(RT_OS_WINDOWS)
+/* Nothing special for windows. */
+
+#else
+/* PORTME: OS specific IDC stuff goes here. */
+#endif
+
+RT_C_DECLS_END
+
+/**
+ * The SUPDRV IDC entry point.
+ *
+ * @returns VBox status code indicating the validity of the session, request and
+ * the return data packet. The status of the request it self is found
+ * in the packet (specific to each request).
+ *
+ * @param pSession The session. (This is NULL for SUPDRV_IDC_REQ_CONNECT.)
+ * @param uReq The request number.
+ * @param pvReq Pointer to the request packet. Optional for some requests.
+ * @param cbReq The size of the request packet.
+ */
+/** @todo move this and change to function proto */
+typedef DECLCALLBACKTYPE(int, FNSUPDRVIDCENTRY,(PSUPDRVSESSION pSession, uint32_t uReq, void *pvReq, uint32_t cbReq));
+
+/** @} */
+
+
+#pragma pack() /* paranoia */
+
+#endif /* !VBOX_INCLUDED_SRC_Support_SUPDrvIDC_h */
+
diff --git a/src/VBox/HostDrivers/Support/SUPDrvIOC.h b/src/VBox/HostDrivers/Support/SUPDrvIOC.h
new file mode 100644
index 00000000..3e3cd0b0
--- /dev/null
+++ b/src/VBox/HostDrivers/Support/SUPDrvIOC.h
@@ -0,0 +1,1725 @@
+/* $Id: SUPDrvIOC.h $ */
+/** @file
+ * VirtualBox Support Driver - IOCtl definitions.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+#ifndef VBOX_INCLUDED_SRC_Support_SUPDrvIOC_h
+#define VBOX_INCLUDED_SRC_Support_SUPDrvIOC_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+#include <iprt/types.h>
+#include <VBox/sup.h>
+
+/*
+ * IOCtl numbers.
+ * We're using the Win32 type of numbers here, thus the macros below.
+ * The SUP_IOCTL_FLAG macro is used to separate requests from 32-bit
+ * and 64-bit processes.
+ */
+#if defined(RT_ARCH_AMD64) || defined(RT_ARCH_SPARC64) || defined(RT_ARCH_ARM64)
+# define SUP_IOCTL_FLAG 128
+#elif defined(RT_ARCH_X86) || defined(RT_ARCH_SPARC) || defined(RT_ARCH_ARM32)
+# define SUP_IOCTL_FLAG 0
+#else
+# error "dunno which arch this is!"
+#endif
+
+#ifdef RT_OS_WINDOWS
+# ifndef CTL_CODE
+# include <iprt/win/windows.h>
+# endif
+ /* Automatic buffering, size not encoded. */
+# define SUP_CTL_CODE_SIZE(Function, Size) CTL_CODE(FILE_DEVICE_UNKNOWN, (Function) | SUP_IOCTL_FLAG, METHOD_BUFFERED, FILE_WRITE_ACCESS)
+# define SUP_CTL_CODE_BIG(Function) CTL_CODE(FILE_DEVICE_UNKNOWN, (Function) | SUP_IOCTL_FLAG, METHOD_BUFFERED, FILE_WRITE_ACCESS)
+# define SUP_CTL_CODE_FAST(Function) CTL_CODE(FILE_DEVICE_UNKNOWN, (Function) | SUP_IOCTL_FLAG, METHOD_NEITHER, FILE_WRITE_ACCESS)
+# define SUP_CTL_CODE_NO_SIZE(uIOCtl) (uIOCtl)
+
+# define SUP_NT_STATUS_BASE UINT32_C(0xe9860000) /**< STATUS_SEVERITY_ERROR + C-bit + facility 0x986. */
+# define SUP_NT_STATUS_IS_VBOX(a_rcNt) ( ((uint32_t)(a_rcNt) & 0xffff0000) == SUP_NT_STATUS_BASE )
+# define SUP_NT_STATUS_TO_VBOX(a_rcNt) ( (int)((uint32_t)(a_rcNt) | UINT32_C(0xffff0000)) )
+
+/** NT device name for system access. */
+# define SUPDRV_NT_DEVICE_NAME_SYS L"\\Device\\VBoxDrv"
+/** NT device name for user access. */
+# define SUPDRV_NT_DEVICE_NAME_USR L"\\Device\\VBoxDrvU"
+# ifdef VBOX_WITH_HARDENING
+/** NT device name for hardened stub access. */
+# define SUPDRV_NT_DEVICE_NAME_STUB L"\\Device\\VBoxDrvStub"
+/** NT device name for getting error information for failed VBoxDrv or
+ * VBoxDrvStub open. */
+# define SUPDRV_NT_DEVICE_NAME_ERROR_INFO L"\\Device\\VBoxDrvErrorInfo"
+# endif
+
+
+#elif defined(RT_OS_SOLARIS)
+ /* No automatic buffering, size limited to 255 bytes. */
+# include <sys/ioccom.h>
+# define SUP_CTL_CODE_SIZE(Function, Size) _IOWRN('V', (Function) | SUP_IOCTL_FLAG, sizeof(SUPREQHDR))
+# define SUP_CTL_CODE_BIG(Function) _IOWRN('V', (Function) | SUP_IOCTL_FLAG, sizeof(SUPREQHDR))
+# define SUP_CTL_CODE_FAST(Function) _IO( 'V', (Function) | SUP_IOCTL_FLAG)
+# define SUP_CTL_CODE_NO_SIZE(uIOCtl) ((uintptr_t)(uIOCtl))
+
+#elif defined(RT_OS_OS2)
+ /* No automatic buffering, size not encoded. */
+# define SUP_CTL_CATEGORY 0xc0
+# define SUP_CTL_CODE_SIZE(Function, Size) ((unsigned char)(Function))
+# define SUP_CTL_CODE_BIG(Function) ((unsigned char)(Function))
+# define SUP_CTL_CATEGORY_FAST 0xc1
+# define SUP_CTL_CODE_FAST(Function) ((unsigned char)(Function))
+# define SUP_CTL_CODE_NO_SIZE(uIOCtl) (uIOCtl)
+
+#elif defined(RT_OS_LINUX)
+ /* No automatic buffering, size limited to 16KB. */
+# include <linux/ioctl.h>
+# define SUP_CTL_CODE_SIZE(Function, Size) _IOC(_IOC_READ | _IOC_WRITE, 'V', (Function) | SUP_IOCTL_FLAG, (Size))
+# define SUP_CTL_CODE_BIG(Function) _IO('V', (Function) | SUP_IOCTL_FLAG)
+# define SUP_CTL_CODE_FAST(Function) _IO('V', (Function) | SUP_IOCTL_FLAG)
+# define SUP_CTL_CODE_NO_SIZE(uIOCtl) ((uIOCtl) & ~IOCSIZE_MASK)
+
+#elif defined(RT_OS_L4)
+ /* Implemented in suplib, no worries. */
+# define SUP_CTL_CODE_SIZE(Function, Size) (Function)
+# define SUP_CTL_CODE_BIG(Function) (Function)
+# define SUP_CTL_CODE_FAST(Function) (Function)
+# define SUP_CTL_CODE_NO_SIZE(uIOCtl) (uIOCtl)
+
+#else /* BSD Like */
+ /* Automatic buffering, size limited to 4KB on *BSD and 8KB on Darwin - commands the limit, 4KB. */
+# include <sys/ioccom.h>
+# define SUP_CTL_CODE_SIZE(Function, Size) _IOC(IOC_INOUT, 'V', (Function) | SUP_IOCTL_FLAG, (Size))
+# define SUP_CTL_CODE_BIG(Function) _IO('V', (Function) | SUP_IOCTL_FLAG)
+# define SUP_CTL_CODE_FAST(Function) _IO('V', (Function) | SUP_IOCTL_FLAG)
+# define SUP_CTL_CODE_NO_SIZE(uIOCtl) ( (uIOCtl) & ~_IOC(0,0,0,IOCPARM_MASK) )
+#endif
+
+/** @name Fast path I/O control codes.
+ * @note These must run parallel to SUP_VMMR0_DO_XXX
+ * @note Implementations ASSUMES up to 32 I/O controls codes in the fast range.
+ * @{ */
+/** Fast path IOCtl: VMMR0_DO_HM_RUN */
+#define SUP_IOCTL_FAST_DO_HM_RUN SUP_CTL_CODE_FAST(64)
+/** Fast path IOCtl: VMMR0_DO_NEM_RUN */
+#define SUP_IOCTL_FAST_DO_NEM_RUN SUP_CTL_CODE_FAST(65)
+/** Just a NOP call for profiling the latency of a fast ioctl call to VMMR0. */
+#define SUP_IOCTL_FAST_DO_NOP SUP_CTL_CODE_FAST(66)
+/** First fast path IOCtl number. */
+#define SUP_IOCTL_FAST_DO_FIRST SUP_IOCTL_FAST_DO_HM_RUN
+/** @} */
+
+
+#ifdef RT_OS_DARWIN
+/** Cookie used to fend off some unwanted clients to the IOService. */
+# define SUP_DARWIN_IOSERVICE_COOKIE 0x64726962 /* 'bird' */
+#endif
+
+
+/*******************************************************************************
+* Structures and Typedefs *
+*******************************************************************************/
+#ifdef RT_ARCH_AMD64
+# pragma pack(8) /* paranoia. */
+#elif defined(RT_ARCH_X86)
+# pragma pack(4) /* paranoia. */
+#endif
+
+
+/**
+ * Common In/Out header.
+ */
+typedef struct SUPREQHDR
+{
+ /** Cookie. */
+ uint32_t u32Cookie;
+ /** Session cookie. */
+ uint32_t u32SessionCookie;
+ /** The size of the input. */
+ uint32_t cbIn;
+ /** The size of the output. */
+ uint32_t cbOut;
+ /** Flags. See SUPREQHDR_FLAGS_* for details and values. */
+ uint32_t fFlags;
+ /** The VBox status code of the operation, out direction only. */
+ int32_t rc;
+} SUPREQHDR;
+/** Pointer to a IOC header. */
+typedef SUPREQHDR *PSUPREQHDR;
+
+/** @name SUPREQHDR::fFlags values
+ * @{ */
+/** Masks out the magic value. */
+#define SUPREQHDR_FLAGS_MAGIC_MASK UINT32_C(0xff0000ff)
+/** The generic mask. */
+#define SUPREQHDR_FLAGS_GEN_MASK UINT32_C(0x0000ff00)
+/** The request specific mask. */
+#define SUPREQHDR_FLAGS_REQ_MASK UINT32_C(0x00ff0000)
+
+/** There is extra input that needs copying on some platforms. */
+#define SUPREQHDR_FLAGS_EXTRA_IN UINT32_C(0x00000100)
+/** There is extra output that needs copying on some platforms. */
+#define SUPREQHDR_FLAGS_EXTRA_OUT UINT32_C(0x00000200)
+
+/** The magic value. */
+#define SUPREQHDR_FLAGS_MAGIC UINT32_C(0x42000042)
+/** The default value. Use this when no special stuff is requested. */
+#define SUPREQHDR_FLAGS_DEFAULT SUPREQHDR_FLAGS_MAGIC
+/** @} */
+
+
+/** @name SUP_IOCTL_COOKIE
+ * @{
+ */
+/** Negotiate cookie. */
+#define SUP_IOCTL_COOKIE SUP_CTL_CODE_SIZE(1, SUP_IOCTL_COOKIE_SIZE)
+/** The request size. */
+#define SUP_IOCTL_COOKIE_SIZE sizeof(SUPCOOKIE)
+/** The SUPREQHDR::cbIn value. */
+#define SUP_IOCTL_COOKIE_SIZE_IN sizeof(SUPREQHDR) + RT_SIZEOFMEMB(SUPCOOKIE, u.In)
+/** The SUPREQHDR::cbOut value. */
+#define SUP_IOCTL_COOKIE_SIZE_OUT sizeof(SUPREQHDR) + RT_SIZEOFMEMB(SUPCOOKIE, u.Out)
+/** SUPCOOKIE_IN magic word. */
+#define SUPCOOKIE_MAGIC "The Magic Word!"
+/** The initial cookie. */
+#define SUPCOOKIE_INITIAL_COOKIE 0x69726f74 /* 'tori' */
+
+/** Current interface version.
+ * The upper 16-bit is the major version, the lower the minor version.
+ * When incompatible changes are made, the upper major number has to be changed.
+ *
+ * Update rules:
+ * -# Only update the major number when incompatible changes have been made to
+ * the IOC interface or the ABI provided via the functions returned by
+ * SUPQUERYFUNCS.
+ * -# When adding new features (new IOC number, new flags, new exports, ++)
+ * only update the minor number and change SUPLib.cpp to require the
+ * new IOC version.
+ * -# When incrementing the major number, clear the minor part and reset
+ * any IOC version requirements in SUPLib.cpp.
+ * -# When increment the major number, execute all pending work.
+ *
+ * @todo Pending work on next major version change:
+ * - nothing
+ */
+#define SUPDRV_IOC_VERSION 0x00330004
+
+/** SUP_IOCTL_COOKIE. */
+typedef struct SUPCOOKIE
+{
+ /** The header.
+ * u32Cookie must be set to SUPCOOKIE_INITIAL_COOKIE.
+ * u32SessionCookie should be set to some random value. */
+ SUPREQHDR Hdr;
+ union
+ {
+ struct
+ {
+ /** Magic word. */
+ char szMagic[16];
+ /** The requested interface version number. */
+ uint32_t u32ReqVersion;
+ /** The minimum interface version number. */
+ uint32_t u32MinVersion;
+ } In;
+ struct
+ {
+ /** Cookie. */
+ uint32_t u32Cookie;
+ /** Session cookie. */
+ uint32_t u32SessionCookie;
+ /** Interface version for this session. */
+ uint32_t u32SessionVersion;
+ /** The actual interface version in the driver. */
+ uint32_t u32DriverVersion;
+ /** Number of functions available for the SUP_IOCTL_QUERY_FUNCS request. */
+ uint32_t cFunctions;
+ /** Session handle. */
+ R0PTRTYPE(PSUPDRVSESSION) pSession;
+ } Out;
+ } u;
+} SUPCOOKIE, *PSUPCOOKIE;
+/** @} */
+
+
+/** @name SUP_IOCTL_QUERY_FUNCS
+ * Query SUPR0 functions.
+ * @{
+ */
+#define SUP_IOCTL_QUERY_FUNCS(cFuncs) SUP_CTL_CODE_BIG(2)
+#define SUP_IOCTL_QUERY_FUNCS_SIZE(cFuncs) RT_UOFFSETOF_DYN(SUPQUERYFUNCS, u.Out.aFunctions[(cFuncs)])
+#define SUP_IOCTL_QUERY_FUNCS_SIZE_IN sizeof(SUPREQHDR)
+#define SUP_IOCTL_QUERY_FUNCS_SIZE_OUT(cFuncs) SUP_IOCTL_QUERY_FUNCS_SIZE(cFuncs)
+
+/** A function. */
+typedef struct SUPFUNC
+{
+ /** Name - mangled. */
+ char szName[47];
+ /** For internal checking. Ignore. */
+ uint8_t cArgs;
+ /** Address. */
+ RTR0PTR pfn;
+} SUPFUNC, *PSUPFUNC;
+
+typedef struct SUPQUERYFUNCS
+{
+ /** The header. */
+ SUPREQHDR Hdr;
+ union
+ {
+ struct
+ {
+ /** Number of functions returned. */
+ uint32_t cFunctions;
+ /** Array of functions. */
+ SUPFUNC aFunctions[1];
+ } Out;
+ } u;
+} SUPQUERYFUNCS, *PSUPQUERYFUNCS;
+/** @} */
+
+
+/** @name SUP_IOCTL_LDR_OPEN
+ * Open an image.
+ * @{
+ */
+#define SUP_IOCTL_LDR_OPEN SUP_CTL_CODE_SIZE(3, SUP_IOCTL_LDR_OPEN_SIZE)
+#define SUP_IOCTL_LDR_OPEN_SIZE sizeof(SUPLDROPEN)
+#define SUP_IOCTL_LDR_OPEN_SIZE_IN sizeof(SUPLDROPEN)
+#define SUP_IOCTL_LDR_OPEN_SIZE_OUT (sizeof(SUPREQHDR) + RT_SIZEOFMEMB(SUPLDROPEN, u.Out))
+typedef struct SUPLDROPEN
+{
+ /** The header. */
+ SUPREQHDR Hdr;
+ union
+ {
+ struct
+ {
+ /** Size of the image we'll be loading (including all tables).
+ * Zero if the caller does not wish to prepare loading anything, then
+ * cbImageBits must be zero too ofc. */
+ uint32_t cbImageWithEverything;
+ /** The size of the image bits. (Less or equal to cbImageWithTabs.)
+ * Zero if the caller does not wish to prepare loading anything. */
+ uint32_t cbImageBits;
+ /** Image name.
+ * This is the NAME of the image, not the file name. It is used
+ * to share code with other processes. (Max len is 32 chars!) */
+ char szName[32];
+ /** Image file name.
+ * This can be used to load the image using a native loader. */
+ char szFilename[260];
+ } In;
+ struct
+ {
+ /** The base address of the image. */
+ RTR0PTR pvImageBase;
+ /** Indicate whether or not the image requires loading. */
+ bool fNeedsLoading;
+ /** Indicates that we're using the native ring-0 loader. */
+ bool fNativeLoader;
+ } Out;
+ } u;
+} SUPLDROPEN, *PSUPLDROPEN;
+/** @} */
+
+
+/** @name SUP_IOCTL_LDR_LOAD
+ * Upload the image bits.
+ * @{
+ */
+#define SUP_IOCTL_LDR_LOAD SUP_CTL_CODE_BIG(4)
+#define SUP_IOCTL_LDR_LOAD_SIZE(cbImage) RT_MAX(RT_UOFFSETOF_DYN(SUPLDRLOAD, u.In.abImage[cbImage]), SUP_IOCTL_LDR_LOAD_SIZE_OUT)
+#define SUP_IOCTL_LDR_LOAD_SIZE_IN(cbImage) RT_UOFFSETOF_DYN(SUPLDRLOAD, u.In.abImage[cbImage])
+#define SUP_IOCTL_LDR_LOAD_SIZE_OUT (RT_UOFFSETOF(SUPLDRLOAD, u.Out.szError) + RT_SIZEOFMEMB(SUPLDRLOAD, u.Out.szError))
+
+/**
+ * Module initialization callback function.
+ * This is called once after the module has been loaded.
+ *
+ * @returns 0 on success.
+ * @returns Appropriate error code on failure.
+ * @param hMod Image handle for use in APIs.
+ */
+typedef DECLCALLBACKTYPE(int, FNR0MODULEINIT,(void *hMod));
+/** Pointer to a FNR0MODULEINIT(). */
+typedef R0PTRTYPE(FNR0MODULEINIT *) PFNR0MODULEINIT;
+
+/**
+ * Module termination callback function.
+ * This is called once right before the module is being unloaded.
+ *
+ * @param hMod Image handle for use in APIs.
+ */
+typedef DECLCALLBACKTYPE(void, FNR0MODULETERM,(void *hMod));
+/** Pointer to a FNR0MODULETERM(). */
+typedef R0PTRTYPE(FNR0MODULETERM *) PFNR0MODULETERM;
+
+/**
+ * Symbol table entry.
+ */
+typedef struct SUPLDRSYM
+{
+ /** Offset into of the string table. */
+ uint32_t offName;
+ /** Offset of the symbol relative to the image load address.
+ * @remarks When used inside the SUPDrv to calculate real addresses, it
+ * must be cast to int32_t for the sake of native loader support
+ * on Solaris. (The loader puts the and data in different
+ * memory areans, and the text one is generally higher.) */
+ uint32_t offSymbol;
+} SUPLDRSYM;
+/** Pointer to a symbol table entry. */
+typedef SUPLDRSYM *PSUPLDRSYM;
+/** Pointer to a const symbol table entry. */
+typedef SUPLDRSYM const *PCSUPLDRSYM;
+
+#define SUPLDR_PROT_READ 1 /**< Grant read access (RTMEM_PROT_READ). */
+#define SUPLDR_PROT_WRITE 2 /**< Grant write access (RTMEM_PROT_WRITE). */
+#define SUPLDR_PROT_EXEC 4 /**< Grant execute access (RTMEM_PROT_EXEC). */
+
+/**
+ * A segment table entry - chiefly for conveying memory protection.
+ */
+typedef struct SUPLDRSEG
+{
+ /** The RVA of the segment. */
+ uint32_t off;
+ /** The size of the segment. */
+ uint32_t cb : 28;
+ /** The segment protection (SUPLDR_PROT_XXX). */
+ uint32_t fProt : 3;
+ /** MBZ. */
+ uint32_t fUnused;
+} SUPLDRSEG;
+/** Pointer to a segment table entry. */
+typedef SUPLDRSEG *PSUPLDRSEG;
+/** Pointer to a const segment table entry. */
+typedef SUPLDRSEG const *PCSUPLDRSEG;
+
+/**
+ * SUPLDRLOAD::u::In::EP type.
+ */
+typedef enum SUPLDRLOADEP
+{
+ SUPLDRLOADEP_NOTHING = 0,
+ SUPLDRLOADEP_VMMR0,
+ SUPLDRLOADEP_SERVICE,
+ SUPLDRLOADEP_32BIT_HACK = 0x7fffffff
+} SUPLDRLOADEP;
+
+typedef struct SUPLDRLOAD
+{
+ /** The header. */
+ SUPREQHDR Hdr;
+ union
+ {
+ struct
+ {
+ /** The address of module initialization function. Similar to _DLL_InitTerm(hmod, 0). */
+ RTR0PTR pfnModuleInit;
+ /** The address of module termination function. Similar to _DLL_InitTerm(hmod, 1). */
+ RTR0PTR pfnModuleTerm;
+ /** Special entry points. */
+ union
+ {
+ /** SUPLDRLOADEP_VMMR0. */
+ struct
+ {
+ /** Address of VMMR0EntryFast function. */
+ RTR0PTR pvVMMR0EntryFast;
+ /** Address of VMMR0EntryEx function. */
+ RTR0PTR pvVMMR0EntryEx;
+ } VMMR0;
+ /** SUPLDRLOADEP_SERVICE. */
+ struct
+ {
+ /** The service request handler.
+ * (PFNR0SERVICEREQHANDLER isn't defined yet.) */
+ RTR0PTR pfnServiceReq;
+ /** Reserved, must be NIL. */
+ RTR0PTR apvReserved[3];
+ } Service;
+ } EP;
+ /** Address. */
+ RTR0PTR pvImageBase;
+ /** Entry point type. */
+ SUPLDRLOADEP eEPType;
+ /** The size of the image bits (starting at offset 0 and
+ * approaching offSymbols). */
+ uint32_t cbImageBits;
+ /** The offset of the symbol table (SUPLDRSYM array). */
+ uint32_t offSymbols;
+ /** The number of entries in the symbol table. */
+ uint32_t cSymbols;
+ /** The offset of the string table. */
+ uint32_t offStrTab;
+ /** Size of the string table. */
+ uint32_t cbStrTab;
+ /** Offset to the segment table (SUPLDRSEG array). */
+ uint32_t offSegments;
+ /** Number of segments. */
+ uint32_t cSegments;
+ /** Size of image data in achImage. */
+ uint32_t cbImageWithEverything;
+ /** Flags (SUPLDRLOAD_F_XXX). */
+ uint32_t fFlags;
+ /** The image data. */
+ uint8_t abImage[1];
+ } In;
+ struct
+ {
+ /** Magic value indicating whether extended error information is
+ * present or not (SUPLDRLOAD_ERROR_MAGIC). */
+ uint64_t uErrorMagic;
+ /** Extended error information. */
+ char szError[2048];
+ } Out;
+ } u;
+} SUPLDRLOAD, *PSUPLDRLOAD;
+/** Magic value that indicates that there is a valid error information string
+ * present on SUP_IOCTL_LDR_LOAD failure.
+ * @remarks The value is choosen to be an unlikely init and term address. */
+#define SUPLDRLOAD_ERROR_MAGIC UINT64_C(0xabcdefef0feddcb9)
+/** The module depends on VMMR0. */
+#define SUPLDRLOAD_F_DEP_VMMR0 RT_BIT_32(0)
+/** Valid flag mask. */
+#define SUPLDRLOAD_F_VALID_MASK UINT32_C(0x00000001)
+/** @} */
+
+
+/** @name SUP_IOCTL_LDR_FREE
+ * Free an image.
+ * @{
+ */
+#define SUP_IOCTL_LDR_FREE SUP_CTL_CODE_SIZE(5, SUP_IOCTL_LDR_FREE_SIZE)
+#define SUP_IOCTL_LDR_FREE_SIZE sizeof(SUPLDRFREE)
+#define SUP_IOCTL_LDR_FREE_SIZE_IN sizeof(SUPLDRFREE)
+#define SUP_IOCTL_LDR_FREE_SIZE_OUT sizeof(SUPREQHDR)
+typedef struct SUPLDRFREE
+{
+ /** The header. */
+ SUPREQHDR Hdr;
+ union
+ {
+ struct
+ {
+ /** Address. */
+ RTR0PTR pvImageBase;
+ } In;
+ } u;
+} SUPLDRFREE, *PSUPLDRFREE;
+/** @} */
+
+
+/** @name SUP_IOCTL_LDR_LOCK_DOWN
+ * Lock down the image loader interface.
+ * @{
+ */
+#define SUP_IOCTL_LDR_LOCK_DOWN SUP_CTL_CODE_SIZE(38, SUP_IOCTL_LDR_LOCK_DOWN_SIZE)
+#define SUP_IOCTL_LDR_LOCK_DOWN_SIZE sizeof(SUPREQHDR)
+#define SUP_IOCTL_LDR_LOCK_DOWN_SIZE_IN sizeof(SUPREQHDR)
+#define SUP_IOCTL_LDR_LOCK_DOWN_SIZE_OUT sizeof(SUPREQHDR)
+/** @} */
+
+
+/** @name SUP_IOCTL_LDR_GET_SYMBOL
+ * Get address of a symbol within an image.
+ * @{
+ */
+#define SUP_IOCTL_LDR_GET_SYMBOL SUP_CTL_CODE_SIZE(6, SUP_IOCTL_LDR_GET_SYMBOL_SIZE)
+#define SUP_IOCTL_LDR_GET_SYMBOL_SIZE sizeof(SUPLDRGETSYMBOL)
+#define SUP_IOCTL_LDR_GET_SYMBOL_SIZE_IN sizeof(SUPLDRGETSYMBOL)
+#define SUP_IOCTL_LDR_GET_SYMBOL_SIZE_OUT (sizeof(SUPREQHDR) + RT_SIZEOFMEMB(SUPLDRGETSYMBOL, u.Out))
+typedef struct SUPLDRGETSYMBOL
+{
+ /** The header. */
+ SUPREQHDR Hdr;
+ union
+ {
+ struct
+ {
+ /** Address. */
+ RTR0PTR pvImageBase;
+ /** The symbol name. */
+ char szSymbol[64];
+ } In;
+ struct
+ {
+ /** The symbol address. */
+ RTR0PTR pvSymbol;
+ } Out;
+ } u;
+} SUPLDRGETSYMBOL, *PSUPLDRGETSYMBOL;
+/** @} */
+
+
+/** @name SUP_IOCTL_CALL_VMMR0
+ * Call the R0 VMM Entry point.
+ * @{
+ */
+#define SUP_IOCTL_CALL_VMMR0(cbReq) SUP_CTL_CODE_SIZE(7, SUP_IOCTL_CALL_VMMR0_SIZE(cbReq))
+#define SUP_IOCTL_CALL_VMMR0_NO_SIZE() SUP_CTL_CODE_SIZE(7, 0)
+#define SUP_IOCTL_CALL_VMMR0_SIZE(cbReq) RT_UOFFSETOF_DYN(SUPCALLVMMR0, abReqPkt[cbReq])
+#define SUP_IOCTL_CALL_VMMR0_SIZE_IN(cbReq) SUP_IOCTL_CALL_VMMR0_SIZE(cbReq)
+#define SUP_IOCTL_CALL_VMMR0_SIZE_OUT(cbReq) SUP_IOCTL_CALL_VMMR0_SIZE(cbReq)
+typedef struct SUPCALLVMMR0
+{
+ /** The header. */
+ SUPREQHDR Hdr;
+ union
+ {
+ struct
+ {
+ /** The VM handle. */
+ PVMR0 pVMR0;
+ /** VCPU id. */
+ uint32_t idCpu;
+ /** Which operation to execute. */
+ uint32_t uOperation;
+ /** Argument to use when no request packet is supplied. */
+ uint64_t u64Arg;
+ } In;
+ } u;
+ /** The VMMR0Entry request packet. */
+ uint8_t abReqPkt[1];
+} SUPCALLVMMR0, *PSUPCALLVMMR0;
+/** @} */
+
+
+/** @name SUP_IOCTL_CALL_VMMR0_BIG
+ * Version of SUP_IOCTL_CALL_VMMR0 for dealing with large requests.
+ * @{
+ */
+#define SUP_IOCTL_CALL_VMMR0_BIG SUP_CTL_CODE_BIG(27)
+#define SUP_IOCTL_CALL_VMMR0_BIG_SIZE(cbReq) RT_UOFFSETOF_DYN(SUPCALLVMMR0, abReqPkt[cbReq])
+#define SUP_IOCTL_CALL_VMMR0_BIG_SIZE_IN(cbReq) SUP_IOCTL_CALL_VMMR0_SIZE(cbReq)
+#define SUP_IOCTL_CALL_VMMR0_BIG_SIZE_OUT(cbReq) SUP_IOCTL_CALL_VMMR0_SIZE(cbReq)
+/** @} */
+
+
+/** @name SUP_IOCTL_LOW_ALLOC
+ * Allocate memory below 4GB (physically).
+ * @{
+ */
+#define SUP_IOCTL_LOW_ALLOC SUP_CTL_CODE_BIG(8)
+#define SUP_IOCTL_LOW_ALLOC_SIZE(cPages) ((uint32_t)RT_UOFFSETOF_DYN(SUPLOWALLOC, u.Out.aPages[cPages]))
+#define SUP_IOCTL_LOW_ALLOC_SIZE_IN (sizeof(SUPREQHDR) + RT_SIZEOFMEMB(SUPLOWALLOC, u.In))
+#define SUP_IOCTL_LOW_ALLOC_SIZE_OUT(cPages) SUP_IOCTL_LOW_ALLOC_SIZE(cPages)
+typedef struct SUPLOWALLOC
+{
+ /** The header. */
+ SUPREQHDR Hdr;
+ union
+ {
+ struct
+ {
+ /** Number of pages to allocate. */
+ uint32_t cPages;
+ } In;
+ struct
+ {
+ /** The ring-3 address of the allocated memory. */
+ RTR3PTR pvR3;
+ /** The ring-0 address of the allocated memory. */
+ RTR0PTR pvR0;
+ /** Array of pages. */
+ RTHCPHYS aPages[1];
+ } Out;
+ } u;
+} SUPLOWALLOC, *PSUPLOWALLOC;
+/** @} */
+
+
+/** @name SUP_IOCTL_LOW_FREE
+ * Free low memory.
+ * @{
+ */
+#define SUP_IOCTL_LOW_FREE SUP_CTL_CODE_SIZE(9, SUP_IOCTL_LOW_FREE_SIZE)
+#define SUP_IOCTL_LOW_FREE_SIZE sizeof(SUPLOWFREE)
+#define SUP_IOCTL_LOW_FREE_SIZE_IN sizeof(SUPLOWFREE)
+#define SUP_IOCTL_LOW_FREE_SIZE_OUT sizeof(SUPREQHDR)
+typedef struct SUPLOWFREE
+{
+ /** The header. */
+ SUPREQHDR Hdr;
+ union
+ {
+ struct
+ {
+ /** The ring-3 address of the memory to free. */
+ RTR3PTR pvR3;
+ } In;
+ } u;
+} SUPLOWFREE, *PSUPLOWFREE;
+/** @} */
+
+
+/** @name SUP_IOCTL_PAGE_ALLOC_EX
+ * Allocate memory and map it into kernel and/or user space. The memory is of
+ * course locked. The result should be freed using SUP_IOCTL_PAGE_FREE.
+ *
+ * @remarks Allocations without a kernel mapping may fail with
+ * VERR_NOT_SUPPORTED on some platforms.
+ *
+ * @{
+ */
+#define SUP_IOCTL_PAGE_ALLOC_EX SUP_CTL_CODE_BIG(10)
+#define SUP_IOCTL_PAGE_ALLOC_EX_SIZE(cPages) RT_UOFFSETOF_DYN(SUPPAGEALLOCEX, u.Out.aPages[cPages])
+#define SUP_IOCTL_PAGE_ALLOC_EX_SIZE_IN (sizeof(SUPREQHDR) + RT_SIZEOFMEMB(SUPPAGEALLOCEX, u.In))
+#define SUP_IOCTL_PAGE_ALLOC_EX_SIZE_OUT(cPages) SUP_IOCTL_PAGE_ALLOC_EX_SIZE(cPages)
+typedef struct SUPPAGEALLOCEX
+{
+ /** The header. */
+ SUPREQHDR Hdr;
+ union
+ {
+ struct
+ {
+ /** Number of pages to allocate */
+ uint32_t cPages;
+ /** Whether it should have kernel mapping. */
+ bool fKernelMapping;
+ /** Whether it should have a user mapping. */
+ bool fUserMapping;
+ /** Reserved. Must be false. */
+ bool fReserved0;
+ /** Reserved. Must be false. */
+ bool fReserved1;
+ } In;
+ struct
+ {
+ /** Returned ring-3 address. */
+ RTR3PTR pvR3;
+ /** Returned ring-0 address. */
+ RTR0PTR pvR0;
+ /** The physical addresses of the allocated pages. */
+ RTHCPHYS aPages[1];
+ } Out;
+ } u;
+} SUPPAGEALLOCEX, *PSUPPAGEALLOCEX;
+/** @} */
+
+
+/** @name SUP_IOCTL_PAGE_MAP_KERNEL
+ * Maps a portion of memory allocated by SUP_IOCTL_PAGE_ALLOC_EX /
+ * SUPR0PageAllocEx into kernel space for use by a device or similar.
+ *
+ * The mapping will be freed together with the ring-3 mapping when
+ * SUP_IOCTL_PAGE_FREE or SUPR0PageFree is called.
+ *
+ * @remarks Not necessarily supported on all platforms.
+ *
+ * @{
+ */
+#define SUP_IOCTL_PAGE_MAP_KERNEL SUP_CTL_CODE_SIZE(11, SUP_IOCTL_PAGE_MAP_KERNEL_SIZE)
+#define SUP_IOCTL_PAGE_MAP_KERNEL_SIZE sizeof(SUPPAGEMAPKERNEL)
+#define SUP_IOCTL_PAGE_MAP_KERNEL_SIZE_IN sizeof(SUPPAGEMAPKERNEL)
+#define SUP_IOCTL_PAGE_MAP_KERNEL_SIZE_OUT sizeof(SUPPAGEMAPKERNEL)
+typedef struct SUPPAGEMAPKERNEL
+{
+ /** The header. */
+ SUPREQHDR Hdr;
+ union
+ {
+ struct
+ {
+ /** The pointer of to the previously allocated memory. */
+ RTR3PTR pvR3;
+ /** The offset to start mapping from. */
+ uint32_t offSub;
+ /** Size of the section to map. */
+ uint32_t cbSub;
+ /** Flags reserved for future fun. */
+ uint32_t fFlags;
+ } In;
+ struct
+ {
+ /** The ring-0 address corresponding to pvR3 + offSub. */
+ RTR0PTR pvR0;
+ } Out;
+ } u;
+} SUPPAGEMAPKERNEL, *PSUPPAGEMAPKERNEL;
+/** @} */
+
+
+/** @name SUP_IOCTL_PAGE_PROTECT
+ * Changes the page level protection of the user and/or kernel mappings of
+ * memory previously allocated by SUPR0PageAllocEx.
+ *
+ * @remarks Not necessarily supported on all platforms.
+ *
+ * @{
+ */
+#define SUP_IOCTL_PAGE_PROTECT SUP_CTL_CODE_SIZE(12, SUP_IOCTL_PAGE_PROTECT_SIZE)
+#define SUP_IOCTL_PAGE_PROTECT_SIZE sizeof(SUPPAGEPROTECT)
+#define SUP_IOCTL_PAGE_PROTECT_SIZE_IN sizeof(SUPPAGEPROTECT)
+#define SUP_IOCTL_PAGE_PROTECT_SIZE_OUT sizeof(SUPPAGEPROTECT)
+typedef struct SUPPAGEPROTECT
+{
+ /** The header. */
+ SUPREQHDR Hdr;
+ union
+ {
+ struct
+ {
+ /** The pointer of to the previously allocated memory.
+ * Pass NIL_RTR3PTR if the ring-0 mapping should remain unaffected. */
+ RTR3PTR pvR3;
+ /** The pointer of to the previously allocated memory.
+ * Pass NIL_RTR0PTR if the ring-0 mapping should remain unaffected. */
+ RTR0PTR pvR0;
+ /** The offset to start changing protection at. */
+ uint32_t offSub;
+ /** Size of the portion that should be changed. */
+ uint32_t cbSub;
+ /** Protection flags, RTMEM_PROT_*. */
+ uint32_t fProt;
+ } In;
+ } u;
+} SUPPAGEPROTECT, *PSUPPAGEPROTECT;
+/** @} */
+
+
+/** @name SUP_IOCTL_PAGE_FREE
+ * Free memory allocated with SUP_IOCTL_PAGE_ALLOC_EX.
+ * @{
+ */
+#define SUP_IOCTL_PAGE_FREE SUP_CTL_CODE_SIZE(13, SUP_IOCTL_PAGE_FREE_SIZE_IN)
+#define SUP_IOCTL_PAGE_FREE_SIZE sizeof(SUPPAGEFREE)
+#define SUP_IOCTL_PAGE_FREE_SIZE_IN sizeof(SUPPAGEFREE)
+#define SUP_IOCTL_PAGE_FREE_SIZE_OUT sizeof(SUPREQHDR)
+typedef struct SUPPAGEFREE
+{
+ /** The header. */
+ SUPREQHDR Hdr;
+ union
+ {
+ struct
+ {
+ /** Address of memory range to free. */
+ RTR3PTR pvR3;
+ } In;
+ } u;
+} SUPPAGEFREE, *PSUPPAGEFREE;
+/** @} */
+
+
+
+
+/** @name SUP_IOCTL_PAGE_LOCK
+ * Pin down physical pages.
+ * @{
+ */
+#define SUP_IOCTL_PAGE_LOCK SUP_CTL_CODE_BIG(14)
+#define SUP_IOCTL_PAGE_LOCK_SIZE(cPages) (RT_MAX((size_t)SUP_IOCTL_PAGE_LOCK_SIZE_IN, (size_t)SUP_IOCTL_PAGE_LOCK_SIZE_OUT(cPages)))
+#define SUP_IOCTL_PAGE_LOCK_SIZE_IN (sizeof(SUPREQHDR) + RT_SIZEOFMEMB(SUPPAGELOCK, u.In))
+#define SUP_IOCTL_PAGE_LOCK_SIZE_OUT(cPages) RT_UOFFSETOF_DYN(SUPPAGELOCK, u.Out.aPages[cPages])
+typedef struct SUPPAGELOCK
+{
+ /** The header. */
+ SUPREQHDR Hdr;
+ union
+ {
+ struct
+ {
+ /** Start of page range. Must be PAGE aligned. */
+ RTR3PTR pvR3;
+ /** The range size given as a page count. */
+ uint32_t cPages;
+ } In;
+
+ struct
+ {
+ /** Array of pages. */
+ RTHCPHYS aPages[1];
+ } Out;
+ } u;
+} SUPPAGELOCK, *PSUPPAGELOCK;
+/** @} */
+
+
+/** @name SUP_IOCTL_PAGE_UNLOCK
+ * Unpin physical pages.
+ * @{ */
+#define SUP_IOCTL_PAGE_UNLOCK SUP_CTL_CODE_SIZE(15, SUP_IOCTL_PAGE_UNLOCK_SIZE)
+#define SUP_IOCTL_PAGE_UNLOCK_SIZE sizeof(SUPPAGEUNLOCK)
+#define SUP_IOCTL_PAGE_UNLOCK_SIZE_IN sizeof(SUPPAGEUNLOCK)
+#define SUP_IOCTL_PAGE_UNLOCK_SIZE_OUT sizeof(SUPREQHDR)
+typedef struct SUPPAGEUNLOCK
+{
+ /** The header. */
+ SUPREQHDR Hdr;
+ union
+ {
+ struct
+ {
+ /** Start of page range of a range previously pinned. */
+ RTR3PTR pvR3;
+ } In;
+ } u;
+} SUPPAGEUNLOCK, *PSUPPAGEUNLOCK;
+/** @} */
+
+
+/** @name SUP_IOCTL_CONT_ALLOC
+ * Allocate continuous memory.
+ * @{
+ */
+#define SUP_IOCTL_CONT_ALLOC SUP_CTL_CODE_SIZE(16, SUP_IOCTL_CONT_ALLOC_SIZE)
+#define SUP_IOCTL_CONT_ALLOC_SIZE sizeof(SUPCONTALLOC)
+#define SUP_IOCTL_CONT_ALLOC_SIZE_IN (sizeof(SUPREQHDR) + RT_SIZEOFMEMB(SUPCONTALLOC, u.In))
+#define SUP_IOCTL_CONT_ALLOC_SIZE_OUT sizeof(SUPCONTALLOC)
+typedef struct SUPCONTALLOC
+{
+ /** The header. */
+ SUPREQHDR Hdr;
+ union
+ {
+ struct
+ {
+ /** The allocation size given as a page count. */
+ uint32_t cPages;
+ } In;
+
+ struct
+ {
+ /** The address of the ring-0 mapping of the allocated memory. */
+ RTR0PTR pvR0;
+ /** The address of the ring-3 mapping of the allocated memory. */
+ RTR3PTR pvR3;
+ /** The physical address of the allocation. */
+ RTHCPHYS HCPhys;
+ } Out;
+ } u;
+} SUPCONTALLOC, *PSUPCONTALLOC;
+/** @} */
+
+
+/** @name SUP_IOCTL_CONT_FREE Input.
+ * @{
+ */
+/** Free continuous memory. */
+#define SUP_IOCTL_CONT_FREE SUP_CTL_CODE_SIZE(17, SUP_IOCTL_CONT_FREE_SIZE)
+#define SUP_IOCTL_CONT_FREE_SIZE sizeof(SUPCONTFREE)
+#define SUP_IOCTL_CONT_FREE_SIZE_IN sizeof(SUPCONTFREE)
+#define SUP_IOCTL_CONT_FREE_SIZE_OUT sizeof(SUPREQHDR)
+typedef struct SUPCONTFREE
+{
+ /** The header. */
+ SUPREQHDR Hdr;
+ union
+ {
+ struct
+ {
+ /** The ring-3 address of the memory to free. */
+ RTR3PTR pvR3;
+ } In;
+ } u;
+} SUPCONTFREE, *PSUPCONTFREE;
+/** @} */
+
+
+/** @name SUP_IOCTL_GET_PAGING_MODE
+ * Get the host paging mode.
+ * @{
+ */
+#define SUP_IOCTL_GET_PAGING_MODE SUP_CTL_CODE_SIZE(18, SUP_IOCTL_GET_PAGING_MODE_SIZE)
+#define SUP_IOCTL_GET_PAGING_MODE_SIZE sizeof(SUPGETPAGINGMODE)
+#define SUP_IOCTL_GET_PAGING_MODE_SIZE_IN sizeof(SUPREQHDR)
+#define SUP_IOCTL_GET_PAGING_MODE_SIZE_OUT sizeof(SUPGETPAGINGMODE)
+typedef struct SUPGETPAGINGMODE
+{
+ /** The header. */
+ SUPREQHDR Hdr;
+ union
+ {
+ struct
+ {
+ /** The paging mode. */
+ SUPPAGINGMODE enmMode;
+ } Out;
+ } u;
+} SUPGETPAGINGMODE, *PSUPGETPAGINGMODE;
+/** @} */
+
+
+/** @name SUP_IOCTL_SET_VM_FOR_FAST
+ * Set the VM handle for doing fast call ioctl calls.
+ * @{
+ */
+#define SUP_IOCTL_SET_VM_FOR_FAST SUP_CTL_CODE_SIZE(19, SUP_IOCTL_SET_VM_FOR_FAST_SIZE)
+#define SUP_IOCTL_SET_VM_FOR_FAST_SIZE sizeof(SUPSETVMFORFAST)
+#define SUP_IOCTL_SET_VM_FOR_FAST_SIZE_IN sizeof(SUPSETVMFORFAST)
+#define SUP_IOCTL_SET_VM_FOR_FAST_SIZE_OUT sizeof(SUPREQHDR)
+typedef struct SUPSETVMFORFAST
+{
+ /** The header. */
+ SUPREQHDR Hdr;
+ union
+ {
+ struct
+ {
+ /** The ring-0 VM handle (pointer). */
+ PVMR0 pVMR0;
+ } In;
+ } u;
+} SUPSETVMFORFAST, *PSUPSETVMFORFAST;
+/** @} */
+
+
+/** @name SUP_IOCTL_GIP_MAP
+ * Map the GIP into user space.
+ * @{
+ */
+#define SUP_IOCTL_GIP_MAP SUP_CTL_CODE_SIZE(20, SUP_IOCTL_GIP_MAP_SIZE)
+#define SUP_IOCTL_GIP_MAP_SIZE sizeof(SUPGIPMAP)
+#define SUP_IOCTL_GIP_MAP_SIZE_IN sizeof(SUPREQHDR)
+#define SUP_IOCTL_GIP_MAP_SIZE_OUT sizeof(SUPGIPMAP)
+typedef struct SUPGIPMAP
+{
+ /** The header. */
+ SUPREQHDR Hdr;
+ union
+ {
+ struct
+ {
+ /** The physical address of the GIP. */
+ RTHCPHYS HCPhysGip;
+ /** Pointer to the read-only usermode GIP mapping for this session. */
+ R3PTRTYPE(PSUPGLOBALINFOPAGE) pGipR3;
+ /** Pointer to the supervisor mode GIP mapping. */
+ R0PTRTYPE(PSUPGLOBALINFOPAGE) pGipR0;
+ } Out;
+ } u;
+} SUPGIPMAP, *PSUPGIPMAP;
+/** @} */
+
+
+/** @name SUP_IOCTL_GIP_UNMAP
+ * Unmap the GIP.
+ * @{
+ */
+#define SUP_IOCTL_GIP_UNMAP SUP_CTL_CODE_SIZE(21, SUP_IOCTL_GIP_UNMAP_SIZE)
+#define SUP_IOCTL_GIP_UNMAP_SIZE sizeof(SUPGIPUNMAP)
+#define SUP_IOCTL_GIP_UNMAP_SIZE_IN sizeof(SUPGIPUNMAP)
+#define SUP_IOCTL_GIP_UNMAP_SIZE_OUT sizeof(SUPGIPUNMAP)
+typedef struct SUPGIPUNMAP
+{
+ /** The header. */
+ SUPREQHDR Hdr;
+} SUPGIPUNMAP, *PSUPGIPUNMAP;
+/** @} */
+
+
+/** @name SUP_IOCTL_CALL_SERVICE
+ * Call the a ring-0 service.
+ *
+ * @todo Might have to convert this to a big request, just like
+ * SUP_IOCTL_CALL_VMMR0
+ * @{
+ */
+#define SUP_IOCTL_CALL_SERVICE(cbReq) SUP_CTL_CODE_SIZE(22, SUP_IOCTL_CALL_SERVICE_SIZE(cbReq))
+#define SUP_IOCTL_CALL_SERVICE_NO_SIZE() SUP_CTL_CODE_SIZE(22, 0)
+#define SUP_IOCTL_CALL_SERVICE_SIZE(cbReq) RT_UOFFSETOF_DYN(SUPCALLSERVICE, abReqPkt[cbReq])
+#define SUP_IOCTL_CALL_SERVICE_SIZE_IN(cbReq) SUP_IOCTL_CALL_SERVICE_SIZE(cbReq)
+#define SUP_IOCTL_CALL_SERVICE_SIZE_OUT(cbReq) SUP_IOCTL_CALL_SERVICE_SIZE(cbReq)
+typedef struct SUPCALLSERVICE
+{
+ /** The header. */
+ SUPREQHDR Hdr;
+ union
+ {
+ struct
+ {
+ /** The service name. */
+ char szName[28];
+ /** Which operation to execute. */
+ uint32_t uOperation;
+ /** Argument to use when no request packet is supplied. */
+ uint64_t u64Arg;
+ } In;
+ } u;
+ /** The request packet passed to SUP. */
+ uint8_t abReqPkt[1];
+} SUPCALLSERVICE, *PSUPCALLSERVICE;
+/** @} */
+
+
+/** @name SUP_IOCTL_LOGGER_SETTINGS
+ * Changes the ring-0 release or debug logger settings.
+ * @{
+ */
+#define SUP_IOCTL_LOGGER_SETTINGS(cbStrTab) SUP_CTL_CODE_SIZE(23, SUP_IOCTL_LOGGER_SETTINGS_SIZE(cbStrTab))
+#define SUP_IOCTL_LOGGER_SETTINGS_NO_SIZE() SUP_CTL_CODE_SIZE(23, 0)
+#define SUP_IOCTL_LOGGER_SETTINGS_SIZE(cbStrTab) RT_UOFFSETOF_DYN(SUPLOGGERSETTINGS, u.In.szStrings[cbStrTab])
+#define SUP_IOCTL_LOGGER_SETTINGS_SIZE_IN(cbStrTab) RT_UOFFSETOF_DYN(SUPLOGGERSETTINGS, u.In.szStrings[cbStrTab])
+#define SUP_IOCTL_LOGGER_SETTINGS_SIZE_OUT sizeof(SUPREQHDR)
+typedef struct SUPLOGGERSETTINGS
+{
+ /** The header. */
+ SUPREQHDR Hdr;
+ union
+ {
+ struct
+ {
+ /** Which logger. */
+ uint32_t fWhich;
+ /** What to do with it. */
+ uint32_t fWhat;
+ /** Offset of the flags setting string. */
+ uint32_t offFlags;
+ /** Offset of the groups setting string. */
+ uint32_t offGroups;
+ /** Offset of the destination setting string. */
+ uint32_t offDestination;
+ /** The string table. */
+ char szStrings[1];
+ } In;
+ } u;
+} SUPLOGGERSETTINGS, *PSUPLOGGERSETTINGS;
+
+/** Debug logger. */
+#define SUPLOGGERSETTINGS_WHICH_DEBUG 0
+/** Release logger. */
+#define SUPLOGGERSETTINGS_WHICH_RELEASE 1
+
+/** Change the settings. */
+#define SUPLOGGERSETTINGS_WHAT_SETTINGS 0
+/** Create the logger instance. */
+#define SUPLOGGERSETTINGS_WHAT_CREATE 1
+/** Destroy the logger instance. */
+#define SUPLOGGERSETTINGS_WHAT_DESTROY 2
+
+/** @} */
+
+
+/** @name Semaphore Types
+ * @{ */
+#define SUP_SEM_TYPE_EVENT 0
+#define SUP_SEM_TYPE_EVENT_MULTI 1
+/** @} */
+
+
+/** @name SUP_IOCTL_SEM_OP2
+ * Semaphore operations.
+ * @remarks This replaces the old SUP_IOCTL_SEM_OP interface.
+ * @{
+ */
+#define SUP_IOCTL_SEM_OP2 SUP_CTL_CODE_SIZE(24, SUP_IOCTL_SEM_OP2_SIZE)
+#define SUP_IOCTL_SEM_OP2_SIZE sizeof(SUPSEMOP2)
+#define SUP_IOCTL_SEM_OP2_SIZE_IN sizeof(SUPSEMOP2)
+#define SUP_IOCTL_SEM_OP2_SIZE_OUT sizeof(SUPREQHDR)
+typedef struct SUPSEMOP2
+{
+ /** The header. */
+ SUPREQHDR Hdr;
+ union
+ {
+ struct
+ {
+ /** The semaphore type. */
+ uint32_t uType;
+ /** The semaphore handle. */
+ uint32_t hSem;
+ /** The operation. */
+ uint32_t uOp;
+ /** Reserved, must be zero. */
+ uint32_t uReserved;
+ /** The number of milliseconds to wait if it's a wait operation. */
+ union
+ {
+ /** Absolute timeout (RTTime[System]NanoTS).
+ * Used by SUPSEMOP2_WAIT_NS_ABS. */
+ uint64_t uAbsNsTimeout;
+ /** Relative nanosecond timeout.
+ * Used by SUPSEMOP2_WAIT_NS_REL. */
+ uint64_t cRelNsTimeout;
+ /** Relative millisecond timeout.
+ * Used by SUPSEMOP2_WAIT_MS_REL. */
+ uint32_t cRelMsTimeout;
+ /** Generic 64-bit accessor.
+ * ASSUMES little endian! */
+ uint64_t u64;
+ } uArg;
+ } In;
+ } u;
+} SUPSEMOP2, *PSUPSEMOP2;
+
+/** Wait for a number of milliseconds. */
+#define SUPSEMOP2_WAIT_MS_REL 0
+/** Wait until the specified deadline is reached. */
+#define SUPSEMOP2_WAIT_NS_ABS 1
+/** Wait for a number of nanoseconds. */
+#define SUPSEMOP2_WAIT_NS_REL 2
+/** Signal the semaphore. */
+#define SUPSEMOP2_SIGNAL 3
+/** Reset the semaphore (only applicable to SUP_SEM_TYPE_EVENT_MULTI). */
+#define SUPSEMOP2_RESET 4
+/** Close the semaphore handle. */
+#define SUPSEMOP2_CLOSE 5
+/** @} */
+
+
+/** @name SUP_IOCTL_SEM_OP3
+ * Semaphore operations.
+ * @{
+ */
+#define SUP_IOCTL_SEM_OP3 SUP_CTL_CODE_SIZE(25, SUP_IOCTL_SEM_OP3_SIZE)
+#define SUP_IOCTL_SEM_OP3_SIZE sizeof(SUPSEMOP3)
+#define SUP_IOCTL_SEM_OP3_SIZE_IN sizeof(SUPSEMOP3)
+#define SUP_IOCTL_SEM_OP3_SIZE_OUT sizeof(SUPSEMOP3)
+typedef struct SUPSEMOP3
+{
+ /** The header. */
+ SUPREQHDR Hdr;
+ union
+ {
+ struct
+ {
+ /** The semaphore type. */
+ uint32_t uType;
+ /** The semaphore handle. */
+ uint32_t hSem;
+ /** The operation. */
+ uint32_t uOp;
+ /** Reserved, must be zero. */
+ uint32_t u32Reserved;
+ /** Reserved for future use. */
+ uint64_t u64Reserved;
+ } In;
+ union
+ {
+ /** The handle of the created semaphore.
+ * Used by SUPSEMOP3_CREATE. */
+ uint32_t hSem;
+ /** The semaphore resolution in nano seconds.
+ * Used by SUPSEMOP3_GET_RESOLUTION. */
+ uint32_t cNsResolution;
+ /** The 32-bit view. */
+ uint32_t u32;
+ /** Reserved some space for later expansion. */
+ uint64_t u64Reserved;
+ } Out;
+ } u;
+} SUPSEMOP3, *PSUPSEMOP3;
+
+/** Get the wait resolution. */
+#define SUPSEMOP3_CREATE 0
+/** Get the wait resolution. */
+#define SUPSEMOP3_GET_RESOLUTION 1
+/** @} */
+
+
+/** @name SUP_IOCTL_VT_CAPS
+ * Get the VT-x/AMD-V capabilities.
+ *
+ * @todo Intended for main, which means we need to relax the privilege requires
+ * when accessing certain vboxdrv functions.
+ *
+ * @{
+ */
+#define SUP_IOCTL_VT_CAPS SUP_CTL_CODE_SIZE(26, SUP_IOCTL_VT_CAPS_SIZE)
+#define SUP_IOCTL_VT_CAPS_SIZE sizeof(SUPVTCAPS)
+#define SUP_IOCTL_VT_CAPS_SIZE_IN sizeof(SUPREQHDR)
+#define SUP_IOCTL_VT_CAPS_SIZE_OUT sizeof(SUPVTCAPS)
+typedef struct SUPVTCAPS
+{
+ /** The header. */
+ SUPREQHDR Hdr;
+ union
+ {
+ struct
+ {
+ /** The VT capability dword. */
+ uint32_t fCaps;
+ } Out;
+ } u;
+} SUPVTCAPS, *PSUPVTCAPS;
+/** @} */
+
+
+/** @name SUP_IOCTL_TRACER_OPEN
+ * Open the tracer.
+ *
+ * Should be matched by an SUP_IOCTL_TRACER_CLOSE call.
+ *
+ * @{
+ */
+#define SUP_IOCTL_TRACER_OPEN SUP_CTL_CODE_SIZE(28, SUP_IOCTL_TRACER_OPEN_SIZE)
+#define SUP_IOCTL_TRACER_OPEN_SIZE sizeof(SUPTRACEROPEN)
+#define SUP_IOCTL_TRACER_OPEN_SIZE_IN sizeof(SUPTRACEROPEN)
+#define SUP_IOCTL_TRACER_OPEN_SIZE_OUT sizeof(SUPREQHDR)
+typedef struct SUPTRACEROPEN
+{
+ /** The header. */
+ SUPREQHDR Hdr;
+ union
+ {
+ struct
+ {
+ /** Tracer cookie. Used to make sure we only open a matching tracer. */
+ uint32_t uCookie;
+ /** Tracer specific argument. */
+ RTHCUINTPTR uArg;
+ } In;
+ } u;
+} SUPTRACEROPEN, *PSUPTRACEROPEN;
+/** @} */
+
+
+/** @name SUP_IOCTL_TRACER_CLOSE
+ * Close the tracer.
+ *
+ * Must match a SUP_IOCTL_TRACER_OPEN call.
+ *
+ * @{
+ */
+#define SUP_IOCTL_TRACER_CLOSE SUP_CTL_CODE_SIZE(29, SUP_IOCTL_TRACER_CLOSE_SIZE)
+#define SUP_IOCTL_TRACER_CLOSE_SIZE sizeof(SUPREQHDR)
+#define SUP_IOCTL_TRACER_CLOSE_SIZE_IN sizeof(SUPREQHDR)
+#define SUP_IOCTL_TRACER_CLOSE_SIZE_OUT sizeof(SUPREQHDR)
+/** @} */
+
+
+/** @name SUP_IOCTL_TRACER_IOCTL
+ * Speak UNIX ioctl() with the tracer.
+ *
+ * The session must have opened the tracer prior to issuing this request.
+ *
+ * @{
+ */
+#define SUP_IOCTL_TRACER_IOCTL SUP_CTL_CODE_SIZE(30, SUP_IOCTL_TRACER_IOCTL_SIZE)
+#define SUP_IOCTL_TRACER_IOCTL_SIZE sizeof(SUPTRACERIOCTL)
+#define SUP_IOCTL_TRACER_IOCTL_SIZE_IN sizeof(SUPTRACERIOCTL)
+#define SUP_IOCTL_TRACER_IOCTL_SIZE_OUT (RT_UOFFSETOF(SUPTRACERIOCTL, u.Out.iRetVal) + sizeof(int32_t))
+typedef struct SUPTRACERIOCTL
+{
+ /** The header. */
+ SUPREQHDR Hdr;
+ union
+ {
+ struct
+ {
+ /** The command. */
+ RTHCUINTPTR uCmd;
+ /** Argument to the command. */
+ RTHCUINTPTR uArg;
+ } In;
+
+ struct
+ {
+ /** The return value. */
+ int32_t iRetVal;
+ } Out;
+ } u;
+} SUPTRACERIOCTL, *PSUPTRACERIOCTL;
+/** @} */
+
+
+/** @name SUP_IOCTL_TRACER_UMOD_REG
+ * Registers tracepoints in a user mode module.
+ *
+ * @{
+ */
+#define SUP_IOCTL_TRACER_UMOD_REG SUP_CTL_CODE_SIZE(31, SUP_IOCTL_TRACER_UMOD_REG_SIZE)
+#define SUP_IOCTL_TRACER_UMOD_REG_SIZE sizeof(SUPTRACERUMODREG)
+#define SUP_IOCTL_TRACER_UMOD_REG_SIZE_IN sizeof(SUPTRACERUMODREG)
+#define SUP_IOCTL_TRACER_UMOD_REG_SIZE_OUT sizeof(SUPREQHDR)
+typedef struct SUPTRACERUMODREG
+{
+ /** The header. */
+ SUPREQHDR Hdr;
+ union
+ {
+ struct
+ {
+ /** The address at which the VTG header actually resides.
+ * This will differ from R3PtrVtgHdr for raw-mode context
+ * modules. */
+ RTUINTPTR uVtgHdrAddr;
+ /** The ring-3 pointer of the VTG header. */
+ RTR3PTR R3PtrVtgHdr;
+ /** The ring-3 pointer of the probe location string table. */
+ RTR3PTR R3PtrStrTab;
+ /** The size of the string table. */
+ uint32_t cbStrTab;
+ /** Future flags, MBZ. */
+ uint32_t fFlags;
+ /** The module name. */
+ char szName[64];
+ } In;
+ } u;
+} SUPTRACERUMODREG, *PSUPTRACERUMODREG;
+/** @} */
+
+
+/** @name SUP_IOCTL_TRACER_UMOD_DEREG
+ * Deregisters tracepoints in a user mode module.
+ *
+ * @{
+ */
+#define SUP_IOCTL_TRACER_UMOD_DEREG SUP_CTL_CODE_SIZE(32, SUP_IOCTL_TRACER_UMOD_DEREG_SIZE)
+#define SUP_IOCTL_TRACER_UMOD_DEREG_SIZE sizeof(SUPTRACERUMODDEREG)
+#define SUP_IOCTL_TRACER_UMOD_DEREG_SIZE_IN sizeof(SUPTRACERUMODDEREG)
+#define SUP_IOCTL_TRACER_UMOD_DEREG_SIZE_OUT sizeof(SUPREQHDR)
+typedef struct SUPTRACERUMODDEREG
+{
+ /** The header. */
+ SUPREQHDR Hdr;
+ union
+ {
+ struct
+ {
+ /** Pointer to the VTG header. */
+ RTR3PTR pVtgHdr;
+ } In;
+ } u;
+} SUPTRACERUMODDEREG, *PSUPTRACERUMODDEREG;
+/** @} */
+
+
+/** @name SUP_IOCTL_TRACER_UMOD_FIRE_PROBE
+ * Fire a probe in a user tracepoint module.
+ *
+ * @{
+ */
+#define SUP_IOCTL_TRACER_UMOD_FIRE_PROBE SUP_CTL_CODE_SIZE(33, SUP_IOCTL_TRACER_UMOD_FIRE_PROBE_SIZE)
+#define SUP_IOCTL_TRACER_UMOD_FIRE_PROBE_SIZE sizeof(SUPTRACERUMODFIREPROBE)
+#define SUP_IOCTL_TRACER_UMOD_FIRE_PROBE_SIZE_IN sizeof(SUPTRACERUMODFIREPROBE)
+#define SUP_IOCTL_TRACER_UMOD_FIRE_PROBE_SIZE_OUT sizeof(SUPREQHDR)
+typedef struct SUPTRACERUMODFIREPROBE
+{
+ /** The header. */
+ SUPREQHDR Hdr;
+ union
+ {
+ SUPDRVTRACERUSRCTX In;
+ } u;
+} SUPTRACERUMODFIREPROBE, *PSUPTRACERUMODFIREPROBE;
+/** @} */
+
+
+/** @name SUP_IOCTL_MSR_PROBER
+ * MSR probing interface, not available in normal builds.
+ *
+ * @{
+ */
+#define SUP_IOCTL_MSR_PROBER SUP_CTL_CODE_SIZE(34, SUP_IOCTL_MSR_PROBER_SIZE)
+#define SUP_IOCTL_MSR_PROBER_SIZE sizeof(SUPMSRPROBER)
+#define SUP_IOCTL_MSR_PROBER_SIZE_IN sizeof(SUPMSRPROBER)
+#define SUP_IOCTL_MSR_PROBER_SIZE_OUT sizeof(SUPMSRPROBER)
+
+typedef enum SUPMSRPROBEROP
+{
+ SUPMSRPROBEROP_INVALID = 0, /**< The customary invalid zero value. */
+ SUPMSRPROBEROP_READ, /**< Read an MSR. */
+ SUPMSRPROBEROP_WRITE, /**< Write a value to an MSR (use with care!). */
+ SUPMSRPROBEROP_MODIFY, /**< Read-modify-restore-flushall. */
+ SUPMSRPROBEROP_MODIFY_FASTER, /**< Read-modify-restore, skip the flushing. */
+ SUPMSRPROBEROP_END, /**< End of valid values. */
+ SUPMSRPROBEROP_32BIT_HACK = 0x7fffffff /**< The customary 32-bit type hack. */
+} SUPMSRPROBEROP;
+
+typedef struct SUPMSRPROBER
+{
+ /** The header. */
+ SUPREQHDR Hdr;
+
+ /** Input/output union. */
+ union
+ {
+ /** Inputs. */
+ struct
+ {
+ /** The operation. */
+ SUPMSRPROBEROP enmOp;
+ /** The MSR to test. */
+ uint32_t uMsr;
+ /** The CPU to perform the operation on.
+ * Use UINT32_MAX to indicate that any CPU will do. */
+ uint32_t idCpu;
+ /** Alignment padding. */
+ uint32_t u32Padding;
+ /** Operation specific arguments. */
+ union
+ {
+ /* SUPMSRPROBEROP_READ takes no extra arguments. */
+
+ /** For SUPMSRPROBEROP_WRITE. */
+ struct
+ {
+ /** The value to write. */
+ uint64_t uToWrite;
+ } Write;
+
+ /** For SUPMSRPROBEROP_MODIFY and SUPMSRPROBEROP_MODIFY_FASTER. */
+ struct
+ {
+ /** The value to AND the current MSR value with to construct the value to
+ * write. This applied first. */
+ uint64_t fAndMask;
+ /** The value to OR the result of the above mentioned AND operation with
+ * attempting to modify the MSR. */
+ uint64_t fOrMask;
+ } Modify;
+
+ /** Reserve space for the future. */
+ uint64_t auPadding[3];
+ } uArgs;
+ } In;
+
+ /** Outputs. */
+ struct
+ {
+ /** Operation specific results. */
+ union
+ {
+ /** For SUPMSRPROBEROP_READ. */
+ struct
+ {
+ /** The value we've read. */
+ uint64_t uValue;
+ /** Set if we GPed while reading it. */
+ bool fGp;
+ } Read;
+
+ /** For SUPMSRPROBEROP_WRITE. */
+ struct
+ {
+ /** Set if we GPed while writing it. */
+ bool fGp;
+ } Write;
+
+ /** For SUPMSRPROBEROP_MODIFY and SUPMSRPROBEROP_MODIFY_FASTER. */
+ SUPMSRPROBERMODIFYRESULT Modify;
+
+ /** Size padding/aligning. */
+ uint64_t auPadding[5];
+ } uResults;
+ } Out;
+ } u;
+} SUPMSRPROBER, *PSUPMSRPROBER;
+AssertCompileMemberAlignment(SUPMSRPROBER, u, 8);
+AssertCompileMemberAlignment(SUPMSRPROBER, u.In.uArgs, 8);
+AssertCompileMembersSameSizeAndOffset(SUPMSRPROBER, u.In, SUPMSRPROBER, u.Out);
+/** @} */
+
+/** @name SUP_IOCTL_RESUME_SUSPENDED_KBDS
+ * Resume suspended keyboard devices if any found in the system.
+ *
+ * @{
+ */
+#define SUP_IOCTL_RESUME_SUSPENDED_KBDS SUP_CTL_CODE_SIZE(35, SUP_IOCTL_RESUME_SUSPENDED_KBDS_SIZE)
+#define SUP_IOCTL_RESUME_SUSPENDED_KBDS_SIZE sizeof(SUPREQHDR)
+#define SUP_IOCTL_RESUME_SUSPENDED_KBDS_SIZE_IN sizeof(SUPREQHDR)
+#define SUP_IOCTL_RESUME_SUSPENDED_KBDS_SIZE_OUT sizeof(SUPREQHDR)
+/** @} */
+
+
+/** @name SUP_IOCTL_TSC_DELTA_MEASURE
+ * Measure the TSC-delta between the specified CPU and the master TSC.
+ *
+ * To call this I/O control, the client must first have mapped the GIP.
+ *
+ * @{
+ */
+#define SUP_IOCTL_TSC_DELTA_MEASURE SUP_CTL_CODE_SIZE(36, SUP_IOCTL_TSC_DELTA_MEASURE_SIZE)
+#define SUP_IOCTL_TSC_DELTA_MEASURE_SIZE sizeof(SUPTSCDELTAMEASURE)
+#define SUP_IOCTL_TSC_DELTA_MEASURE_SIZE_IN sizeof(SUPTSCDELTAMEASURE)
+#define SUP_IOCTL_TSC_DELTA_MEASURE_SIZE_OUT sizeof(SUPREQHDR)
+typedef struct SUPTSCDELTAMEASURE
+{
+ /** The header. */
+ SUPREQHDR Hdr;
+
+ /** Input/output union. */
+ union
+ {
+ struct
+ {
+ /** Which CPU to take the TSC-delta measurement for. */
+ RTCPUID idCpu;
+ /** Number of times to retry on failure (specify 0 for default). */
+ uint8_t cRetries;
+ /** Number of milliseconds to wait before each retry. */
+ uint8_t cMsWaitRetry;
+ /** Whether to force taking a measurement if one exists already. */
+ bool fForce;
+ /** Whether to do the measurement asynchronously (if possible). */
+ bool fAsync;
+ } In;
+ } u;
+} SUPTSCDELTAMEASURE, *PSUPTSCDELTAMEASURE;
+AssertCompileMemberAlignment(SUPTSCDELTAMEASURE, u, 8);
+AssertCompileSize(SUPTSCDELTAMEASURE, 6*4 + 4+1+1+1+1);
+/** @} */
+
+
+/** @name SUP_IOCTL_TSC_READ
+ * Reads the TSC and apply TSC-delta if applicable, determining the delta if
+ * necessary (i64TSCDelta = INT64_MAX).
+ *
+ * This latter function is the primary use case of this I/O control. To call
+ * this I/O control, the client must first have mapped the GIP.
+ *
+ * @{
+ */
+#define SUP_IOCTL_TSC_READ SUP_CTL_CODE_SIZE(37, SUP_IOCTL_TSC_READ_SIZE)
+#define SUP_IOCTL_TSC_READ_SIZE sizeof(SUPTSCREAD)
+#define SUP_IOCTL_TSC_READ_SIZE_IN sizeof(SUPREQHDR)
+#define SUP_IOCTL_TSC_READ_SIZE_OUT sizeof(SUPTSCREAD)
+typedef struct SUPTSCREAD
+{
+ /** The header. */
+ SUPREQHDR Hdr;
+
+ /** Input/output union. */
+ union
+ {
+ struct
+ {
+ /** The TSC after applying the relevant delta. */
+ uint64_t u64AdjustedTsc;
+ /** The APIC Id of the CPU where the TSC was read. */
+ uint16_t idApic;
+ /** Explicit alignment padding. */
+ uint16_t auPadding[3];
+ } Out;
+ } u;
+} SUPTSCREAD, *PSUPTSCREAD;
+AssertCompileMemberAlignment(SUPTSCREAD, u, 8);
+AssertCompileSize(SUPTSCREAD, 6*4 + 2*8);
+/** @} */
+
+
+/** @name SUP_IOCTL_GIP_SET_FLAGS
+ * Set GIP flags.
+ *
+ * @{
+ */
+#define SUP_IOCTL_GIP_SET_FLAGS SUP_CTL_CODE_SIZE(39, SUP_IOCTL_GIP_SET_FLAGS_SIZE)
+#define SUP_IOCTL_GIP_SET_FLAGS_SIZE sizeof(SUPGIPSETFLAGS)
+#define SUP_IOCTL_GIP_SET_FLAGS_SIZE_IN sizeof(SUPGIPSETFLAGS)
+#define SUP_IOCTL_GIP_SET_FLAGS_SIZE_OUT sizeof(SUPREQHDR)
+typedef struct SUPGIPSETFLAGS
+{
+ /** The header. */
+ SUPREQHDR Hdr;
+ union
+ {
+ struct
+ {
+ /** The AND flags mask, see SUPGIP_FLAGS_XXX. */
+ uint32_t fAndMask;
+ /** The OR flags mask, see SUPGIP_FLAGS_XXX. */
+ uint32_t fOrMask;
+ } In;
+ } u;
+} SUPGIPSETFLAGS, *PSUPGIPSETFLAGS;
+/** @} */
+
+
+/** @name SUP_IOCTL_UCODE_REV
+ * Get the CPU microcode revision.
+ *
+ * @{
+ */
+#define SUP_IOCTL_UCODE_REV SUP_CTL_CODE_SIZE(40, SUP_IOCTL_UCODE_REV_SIZE)
+#define SUP_IOCTL_UCODE_REV_SIZE sizeof(SUPUCODEREV)
+#define SUP_IOCTL_UCODE_REV_SIZE_IN sizeof(SUPREQHDR)
+#define SUP_IOCTL_UCODE_REV_SIZE_OUT sizeof(SUPUCODEREV)
+typedef struct SUPUCODEREV
+{
+ /** The header. */
+ SUPREQHDR Hdr;
+ union
+ {
+ struct
+ {
+ /** The microcode revision dword. */
+ uint32_t MicrocodeRev;
+ } Out;
+ } u;
+} SUPUCODEREV, *PSUPUCODEREV;
+/** @} */
+
+
+/** @name SUP_IOCTL_HWVIRT_MSRS
+ * Get hardware-virtualization MSRs.
+ *
+ * This queries a lot more information than merely VT-x/AMD-V basic capabilities
+ * provided by SUP_IOCTL_VT_CAPS.
+ *
+ * @{
+ */
+#define SUP_IOCTL_GET_HWVIRT_MSRS SUP_CTL_CODE_SIZE(41, SUP_IOCTL_GET_HWVIRT_MSRS_SIZE)
+#define SUP_IOCTL_GET_HWVIRT_MSRS_SIZE sizeof(SUPGETHWVIRTMSRS)
+#define SUP_IOCTL_GET_HWVIRT_MSRS_SIZE_IN (sizeof(SUPREQHDR) + RT_SIZEOFMEMB(SUPGETHWVIRTMSRS, u.In))
+#define SUP_IOCTL_GET_HWVIRT_MSRS_SIZE_OUT sizeof(SUPGETHWVIRTMSRS)
+
+typedef struct SUPGETHWVIRTMSRS
+{
+ /** The header. */
+ SUPREQHDR Hdr;
+ union
+ {
+ struct
+ {
+ /** Whether to force re-querying of MSRs. */
+ bool fForce;
+ /** Reserved. Must be false. */
+ bool fReserved0;
+ /** Reserved. Must be false. */
+ bool fReserved1;
+ /** Reserved. Must be false. */
+ bool fReserved2;
+ } In;
+
+ struct
+ {
+ /** Hardware-virtualization MSRs. */
+ SUPHWVIRTMSRS HwvirtMsrs;
+ } Out;
+ } u;
+} SUPGETHWVIRTMSRS, *PSUPGETHWVIRTMSRS;
+/** @} */
+
+
+#if defined(RT_ARCH_AMD64) || defined(RT_ARCH_X86)
+# pragma pack() /* paranoia */
+#endif
+
+#endif /* !VBOX_INCLUDED_SRC_Support_SUPDrvIOC_h */
+
diff --git a/src/VBox/HostDrivers/Support/SUPDrvInternal.h b/src/VBox/HostDrivers/Support/SUPDrvInternal.h
new file mode 100644
index 00000000..78ad95d8
--- /dev/null
+++ b/src/VBox/HostDrivers/Support/SUPDrvInternal.h
@@ -0,0 +1,1296 @@
+/* $Id: SUPDrvInternal.h $ */
+/** @file
+ * VirtualBox Support Driver - Internal header.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+#ifndef VBOX_INCLUDED_SRC_Support_SUPDrvInternal_h
+#define VBOX_INCLUDED_SRC_Support_SUPDrvInternal_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include <VBox/cdefs.h>
+#include <VBox/types.h>
+#include <VBox/sup.h>
+
+#include <iprt/assert.h>
+#include <iprt/list.h>
+#include <iprt/memobj.h>
+#include <iprt/time.h>
+#include <iprt/timer.h>
+#include <iprt/string.h>
+#include <iprt/err.h>
+
+#if defined(SUPDRV_AGNOSTIC) && !defined(RT_OS_LINUX)
+/* do nothing */
+
+#elif defined(RT_OS_WINDOWS)
+# include <iprt/nt/nt.h>
+# include <memory.h>
+
+#elif defined(RT_OS_LINUX)
+# include <iprt/linux/version.h>
+# if RTLNX_VER_MIN(2,6,33)
+# include <generated/autoconf.h>
+# else
+# ifndef AUTOCONF_INCLUDED
+# include <linux/autoconf.h>
+# endif
+# endif
+# if defined(CONFIG_MODVERSIONS) && !defined(MODVERSIONS)
+# define MODVERSIONS
+# if RTLNX_VER_MAX(2,5,71)
+# include <linux/modversions.h>
+# endif
+# endif
+# ifndef KBUILD_STR
+# if RTLNX_VER_MAX(2,6,16)
+# define KBUILD_STR(s) s
+# else
+# define KBUILD_STR(s) #s
+# endif
+# endif
+# ifndef SUPDRV_AGNOSTIC
+# include <linux/string.h>
+# include <linux/spinlock.h>
+# include <linux/slab.h>
+# if RTLNX_VER_MIN(2,6,27)
+# include <linux/semaphore.h>
+# else /* older kernels */
+# include <asm/semaphore.h>
+# endif /* older kernels */
+# include <linux/timer.h>
+# endif
+# if RTLNX_VER_MIN(3,2,0)
+# include <linux/export.h>
+# else
+# include <linux/module.h>
+# if (defined(RT_ARCH_X86) || defined(RT_ARCH_AMD64)) && defined(SUPDRV_AGNOSTIC) /* fix conflicts with iprt/x86.h */
+# undef CS
+# undef DS
+# undef ES
+# undef FS
+# undef GS
+# undef SS
+# undef EFLAGS
+# undef R15
+# undef R14
+# undef R13
+# undef R12
+# undef R11
+# undef R10
+# undef R9
+# undef R8
+# undef RDI
+# undef RSI
+# undef RBP
+# undef RSP
+# undef RBX
+# undef RDX
+# undef RCX
+# undef RAX
+# undef MSR_CORE_PERF_LIMIT_REASONS
+# undef MSR_DRAM_ENERGY_STATUS
+# undef MSR_DRAM_PERF_STATUS
+# undef MSR_DRAM_POWER_INFO
+# undef MSR_DRAM_POWER_LIMIT
+# undef MSR_IA32_APERF
+# undef MSR_IA32_ARCH_CAPABILITIES
+# undef MSR_IA32_CR_PAT
+# undef MSR_IA32_DS_AREA
+# undef MSR_IA32_FEATURE_CONTROL
+# undef MSR_IA32_FLUSH_CMD
+# undef MSR_IA32_MC0_CTL
+# undef MSR_IA32_MC0_STATUS
+# undef MSR_IA32_MCG_CAP
+# undef MSR_IA32_MCG_STATUS
+# undef MSR_IA32_MISC_ENABLE
+# undef MSR_IA32_MISC_ENABLE_BTS_UNAVAIL
+# undef MSR_IA32_MISC_ENABLE_LIMIT_CPUID
+# undef MSR_IA32_MISC_ENABLE_PEBS_UNAVAIL
+# undef MSR_IA32_MISC_ENABLE_TCC
+# undef MSR_IA32_MISC_ENABLE_XD_DISABLE
+# undef MSR_IA32_MPERF
+# undef MSR_IA32_PEBS_ENABLE
+# undef MSR_IA32_PERF_CTL
+# undef MSR_IA32_PERF_STATUS
+# undef MSR_IA32_PLATFORM_ID
+# undef MSR_IA32_PMC0
+# undef MSR_IA32_PRED_CMD
+# undef MSR_IA32_RTIT_CTL
+# undef MSR_IA32_SMBASE
+# undef MSR_IA32_SMM_MONITOR_CTL
+# undef MSR_IA32_SPEC_CTRL
+# undef MSR_IA32_THERM_STATUS
+# undef MSR_IA32_TSC
+# undef MSR_IA32_TSC_ADJUST
+# undef MSR_IA32_TSX_CTRL
+# undef MSR_IA32_VMX_BASIC
+# undef MSR_IA32_VMX_CR0_FIXED0
+# undef MSR_IA32_VMX_CR0_FIXED1
+# undef MSR_IA32_VMX_CR4_FIXED0
+# undef MSR_IA32_VMX_CR4_FIXED1
+# undef MSR_IA32_VMX_ENTRY_CTLS
+# undef MSR_IA32_VMX_EPT_VPID_CAP
+# undef MSR_IA32_VMX_EXIT_CTLS
+# undef MSR_IA32_VMX_MISC
+# undef MSR_IA32_VMX_PINBASED_CTLS
+# undef MSR_IA32_VMX_PROCBASED_CTLS
+# undef MSR_IA32_VMX_PROCBASED_CTLS2
+# undef MSR_IA32_VMX_TRUE_ENTRY_CTLS
+# undef MSR_IA32_VMX_TRUE_EXIT_CTLS
+# undef MSR_IA32_VMX_TRUE_PINBASED_CTLS
+# undef MSR_IA32_VMX_TRUE_PROCBASED_CTLS
+# undef MSR_IA32_VMX_VMCS_ENUM
+# undef MSR_IA32_VMX_VMFUNC
+# undef MSR_K6_PFIR
+# undef MSR_K6_PSOR
+# undef MSR_K6_UWCCR
+# undef MSR_K6_WHCR
+# undef MSR_K7_EVNTSEL0
+# undef MSR_K7_EVNTSEL1
+# undef MSR_K7_EVNTSEL2
+# undef MSR_K7_EVNTSEL3
+# undef MSR_K7_PERFCTR0
+# undef MSR_K7_PERFCTR1
+# undef MSR_K7_PERFCTR2
+# undef MSR_K7_PERFCTR3
+# undef MSR_K8_SYSCFG
+# undef MSR_K8_TOP_MEM1
+# undef MSR_K8_TOP_MEM2
+# undef MSR_OFFCORE_RSP_0
+# undef MSR_OFFCORE_RSP_1
+# undef MSR_PKG_C10_RESIDENCY
+# undef MSR_PKG_C2_RESIDENCY
+# undef MSR_PKG_CST_CONFIG_CONTROL
+# undef MSR_PKG_ENERGY_STATUS
+# undef MSR_PKG_PERF_STATUS
+# undef MSR_PKG_POWER_INFO
+# undef MSR_PKG_POWER_LIMIT
+# undef MSR_PKGC3_IRTL
+# undef MSR_PP0_ENERGY_STATUS
+# undef MSR_PP1_ENERGY_STATUS
+# undef MSR_RAPL_POWER_UNIT
+# undef MSR_TURBO_ACTIVATION_RATIO
+# undef VMX_BASIC_MEM_TYPE_WB
+# undef X86_CR0_AM
+# undef X86_CR0_CD
+# undef X86_CR0_EM
+# undef X86_CR0_ET
+# undef X86_CR0_MP
+# undef X86_CR0_NE
+# undef X86_CR0_NW
+# undef X86_CR0_PE
+# undef X86_CR0_PG
+# undef X86_CR0_TS
+# undef X86_CR0_WP
+# undef X86_CR3_PCD
+# undef X86_CR3_PWT
+# undef X86_CR4_DE
+# undef X86_CR4_FSGSBASE
+# undef X86_CR4_MCE
+# undef X86_CR4_OSFXSR
+# undef X86_CR4_OSXSAVE
+# undef X86_CR4_PAE
+# undef X86_CR4_PCE
+# undef X86_CR4_PCIDE
+# undef X86_CR4_PGE
+# undef X86_CR4_PKE
+# undef X86_CR4_PSE
+# undef X86_CR4_PVI
+# undef X86_CR4_SMAP
+# undef X86_CR4_SMEP
+# undef X86_CR4_SMXE
+# undef X86_CR4_TSD
+# undef X86_CR4_UMIP
+# undef X86_CR4_VME
+# undef X86_CR4_VMXE
+# endif
+# endif
+# define SUPR0_EXPORT_SYMBOL(a_Name) EXPORT_SYMBOL(a_Name)
+
+#elif defined(RT_OS_DARWIN)
+# include <libkern/libkern.h>
+# include <iprt/string.h>
+
+#elif defined(RT_OS_OS2)
+
+#elif defined(RT_OS_FREEBSD)
+# define memset libkern_memset /** @todo these are just hacks to get it compiling, check out later. */
+# define memcmp libkern_memcmp
+# define strchr libkern_strchr
+# define strrchr libkern_strrchr
+# define ffsl libkern_ffsl
+# define fls libkern_fls
+# define flsl libkern_flsl
+# include <sys/libkern.h>
+# undef memset
+# undef memcmp
+# undef strchr
+# undef strrchr
+# undef ffs
+# undef ffsl
+# undef fls
+# undef flsl
+# include <iprt/string.h>
+
+#elif defined(RT_OS_SOLARIS)
+# include <sys/cmn_err.h>
+# include <iprt/string.h>
+
+#else
+# error "unsupported OS."
+#endif
+
+#include "SUPDrvIOC.h"
+#include "SUPDrvIDC.h"
+
+
+
+/*********************************************************************************************************************************
+* Defined Constants And Macros *
+*********************************************************************************************************************************/
+/*
+ * Hardcoded cookies.
+ */
+#define BIRD 0x64726962 /* 'bird' */
+#define BIRD_INV 0x62697264 /* 'drib' */
+
+
+#ifdef RT_OS_WINDOWS
+/** Use a normal mutex for the loader so we remain at the same IRQL after
+ * taking it.
+ * @todo fix the mutex implementation on linux and make this the default. */
+# define SUPDRV_USE_MUTEX_FOR_LDR
+
+/** Use a normal mutex for the GIP so we remain at the same IRQL after
+ * taking it.
+ * @todo fix the mutex implementation on linux and make this the default. */
+# define SUPDRV_USE_MUTEX_FOR_GIP
+#endif
+
+#ifndef SUPR0_EXPORT_SYMBOL
+# define SUPR0_EXPORT_SYMBOL(a_Name) extern int g_supDrvExportSymbolDummyVariable
+#endif
+
+/**
+ * OS debug print macro.
+ */
+#define OSDBGPRINT(a) SUPR0Printf a
+
+
+/** @name Context values for the per-session handle tables.
+ * The context value is used to distinguish between the different kinds of
+ * handles, making the handle table API do all the work.
+ * @{ */
+/** Handle context value for single release event handles. */
+#define SUPDRV_HANDLE_CTX_EVENT ((void *)(uintptr_t)(SUPDRVOBJTYPE_SEM_EVENT))
+/** Handle context value for multiple release event handles. */
+#define SUPDRV_HANDLE_CTX_EVENT_MULTI ((void *)(uintptr_t)(SUPDRVOBJTYPE_SEM_EVENT_MULTI))
+/** @} */
+
+
+/**
+ * Validates a session pointer.
+ *
+ * @returns true/false accordingly.
+ * @param pSession The session.
+ */
+#define SUP_IS_SESSION_VALID(pSession) \
+ ( RT_VALID_PTR(pSession) \
+ && pSession->u32Cookie == BIRD_INV)
+
+/**
+ * Validates a device extension pointer.
+ *
+ * @returns true/false accordingly.
+ * @param pDevExt The device extension.
+ */
+#define SUP_IS_DEVEXT_VALID(pDevExt) \
+ ( RT_VALID_PTR(pDevExt) \
+ && pDevExt->u32Cookie == BIRD)
+
+
+/** @def SUPDRV_WITH_MSR_PROBER
+ * Enables the SUP_IOCTL_MSR_PROBER function.
+ * By default, only enabled in DEBUG builds as it's a sensitive feature.
+ */
+#if defined(DEBUG) && !defined(SUPDRV_WITH_MSR_PROBER) && !defined(SUPDRV_WITHOUT_MSR_PROBER)
+# define SUPDRV_WITH_MSR_PROBER
+#endif
+
+/** @def SUPDRV_WITHOUT_MSR_PROBER
+ * Executive overide for disabling the SUP_IOCTL_MSR_PROBER function.
+ */
+#ifdef SUPDRV_WITHOUT_MSR_PROBER
+# undef SUPDRV_WITH_MSR_PROBER
+#endif
+
+#ifdef DOXYGEN_RUNNING
+# define SUPDRV_WITH_MSR_PROBER
+# define SUPDRV_WITHOUT_MSR_PROBER
+#endif
+
+#if 1
+/** @def SUPDRV_USE_TSC_DELTA_THREAD
+ * Use a dedicated kernel thread to service TSC-delta measurement requests.
+ * @todo Test on servers with many CPUs and sockets. */
+# define SUPDRV_USE_TSC_DELTA_THREAD
+#endif
+
+
+/*********************************************************************************************************************************
+* Structures and Typedefs *
+*********************************************************************************************************************************/
+/** Pointer to the device extension. */
+typedef struct SUPDRVDEVEXT *PSUPDRVDEVEXT;
+
+#ifdef SUPDRV_USE_TSC_DELTA_THREAD
+/**
+ * TSC-delta measurement thread state machine.
+ */
+typedef enum SUPDRVTSCDELTATHREADSTATE
+{
+ /** Uninitialized/invalid value. */
+ kTscDeltaThreadState_Invalid = 0,
+ /** The thread is being created.
+ * Next state: Listening, Butchered, Terminating */
+ kTscDeltaThreadState_Creating,
+ /** The thread is listening for events.
+ * Previous state: Creating, Measuring
+ * Next state: WaitAndMeasure, Butchered, Terminated */
+ kTscDeltaThreadState_Listening,
+ /** The thread is sleeping before starting a measurement.
+ * Previous state: Listening, Measuring
+ * Next state: Measuring, Butchered, Terminating
+ * @remarks The thread won't enter this state on its own, it is put into this
+ * state by the GIP timer, the CPU online callback and by the
+ * SUP_IOCTL_TSC_DELTA_MEASURE code. */
+ kTscDeltaThreadState_WaitAndMeasure,
+ /** The thread is currently servicing a measurement request.
+ * Previous state: WaitAndMeasure
+ * Next state: Listening, WaitAndMeasure, Terminate */
+ kTscDeltaThreadState_Measuring,
+ /** The thread is terminating.
+ * @remarks The thread won't enter this state on its own, is put into this state
+ * by supdrvTscDeltaTerm. */
+ kTscDeltaThreadState_Terminating,
+ /** The thread is butchered due to an unexpected error.
+ * Previous State: Creating, Listening, WaitAndMeasure */
+ kTscDeltaThreadState_Butchered,
+ /** The thread is destroyed (final).
+ * Previous state: Terminating */
+ kTscDeltaThreadState_Destroyed,
+ /** The usual 32-bit blowup hack. */
+ kTscDeltaThreadState_32BitHack = 0x7fffffff
+} SUPDRVTSCDELTATHREADSTATE;
+#endif /* SUPDRV_USE_TSC_DELTA_THREAD */
+
+/**
+ * Memory reference types.
+ */
+typedef enum
+{
+ /** Unused entry */
+ MEMREF_TYPE_UNUSED = 0,
+ /** Locked memory (r3 mapping only). */
+ MEMREF_TYPE_LOCKED,
+ /** Continuous memory block (r3 and r0 mapping). */
+ MEMREF_TYPE_CONT,
+ /** Low memory block (r3 and r0 mapping). */
+ MEMREF_TYPE_LOW,
+ /** Memory block (r3 and r0 mapping). */
+ MEMREF_TYPE_MEM,
+ /** Locked memory (r3 mapping only) allocated by the support driver. */
+ MEMREF_TYPE_PAGE,
+ /** Blow the type up to 32-bit and mark the end. */
+ MEMREF_TYPE_32BIT_HACK = 0x7fffffff
+} SUPDRVMEMREFTYPE, *PSUPDRVMEMREFTYPE;
+
+
+/**
+ * Structure used for tracking memory a session
+ * references in one way or another.
+ */
+typedef struct SUPDRVMEMREF
+{
+ /** The memory object handle. */
+ RTR0MEMOBJ MemObj;
+ /** The ring-3 mapping memory object handle. */
+ RTR0MEMOBJ MapObjR3;
+ /** Type of memory. */
+ SUPDRVMEMREFTYPE eType;
+} SUPDRVMEMREF, *PSUPDRVMEMREF;
+
+
+/**
+ * Bundle of locked memory ranges.
+ */
+typedef struct SUPDRVBUNDLE
+{
+ /** Pointer to the next bundle. */
+ struct SUPDRVBUNDLE * volatile pNext;
+ /** Referenced memory. */
+ SUPDRVMEMREF aMem[64];
+ /** Number of entries used. */
+ uint32_t volatile cUsed;
+} SUPDRVBUNDLE, *PSUPDRVBUNDLE;
+
+
+/**
+ * Loaded image.
+ */
+typedef struct SUPDRVLDRIMAGE
+{
+ /** Next in chain. */
+ struct SUPDRVLDRIMAGE * volatile pNext;
+ /** Pointer to the image. */
+ void *pvImage;
+ /** The memory object for the module allocation. */
+ RTR0MEMOBJ hMemObjImage;
+ /** Magic value (SUPDRVLDRIMAGE_MAGIC). */
+ uint32_t uMagic;
+ /** Size of the image including the tables. This is mainly for verification
+ * of the load request. */
+ uint32_t cbImageWithEverything;
+ /** Size of the image. */
+ uint32_t cbImageBits;
+ /** The number of entries in the symbol table. */
+ uint32_t cSymbols;
+ /** Pointer to the symbol table. */
+ PSUPLDRSYM paSymbols;
+ /** The offset of the string table. */
+ char *pachStrTab;
+ /** Size of the string table. */
+ uint32_t cbStrTab;
+ /** Number of segments. */
+ uint32_t cSegments;
+ /** Segments (for memory protection). */
+ PSUPLDRSEG paSegments;
+ /** Pointer to the optional module initialization callback. */
+ PFNR0MODULEINIT pfnModuleInit;
+ /** Pointer to the optional module termination callback. */
+ PFNR0MODULETERM pfnModuleTerm;
+ /** Service request handler. This is NULL for non-service modules. */
+ PFNSUPR0SERVICEREQHANDLER pfnServiceReqHandler;
+ /** The ldr image state. (IOCtl code of last operation.) */
+ uint32_t uState;
+ /** Usage count. */
+ uint32_t volatile cImgUsage;
+ /** Pointer to the device extension. */
+ struct SUPDRVDEVEXT *pDevExt;
+ /** Image (VMMR0.r0) containing functions/data that this one uses. */
+ struct SUPDRVLDRIMAGE *pImageImport;
+#ifdef RT_OS_WINDOWS
+ /** The section object for the loaded image (fNative=true). */
+ void *pvNtSectionObj;
+ /** Lock object. */
+ RTR0MEMOBJ hMemLock;
+#endif
+#if defined(RT_OS_SOLARIS) && defined(VBOX_WITH_NATIVE_SOLARIS_LOADING)
+ /** The Solaris module ID. */
+ int idSolMod;
+ /** Pointer to the module control structure. */
+ struct modctl *pSolModCtl;
+#endif
+#ifdef RT_OS_LINUX
+ /** Hack for seeing the module in perf, dtrace and other stack crawlers. */
+ struct module *pLnxModHack;
+ /** The wrapper module. */
+ struct module *pLnxWrapperModule;
+ /** Set if we're holding a reference to the wrapper module. */
+ bool fLnxWrapperRef;
+#endif
+#if defined(RT_OS_DARWIN) && defined(VBOX_WITH_DARWIN_R0_DARWIN_IMAGE_VERIFICATION)
+ /** Load module handle. */
+ RTLDRMOD hLdrMod;
+ /** Allocate object. */
+ RTR0MEMOBJ hMemAlloc;
+#endif
+ /** This points to the module info if the image is a wrapped up in a native one. */
+ PCSUPLDRWRAPPEDMODULE pWrappedModInfo;
+ /** OS specific information for wrapped modules. */
+ void *pvWrappedNative;
+ /** Whether it's loaded by the native loader or not. */
+ bool fNative;
+ /** Image name. */
+ char szName[32];
+} SUPDRVLDRIMAGE, *PSUPDRVLDRIMAGE;
+
+/** Magic value for SUPDRVLDRIMAGE::uMagic (Charlotte Bronte). */
+#define SUPDRVLDRIMAGE_MAGIC UINT32_C(0x18160421)
+/** Magic value for SUPDRVLDRIMAGE::uMagic when freed. */
+#define SUPDRVLDRIMAGE_MAGIC_DEAD UINT32_C(0x18550331)
+
+
+/** Image usage record. */
+typedef struct SUPDRVLDRUSAGE
+{
+ /** Next in chain. */
+ struct SUPDRVLDRUSAGE * volatile pNext;
+ /** The image. */
+ PSUPDRVLDRIMAGE pImage;
+ /** Load count (ring-3). */
+ uint32_t volatile cRing3Usage;
+ /** Ring-0 usage counter. */
+ uint32_t volatile cRing0Usage;
+} SUPDRVLDRUSAGE, *PSUPDRVLDRUSAGE;
+
+
+/**
+ * Component factory registration record.
+ */
+typedef struct SUPDRVFACTORYREG
+{
+ /** Pointer to the next registration. */
+ struct SUPDRVFACTORYREG *pNext;
+ /** Pointer to the registered factory. */
+ PCSUPDRVFACTORY pFactory;
+ /** The session owning the factory.
+ * Used for deregistration and session cleanup. */
+ PSUPDRVSESSION pSession;
+ /** Length of the name. */
+ size_t cchName;
+} SUPDRVFACTORYREG;
+/** Pointer to a component factory registration record. */
+typedef SUPDRVFACTORYREG *PSUPDRVFACTORYREG;
+/** Pointer to a const component factory registration record. */
+typedef SUPDRVFACTORYREG const *PCSUPDRVFACTORYREG;
+
+
+/**
+ * Registered object.
+ * This takes care of reference counting and tracking data for access checks.
+ */
+typedef struct SUPDRVOBJ
+{
+ /** Magic value (SUPDRVOBJ_MAGIC). */
+ uint32_t u32Magic;
+ /** The object type. */
+ SUPDRVOBJTYPE enmType;
+ /** Pointer to the next in the global list. */
+ struct SUPDRVOBJ * volatile pNext;
+ /** Pointer to the object destructor.
+ * This may be set to NULL if the image containing the destructor get unloaded. */
+ PFNSUPDRVDESTRUCTOR pfnDestructor;
+ /** User argument 1. */
+ void *pvUser1;
+ /** User argument 2. */
+ void *pvUser2;
+ /** The total sum of all per-session usage. */
+ uint32_t volatile cUsage;
+ /** The creator user id. */
+ RTUID CreatorUid;
+ /** The creator group id. */
+ RTGID CreatorGid;
+ /** The creator process id. */
+ RTPROCESS CreatorProcess;
+} SUPDRVOBJ, *PSUPDRVOBJ;
+
+/** Magic number for SUPDRVOBJ::u32Magic. (Dame Agatha Mary Clarissa Christie). */
+#define SUPDRVOBJ_MAGIC UINT32_C(0x18900915)
+/** Dead number magic for SUPDRVOBJ::u32Magic. */
+#define SUPDRVOBJ_MAGIC_DEAD UINT32_C(0x19760112)
+
+/**
+ * The per-session object usage record.
+ */
+typedef struct SUPDRVUSAGE
+{
+ /** Pointer to the next in the list. */
+ struct SUPDRVUSAGE * volatile pNext;
+ /** Pointer to the object we're recording usage for. */
+ PSUPDRVOBJ pObj;
+ /** The usage count. */
+ uint32_t volatile cUsage;
+} SUPDRVUSAGE, *PSUPDRVUSAGE;
+
+
+/**
+ * I/O control context.
+ */
+typedef struct SUPR0IOCTLCTX
+{
+ /** Magic value (SUPR0IOCTLCTX_MAGIC). */
+ uint32_t u32Magic;
+ /** Reference counter. */
+ uint32_t volatile cRefs;
+#ifdef RT_OS_WINDOWS
+# ifndef SUPDRV_AGNOSTIC
+ /** The file object, referenced. */
+ PFILE_OBJECT pFileObject;
+ /** The device object, not referenced. */
+ PDEVICE_OBJECT pDeviceObject;
+ /** Pointer to fast I/O routine if available. */
+ FAST_IO_DEVICE_CONTROL *pfnFastIoDeviceControl;
+# else
+ void *apvPadding[3];
+# endif
+#endif
+} SUPR0IOCTLCTX;
+/** Magic value for SUPR0IOCTLCTX (Ahmad Jamal). */
+#define SUPR0IOCTLCTX_MAGIC UINT32_C(0x19300702)
+
+
+/**
+ * Per session data.
+ * This is mainly for memory tracking.
+ */
+typedef struct SUPDRVSESSION
+{
+ /** Pointer to the device extension. */
+ PSUPDRVDEVEXT pDevExt;
+ /** Session Cookie. */
+ uint32_t u32Cookie;
+ /** Set if is an unrestricted session, clear if restricted. */
+ bool fUnrestricted;
+
+ /** Set if we're in the hash table, clear if not. Protected by the hash
+ * table spinlock. */
+ bool fInHashTable;
+ /** Reference counter. */
+ uint32_t volatile cRefs;
+ /** Pointer to the next session with the same hash (common hash table).
+ * Protected by the hash table spinlock. */
+ PSUPDRVSESSION pCommonNextHash;
+ /** Pointer to the OS specific session pointer, if available and in use.
+ * This is atomically set and cleared as the session is inserted and removed
+ * from the hash table (protected by the session hash table spinlock). */
+ PSUPDRVSESSION *ppOsSessionPtr;
+ /** The process (id) of the session. */
+ RTPROCESS Process;
+ /** Which process this session is associated with.
+ * This is NIL_RTR0PROCESS for kernel sessions and valid for user ones. */
+ RTR0PROCESS R0Process;
+
+ /** The GVM associated with the session.
+ * This is set by VMMR0. */
+ PGVM pSessionGVM;
+ /** The VM associated with the session.
+ * This is set by VMMR0. */
+ PVM pSessionVM;
+ /** Set to pSessionVM if fast I/O controlls are enabled. */
+ PVM pFastIoCtrlVM;
+ /** Handle table for IPRT semaphore wrapper APIs.
+ * This takes care of its own locking in an IRQ safe manner. */
+ RTHANDLETABLE hHandleTable;
+ /** Load usage records (LIFO!). (protected by SUPDRVDEVEXT::mtxLdr) */
+ PSUPDRVLDRUSAGE volatile pLdrUsage;
+
+ /** Spinlock protecting the bundles, the GIP members and the
+ * fProcessCleanupDone flag. It continues to be valid until the last
+ * reference to the session is released. */
+ RTSPINLOCK Spinlock;
+ /** The ring-3 mapping of the GIP (readonly). */
+ RTR0MEMOBJ GipMapObjR3;
+ /** Set if the session is using the GIP. */
+ uint32_t fGipReferenced;
+ /** Bundle of locked memory objects. */
+ SUPDRVBUNDLE Bundle;
+ /** List of generic usage records. (protected by SUPDRVDEVEXT::SpinLock) */
+ PSUPDRVUSAGE volatile pUsage;
+
+ /** The user id of the session - set by the OS part or NIL_RTUID.
+ * This should be unique accross namespace/zones/whatever. */
+ RTUID Uid;
+ /** The group id of the session - set by the OS part or NIL_RTGID.
+ * This should be unique accross namespace/zones/whatever. */
+ RTGID Gid;
+ /** Per session tracer specfic data. */
+ uintptr_t uTracerData;
+ /** The thread currently actively talking to the tracer. (One at the time!) */
+ RTNATIVETHREAD hTracerCaller;
+ /** List of tracepoint providers associated with the session
+ * (SUPDRVTPPROVIDER). */
+ RTLISTANCHOR TpProviders;
+ /** The number of providers in TpProviders. */
+ uint32_t cTpProviders;
+ /** The number of threads active in supdrvIOCtl_TracerUmodProbeFire or
+ * SUPR0TracerUmodProbeFire. */
+ uint32_t volatile cTpProbesFiring;
+ /** User tracepoint modules (PSUPDRVTRACKERUMOD). */
+ RTLISTANCHOR TpUmods;
+ /** The user tracepoint module lookup table. */
+ struct SUPDRVTRACERUMOD *apTpLookupTable[32];
+ /** Whether this is a GIP test-mode client session or not. */
+ bool fGipTestMode;
+#ifndef SUPDRV_AGNOSTIC
+# if defined(RT_OS_DARWIN)
+ /** Pointer to the associated org_virtualbox_SupDrvClient object. */
+ void *pvSupDrvClient;
+ /** Whether this session has been opened or not. */
+ bool fOpened;
+# endif
+# if defined(RT_OS_OS2)
+ /** The system file number of this session. */
+ uint16_t sfn;
+ uint16_t Alignment; /**< Alignment */
+# endif
+# if defined(RT_OS_DARWIN) || defined(RT_OS_OS2) || defined(RT_OS_SOLARIS)
+ /** Pointer to the next session with the same hash. */
+ PSUPDRVSESSION pNextHash;
+# endif
+# if defined(RT_OS_WINDOWS) && defined(VBOX_WITH_HARDENING)
+ /** Pointer to the process protection structure for this session. */
+ struct SUPDRVNTPROTECT *pNtProtect;
+# endif
+# if defined(RT_OS_WINDOWS)
+ /** Reference to the user ID structure corresponding to the Uid member. */
+ struct SUPDRVNTUSERID *pNtUserId;
+# endif
+#endif /* !SUPDRV_AGNOSTIC */
+} SUPDRVSESSION;
+
+
+/**
+ * Device extension.
+ */
+typedef struct SUPDRVDEVEXT
+{
+ /** Global cookie. */
+ uint32_t u32Cookie;
+ /** The actual size of SUPDRVSESSION. (SUPDRV_AGNOSTIC) */
+ uint32_t cbSession;
+
+ /** Spinlock to serialize the initialization, usage counting and objects.
+ * This is IRQ safe because we want to be able signal semaphores from the
+ * special HM context (and later maybe interrupt handlers), so we must be able
+ * to reference and dereference handles when IRQs are disabled. */
+ RTSPINLOCK Spinlock;
+
+ /** List of registered objects. Protected by the spinlock. */
+ PSUPDRVOBJ volatile pObjs;
+ /** List of free object usage records. */
+ PSUPDRVUSAGE volatile pUsageFree;
+
+ /** Loader mutex.
+ * This protects pvVMMR0, pvVMMR0Entry, pImages and SUPDRVSESSION::pLdrUsage. */
+#ifdef SUPDRV_USE_MUTEX_FOR_LDR
+ RTSEMMUTEX mtxLdr;
+#else
+ RTSEMFASTMUTEX mtxLdr;
+#endif
+
+ /** VMM Module 'handle'.
+ * 0 if the code VMM isn't loaded and Idt are nops. */
+ void * volatile pvVMMR0;
+ /** VMMR0EntryFast() pointer. */
+ DECLR0CALLBACKMEMBER(void, pfnVMMR0EntryFast, (PGVM pGVM, PVM pVM, VMCPUID idCpu, uint32_t uOperation));
+ /** VMMR0EntryEx() pointer. */
+ DECLR0CALLBACKMEMBER(int, pfnVMMR0EntryEx, (PGVM pGVM, PVM pVM, VMCPUID idCpu, uint32_t uOperation,
+ PSUPVMMR0REQHDR pReq, uint64_t u64Arg, PSUPDRVSESSION pSession));
+
+ /** Linked list of loaded code. */
+ PSUPDRVLDRIMAGE volatile pLdrImages;
+ /** Set if the image loading interface got disabled after loading all needed images */
+ bool fLdrLockedDown;
+
+ /** @name These members for detecting whether an API caller is in ModuleInit.
+ * Certain APIs are only permitted from ModuleInit, like for instance tracepoint
+ * registration.
+ * @{ */
+ /** The image currently executing its ModuleInit. */
+ PSUPDRVLDRIMAGE volatile pLdrInitImage;
+ /** The thread currently executing a ModuleInit function. */
+ RTNATIVETHREAD volatile hLdrInitThread;
+ /** The thread currently executing a ModuleTerm function. */
+ RTNATIVETHREAD volatile hLdrTermThread;
+ /** @} */
+
+ /** Number of times someone reported bad execution context via SUPR0BadContext.
+ * (This is times EFLAGS.AC is zero when we expected it to be 1.) */
+ uint32_t volatile cBadContextCalls;
+
+ /** GIP mutex.
+ * Any changes to any of the GIP members requires ownership of this mutex,
+ * except on driver init and termination. */
+#ifdef SUPDRV_USE_MUTEX_FOR_GIP
+ RTSEMMUTEX mtxGip;
+#else
+ RTSEMFASTMUTEX mtxGip;
+#endif
+ /** GIP spinlock protecting GIP members during Mp events.
+ * This is IRQ safe since be may get MP callbacks in contexts where IRQs are
+ * disabled (on some platforms). */
+ RTSPINLOCK hGipSpinlock;
+ /** Pointer to the Global Info Page (GIP). */
+ PSUPGLOBALINFOPAGE pGip;
+ /** The physical address of the GIP. */
+ RTHCPHYS HCPhysGip;
+ /** Number of processes using the GIP.
+ * (The updates are suspend while cGipUsers is 0.)*/
+ uint32_t volatile cGipUsers;
+ /** The ring-0 memory object handle for the GIP page. */
+ RTR0MEMOBJ GipMemObj;
+ /** The GIP timer handle. */
+ PRTTIMER pGipTimer;
+ /** If non-zero we've successfully called RTTimerRequestSystemGranularity(). */
+ uint32_t u32SystemTimerGranularityGrant;
+ /** The CPU id of the GIP master.
+ * This CPU is responsible for the updating the common GIP data and it is
+ * the one used to calculate TSC deltas relative to.
+ * (The initial master will have a 0 zero value, but it it goes offline the
+ * new master may have a non-zero value.) */
+ RTCPUID volatile idGipMaster;
+
+ /** Component factory mutex.
+ * This protects pComponentFactoryHead and component factory querying. */
+ RTSEMFASTMUTEX mtxComponentFactory;
+ /** The head of the list of registered component factories. */
+ PSUPDRVFACTORYREG pComponentFactoryHead;
+
+ /** Lock protecting The tracer members. */
+ RTSEMFASTMUTEX mtxTracer;
+ /** List of tracer providers (SUPDRVTPPROVIDER). */
+ RTLISTANCHOR TracerProviderList;
+ /** List of zombie tracer providers (SUPDRVTPPROVIDER). */
+ RTLISTANCHOR TracerProviderZombieList;
+ /** Pointer to the tracer registration record. */
+ PCSUPDRVTRACERREG pTracerOps;
+ /** The ring-0 session of a native tracer provider. */
+ PSUPDRVSESSION pTracerSession;
+ /** The image containing the tracer. */
+ PSUPDRVLDRIMAGE pTracerImage;
+ /** The tracer helpers. */
+ SUPDRVTRACERHLP TracerHlp;
+ /** The number of session having opened the tracer currently. */
+ uint32_t cTracerOpens;
+ /** The number of threads currently calling into the tracer. */
+ uint32_t volatile cTracerCallers;
+ /** Set if the tracer is being unloaded. */
+ bool fTracerUnloading;
+ /** Hash table for user tracer modules (SUPDRVVTGCOPY). */
+ RTLISTANCHOR aTrackerUmodHash[128];
+
+ /** @name Session Handle Table.
+ * @{ */
+ /** Spinlock protecting apSessionHashTab, cSessions,
+ * SUPDRVSESSION::ppOsSessionPtr, SUPDRVSESSION::pCommonNextHash, and possibly
+ * others depending on the OS. */
+ RTSPINLOCK hSessionHashTabSpinlock;
+ /** Session hash table hash table. The size of this table must make sense in
+ * comparison to GVMM_MAX_HANDLES. */
+ PSUPDRVSESSION apSessionHashTab[HC_ARCH_BITS == 64 ? 8191 : 127];
+ /** The number of open sessions. */
+ int32_t cSessions;
+ /** @} */
+
+ /** @name Invariant TSC frequency refinement.
+ * @{ */
+ /** Nanosecond timestamp at the start of the TSC frequency refinement phase. */
+ uint64_t nsStartInvarTscRefine;
+ /** TSC reading at the start of the TSC frequency refinement phase. */
+ uint64_t uTscStartInvarTscRefine;
+ /** The CPU id of the CPU that u64TscAnchor was measured on. */
+ RTCPUID idCpuInvarTscRefine;
+ /** Pointer to the timer used to refine the TSC frequency. */
+ PRTTIMER pInvarTscRefineTimer;
+ /** Stop the timer on the next tick because we saw a power event. */
+ bool volatile fInvTscRefinePowerEvent;
+ /** @} */
+
+ /** @name TSC-delta measurement.
+ * @{ */
+ /** Number of online/offline events, incremented each time a CPU goes online
+ * or offline. */
+ uint32_t volatile cMpOnOffEvents;
+ /** TSC-delta measurement mutext.
+ * At the moment, we don't want to have more than one measurement going on at
+ * any one time. We might be using broadcast IPIs which are heavy and could
+ * perhaps get in each others way. */
+#ifdef SUPDRV_USE_MUTEX_FOR_GIP
+ RTSEMMUTEX mtxTscDelta;
+#else
+ RTSEMFASTMUTEX mtxTscDelta;
+#endif
+ /** The set of CPUs we need to take measurements for. */
+ RTCPUSET TscDeltaCpuSet;
+ /** The set of CPUs we have completed taken measurements for. */
+ RTCPUSET TscDeltaObtainedCpuSet;
+ /** @} */
+
+#ifdef SUPDRV_USE_TSC_DELTA_THREAD
+ /** @name TSC-delta measurement thread.
+ * @{ */
+ /** Spinlock protecting enmTscDeltaThreadState. */
+ RTSPINLOCK hTscDeltaSpinlock;
+ /** TSC-delta measurement thread. */
+ RTTHREAD hTscDeltaThread;
+ /** The event signalled during state changes to the TSC-delta thread. */
+ RTSEMEVENT hTscDeltaEvent;
+ /** The state of the TSC-delta measurement thread. */
+ SUPDRVTSCDELTATHREADSTATE enmTscDeltaThreadState;
+ /** Thread timeout time before rechecking state in ms. */
+ RTMSINTERVAL cMsTscDeltaTimeout;
+ /** Whether the TSC-delta measurement was successful. */
+ int32_t volatile rcTscDelta;
+ /** Tell the thread we want TSC-deltas for all CPUs with retries. */
+ bool fTscThreadRecomputeAllDeltas;
+ /** @} */
+#endif
+
+ /** @name GIP test mode.
+ * @{ */
+ /** Reference counter for GIP test-mode sessions. */
+ uint32_t cGipTestModeRefs;
+ /** Cache of TSC frequency before enabling test-mode on invariant GIP systems. */
+ uint64_t uGipTestModeInvariantCpuHz;
+ /** @} */
+
+ /*
+ * Note! The non-agnostic bits must be at the very end of the structure!
+ */
+#ifndef SUPDRV_AGNOSTIC
+# ifdef RT_OS_WINDOWS
+ /** Callback object returned by ExCreateCallback. */
+ PCALLBACK_OBJECT pObjPowerCallback;
+ /** Callback handle returned by ExRegisterCallback. */
+ PVOID hPowerCallback;
+# elif defined(RT_OS_DARWIN) && defined(VBOX_WITH_DARWIN_R0_DARWIN_IMAGE_VERIFICATION)
+ /** Trusted root certificates for code signing validation. */
+ RTCRSTORE hRootStore;
+ /** Intermedite certificates for code signing validation. */
+ RTCRSTORE hAdditionalStore;
+# endif
+#endif
+} SUPDRVDEVEXT;
+
+/** Calculates the index into g_apSessionHashTab.*/
+#define SUPDRV_SESSION_HASH(a_pid) ( (a_pid) % RT_ELEMENTS(((SUPDRVDEVEXT *)NULL)->apSessionHashTab) )
+
+
+RT_C_DECLS_BEGIN
+
+/*******************************************************************************
+* OS Specific Functions *
+*******************************************************************************/
+/**
+ * Called to clean up the session structure before it's freed.
+ *
+ * @param pDevExt The device globals.
+ * @param pSession The session that's being cleaned up.
+ */
+void VBOXCALL supdrvOSCleanupSession(PSUPDRVDEVEXT pDevExt, PSUPDRVSESSION pSession);
+
+/**
+ * Called to let the OS specfic code perform additional insertion work while
+ * still under the protection of the hash table spinlock.
+ *
+ * @param pDevExt The device globals.
+ * @param pSession The session that was inserted.
+ * @param pvUser User context specified to the insert call.
+ */
+void VBOXCALL supdrvOSSessionHashTabInserted(PSUPDRVDEVEXT pDevExt, PSUPDRVSESSION pSession, void *pvUser);
+
+/**
+ * Called to let the OS specfic code perform additional removal work while still
+ * under the protection of the hash table spinlock.
+ *
+ * @param pDevExt The device globals.
+ * @param pSession The session that was removed.
+ * @param pvUser User context specified to the remove call.
+ */
+void VBOXCALL supdrvOSSessionHashTabRemoved(PSUPDRVDEVEXT pDevExt, PSUPDRVSESSION pSession, void *pvUser);
+
+/**
+ * Called during GIP initializtion to calc the CPU group table size.
+ *
+ * This is currently only implemented on windows [lazy bird].
+ *
+ * @returns Number of bytes needed for SUPGIPCPUGROUP structures.
+ * @param pDevExt The device globals.
+ */
+size_t VBOXCALL supdrvOSGipGetGroupTableSize(PSUPDRVDEVEXT pDevExt);
+
+/**
+ * Called during GIP initialization to set up the group table and group count.
+ *
+ * This is currently only implemented on windows [lazy bird].
+ *
+ * @param pDevExt The device globals.
+ * @param pGip The GIP which group table needs initialization.
+ * It's only partially initialized at this point.
+ * @param cbGipCpuGroups What supdrvOSGipGetGroupTableSize returned.
+ */
+int VBOXCALL supdrvOSInitGipGroupTable(PSUPDRVDEVEXT pDevExt, PSUPGLOBALINFOPAGE pGip, size_t cbGipCpuGroups);
+
+/**
+ * Initializes the group related members when a CPU is added to the GIP.
+ *
+ * This is called both during GIP initalization and during an CPU online event.
+ *
+ * This is currently only implemented on windows [lazy bird].
+ *
+ * @param pDevExt The device globals.
+ * @param pGip The GIP.
+ * @param pGipCpu The GIP CPU structure being initialized.
+ */
+void VBOXCALL supdrvOSGipInitGroupBitsForCpu(PSUPDRVDEVEXT pDevExt, PSUPGLOBALINFOPAGE pGip, PSUPGIPCPU pGipCpu);
+
+void VBOXCALL supdrvOSObjInitCreator(PSUPDRVOBJ pObj, PSUPDRVSESSION pSession);
+bool VBOXCALL supdrvOSObjCanAccess(PSUPDRVOBJ pObj, PSUPDRVSESSION pSession, const char *pszObjName, int *prc);
+bool VBOXCALL supdrvOSGetForcedAsyncTscMode(PSUPDRVDEVEXT pDevExt);
+bool VBOXCALL supdrvOSAreCpusOfflinedOnSuspend(void);
+bool VBOXCALL supdrvOSAreTscDeltasInSync(void);
+int VBOXCALL supdrvOSEnableVTx(bool fEnabled);
+RTCCUINTREG VBOXCALL supdrvOSChangeCR4(RTCCUINTREG fOrMask, RTCCUINTREG fAndMask);
+bool VBOXCALL supdrvOSSuspendVTxOnCpu(void);
+void VBOXCALL supdrvOSResumeVTxOnCpu(bool fSuspended);
+int VBOXCALL supdrvOSGetCurrentGdtRw(RTHCUINTPTR *pGdtRw);
+
+/**
+ * Try open the image using the native loader.
+ *
+ * @returns IPRT status code.
+ * @retval VERR_NOT_SUPPORTED if native loading isn't supported.
+ *
+ * @param pDevExt The device globals.
+ * @param pImage The image handle. pvImage should be set on
+ * success, pvImageAlloc can also be set if
+ * appropriate.
+ * @param pszFilename The file name - UTF-8, may containing UNIX
+ * slashes on non-UNIX systems.
+ */
+int VBOXCALL supdrvOSLdrOpen(PSUPDRVDEVEXT pDevExt, PSUPDRVLDRIMAGE pImage, const char *pszFilename);
+
+/**
+ * Notification call indicating that a image is being opened for the first time.
+ *
+ * Called for both native and non-native images (after supdrvOSLdrOpen). Can be
+ * used to log the load address of the image or inform the kernel about the
+ * alien image.
+ *
+ * @param pDevExt The device globals.
+ * @param pImage The image handle.
+ * @param pszFilename The file name - UTF-8, may containing UNIX
+ * slashes on non-UNIX systems.
+ */
+void VBOXCALL supdrvOSLdrNotifyOpened(PSUPDRVDEVEXT pDevExt, PSUPDRVLDRIMAGE pImage, const char *pszFilename);
+
+/**
+ * Validates an entry point address.
+ *
+ * Called before supdrvOSLdrLoad.
+ *
+ * @returns IPRT status code.
+ * @param pDevExt The device globals.
+ * @param pImage The image data (still in the open state).
+ * @param pv The address within the image.
+ * @param pbImageBits The image bits as loaded by ring-3.
+ * @param pszSymbol The name of the entrypoint being checked.
+ */
+int VBOXCALL supdrvOSLdrValidatePointer(PSUPDRVDEVEXT pDevExt, PSUPDRVLDRIMAGE pImage,
+ void *pv, const uint8_t *pbImageBits, const char *pszSymbol);
+
+/**
+ * Load the image.
+ *
+ * @returns IPRT status code.
+ * @param pDevExt The device globals.
+ * @param pImage The image data (up to date). Adjust entrypoints
+ * and exports if necessary.
+ * @param pbImageBits The image bits as loaded by ring-3.
+ * @param pReq Pointer to the request packet so that the VMMR0
+ * entry points can be adjusted.
+ */
+int VBOXCALL supdrvOSLdrLoad(PSUPDRVDEVEXT pDevExt, PSUPDRVLDRIMAGE pImage, const uint8_t *pbImageBits, PSUPLDRLOAD pReq);
+
+/**
+ * Unload the image (only called if supdrvOSLdrOpen returned success).
+ *
+ * @param pDevExt The device globals.
+ * @param pImage The image data (mostly still valid).
+ */
+void VBOXCALL supdrvOSLdrUnload(PSUPDRVDEVEXT pDevExt, PSUPDRVLDRIMAGE pImage);
+
+/**
+ * Notification call indicating that a image is being unloaded.
+ *
+ * Called for both native and non-native images. In the former case, it's
+ * called after supdrvOSLdrUnload.
+ *
+ * @param pDevExt The device globals.
+ * @param pImage The image handle.
+ */
+void VBOXCALL supdrvOSLdrNotifyUnloaded(PSUPDRVDEVEXT pDevExt, PSUPDRVLDRIMAGE pImage);
+
+/**
+ * Queries a symbol address is a native module.
+ *
+ * @returns IPRT status code.
+ * @param pDevExt The device globals.
+ * @param pImage The image to search.
+ * @param pszSymbol The symbol to search for.
+ * @param cchSymbol The length of the symbol.
+ * @param ppvSymbol Where to return the symbol address if found.
+ */
+int VBOXCALL supdrvOSLdrQuerySymbol(PSUPDRVDEVEXT pDevExt, PSUPDRVLDRIMAGE pImage,
+ const char *pszSymbol, size_t cchSymbol, void **ppvSymbol);
+
+/**
+ * Retains a native wrapper module when it is first being used.
+ *
+ * This will be call when pImage->cImgUsage is incremented to 2.
+ *
+ * @param pDevExt The device globals.
+ * @param pImage The wrapped image.
+ */
+void VBOXCALL supdrvOSLdrRetainWrapperModule(PSUPDRVDEVEXT pDevExt, PSUPDRVLDRIMAGE pImage);
+
+/**
+ * Release a native wrapper module when it is no longer being used.
+ *
+ * This will be call when pImage->cImgUsage is decremented to 1.
+ *
+ * @param pDevExt The device globals.
+ * @param pImage The wrapped image.
+ */
+void VBOXCALL supdrvOSLdrReleaseWrapperModule(PSUPDRVDEVEXT pDevExt, PSUPDRVLDRIMAGE pImage);
+
+#ifdef SUPDRV_WITH_MSR_PROBER
+
+/**
+ * Tries to read an MSR.
+ *
+ * @returns One of the listed VBox status codes.
+ * @retval VINF_SUCCESS if read successfully, value in *puValue.
+ * @retval VERR_ACCESS_DENIED if we couldn't read it (GP).
+ * @retval VERR_NOT_SUPPORTED if not supported.
+ *
+ * @param uMsr The MSR to read from.
+ * @param idCpu The CPU to read the MSR on. NIL_RTCPUID
+ * indicates any suitable CPU.
+ * @param puValue Where to return the value.
+ */
+int VBOXCALL supdrvOSMsrProberRead(uint32_t uMsr, RTCPUID idCpu, uint64_t *puValue);
+
+/**
+ * Tries to write an MSR.
+ *
+ * @returns One of the listed VBox status codes.
+ * @retval VINF_SUCCESS if written successfully.
+ * @retval VERR_ACCESS_DENIED if we couldn't write the value to it (GP).
+ * @retval VERR_NOT_SUPPORTED if not supported.
+ *
+ * @param uMsr The MSR to write to.
+ * @param idCpu The CPU to write the MSR on. NIL_RTCPUID
+ * indicates any suitable CPU.
+ * @param uValue The value to write.
+ */
+int VBOXCALL supdrvOSMsrProberWrite(uint32_t uMsr, RTCPUID idCpu, uint64_t uValue);
+
+/**
+ * Tries to modify an MSR value.
+ *
+ * @returns One of the listed VBox status codes.
+ * @retval VINF_SUCCESS if succeeded.
+ * @retval VERR_NOT_SUPPORTED if not supported.
+ *
+ * @param idCpu The CPU to modify the MSR on. NIL_RTCPUID
+ * indicates any suitable CPU.
+ * @param pReq The request packet with input arguments and
+ * where to store the results.
+ */
+int VBOXCALL supdrvOSMsrProberModify(RTCPUID idCpu, PSUPMSRPROBER pReq);
+
+#endif /* SUPDRV_WITH_MSR_PROBER */
+
+#if defined(RT_OS_DARWIN)
+int VBOXCALL supdrvDarwinResumeSuspendedKbds(void);
+#endif
+
+
+/*********************************************************************************************************************************
+* Shared Functions *
+*********************************************************************************************************************************/
+/* SUPDrv.c */
+int VBOXCALL supdrvIOCtl(uintptr_t uIOCtl, PSUPDRVDEVEXT pDevExt, PSUPDRVSESSION pSession, PSUPREQHDR pReqHdr, size_t cbReq);
+int VBOXCALL supdrvIOCtlFast(uintptr_t uOperation, VMCPUID idCpu, PSUPDRVDEVEXT pDevExt, PSUPDRVSESSION pSession);
+int VBOXCALL supdrvIDC(uintptr_t uIOCtl, PSUPDRVDEVEXT pDevExt, PSUPDRVSESSION pSession, PSUPDRVIDCREQHDR pReqHdr);
+int VBOXCALL supdrvInitDevExt(PSUPDRVDEVEXT pDevExt, size_t cbSession);
+void VBOXCALL supdrvDeleteDevExt(PSUPDRVDEVEXT pDevExt);
+int VBOXCALL supdrvCreateSession(PSUPDRVDEVEXT pDevExt, bool fUser, bool fUnrestricted, PSUPDRVSESSION *ppSession);
+int VBOXCALL supdrvSessionHashTabInsert(PSUPDRVDEVEXT pDevExt, PSUPDRVSESSION pSession, PSUPDRVSESSION *ppOsSessionPtr, void *pvUser);
+int VBOXCALL supdrvSessionHashTabRemove(PSUPDRVDEVEXT pDevExt, PSUPDRVSESSION pSession, void *pvUser);
+PSUPDRVSESSION VBOXCALL supdrvSessionHashTabLookup(PSUPDRVDEVEXT pDevExt, RTPROCESS Process, RTR0PROCESS R0Process,
+ PSUPDRVSESSION *ppOsSessionPtr);
+uint32_t VBOXCALL supdrvSessionRetain(PSUPDRVSESSION pSession);
+uint32_t VBOXCALL supdrvSessionRelease(PSUPDRVSESSION pSession);
+void VBOXCALL supdrvBadContext(PSUPDRVDEVEXT pDevExt, const char *pszFile, uint32_t uLine, const char *pszExtra);
+int VBOXCALL supdrvQueryVTCapsInternal(uint32_t *pfCaps);
+int VBOXCALL supdrvLdrLoadError(int rc, PSUPLDRLOAD pReq, const char *pszFormat, ...);
+int VBOXCALL supdrvLdrGetExportedSymbol(const char *pszSymbol, uintptr_t *puValue);
+int VBOXCALL supdrvLdrRegisterWrappedModule(PSUPDRVDEVEXT pDevExt, PCSUPLDRWRAPPEDMODULE pWrappedModInfo,
+ void *pvNative, void **phMod);
+int VBOXCALL supdrvLdrDeregisterWrappedModule(PSUPDRVDEVEXT pDevExt, PCSUPLDRWRAPPEDMODULE pWrappedModInfo, void **phMod);
+
+
+/* SUPDrvGip.cpp */
+int VBOXCALL supdrvGipCreate(PSUPDRVDEVEXT pDevExt);
+void VBOXCALL supdrvGipDestroy(PSUPDRVDEVEXT pDevExt);
+int VBOXCALL supdrvIOCtl_TscDeltaMeasure(PSUPDRVDEVEXT pDevExt, PSUPDRVSESSION pSession, PSUPTSCDELTAMEASURE pReq);
+int VBOXCALL supdrvIOCtl_TscRead(PSUPDRVDEVEXT pDevExt, PSUPDRVSESSION pSession, PSUPTSCREAD pReq);
+int VBOXCALL supdrvIOCtl_GipSetFlags(PSUPDRVDEVEXT pDevExt, PSUPDRVSESSION pSession, uint32_t fOrMask, uint32_t fAndMask);
+
+
+/* SUPDrvTracer.cpp */
+int VBOXCALL supdrvTracerInit(PSUPDRVDEVEXT pDevExt);
+void VBOXCALL supdrvTracerTerm(PSUPDRVDEVEXT pDevExt);
+void VBOXCALL supdrvTracerModuleUnloading(PSUPDRVDEVEXT pDevExt, PSUPDRVLDRIMAGE pImage);
+void VBOXCALL supdrvTracerCleanupSession(PSUPDRVDEVEXT pDevExt, PSUPDRVSESSION pSession);
+int VBOXCALL supdrvIOCtl_TracerUmodRegister(PSUPDRVDEVEXT pDevExt, PSUPDRVSESSION pSession,
+ RTR3PTR R3PtrVtgHdr, RTUINTPTR uVtgHdrAddr,
+ RTR3PTR R3PtrStrTab, uint32_t cbStrTab,
+ const char *pszModName, uint32_t fFlags);
+int VBOXCALL supdrvIOCtl_TracerUmodDeregister(PSUPDRVDEVEXT pDevExt, PSUPDRVSESSION pSession, RTR3PTR R3PtrVtgHdr);
+void VBOXCALL supdrvIOCtl_TracerUmodProbeFire(PSUPDRVDEVEXT pDevExt, PSUPDRVSESSION pSession, PSUPDRVTRACERUSRCTX pCtx);
+int VBOXCALL supdrvIOCtl_TracerOpen(PSUPDRVDEVEXT pDevExt, PSUPDRVSESSION pSession, uint32_t uCookie, uintptr_t uArg);
+int VBOXCALL supdrvIOCtl_TracerClose(PSUPDRVDEVEXT pDevExt, PSUPDRVSESSION pSession);
+int VBOXCALL supdrvIOCtl_TracerIOCtl(PSUPDRVDEVEXT pDevExt, PSUPDRVSESSION pSession, uintptr_t uCmd, uintptr_t uArg, int32_t *piRetVal);
+extern PFNRT g_pfnSupdrvProbeFireKernel;
+DECLASM(void) supdrvTracerProbeFireStub(void);
+
+#ifdef VBOX_WITH_NATIVE_DTRACE
+const SUPDRVTRACERREG * VBOXCALL supdrvDTraceInit(void);
+void VBOXCALL supdrvDTraceFini(void);
+#endif
+
+RT_C_DECLS_END
+
+#endif /* !VBOX_INCLUDED_SRC_Support_SUPDrvInternal_h */
+
diff --git a/src/VBox/HostDrivers/Support/SUPDrvSem.cpp b/src/VBox/HostDrivers/Support/SUPDrvSem.cpp
new file mode 100644
index 00000000..48b65ca3
--- /dev/null
+++ b/src/VBox/HostDrivers/Support/SUPDrvSem.cpp
@@ -0,0 +1,451 @@
+/* $Id: SUPDrvSem.cpp $ */
+/** @file
+ * VBoxDrv - The VirtualBox Support Driver - Common OS agnostic.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#define LOG_GROUP LOG_GROUP_SUP_DRV
+#define SUPDRV_AGNOSTIC
+#include "SUPDrvInternal.h"
+
+/** @todo trim this down. */
+#include <iprt/param.h>
+#include <iprt/alloc.h>
+#include <iprt/cpuset.h>
+#include <iprt/handletable.h>
+#include <iprt/mp.h>
+#include <iprt/power.h>
+#include <iprt/process.h>
+#include <iprt/semaphore.h>
+#include <iprt/spinlock.h>
+#include <iprt/thread.h>
+#include <iprt/uuid.h>
+
+#include <VBox/param.h>
+#include <VBox/log.h>
+#include <iprt/errcore.h>
+
+
+/**
+ * Destructor for objects created by SUPSemEventCreate.
+ *
+ * @param pvObj The object handle.
+ * @param pvUser1 The IPRT event handle.
+ * @param pvUser2 NULL.
+ */
+static DECLCALLBACK(void) supR0SemEventDestructor(void *pvObj, void *pvUser1, void *pvUser2)
+{
+ Assert(pvUser2 == NULL);
+ RT_NOREF2(pvObj, pvUser2);
+ RTSemEventDestroy((RTSEMEVENT)pvUser1);
+}
+
+
+SUPDECL(int) SUPSemEventCreate(PSUPDRVSESSION pSession, PSUPSEMEVENT phEvent)
+{
+ int rc;
+ RTSEMEVENT hEventReal;
+
+ /*
+ * Input validation.
+ */
+ AssertReturn(SUP_IS_SESSION_VALID(pSession), VERR_INVALID_PARAMETER);
+ AssertPtrReturn(phEvent, VERR_INVALID_POINTER);
+
+ /*
+ * Create the event semaphore object.
+ */
+ rc = RTSemEventCreate(&hEventReal);
+ if (RT_SUCCESS(rc))
+ {
+ void *pvObj = SUPR0ObjRegister(pSession, SUPDRVOBJTYPE_SEM_EVENT, supR0SemEventDestructor, hEventReal, NULL);
+ if (pvObj)
+ {
+ uint32_t h32;
+ rc = RTHandleTableAllocWithCtx(pSession->hHandleTable, pvObj, SUPDRV_HANDLE_CTX_EVENT, &h32);
+ if (RT_SUCCESS(rc))
+ {
+ *phEvent = (SUPSEMEVENT)(uintptr_t)h32;
+ return VINF_SUCCESS;
+ }
+ SUPR0ObjRelease(pvObj, pSession);
+ }
+ else
+ RTSemEventDestroy(hEventReal);
+ }
+ return rc;
+}
+SUPR0_EXPORT_SYMBOL(SUPSemEventCreate);
+
+
+SUPDECL(int) SUPSemEventClose(PSUPDRVSESSION pSession, SUPSEMEVENT hEvent)
+{
+ uint32_t h32;
+ PSUPDRVOBJ pObj;
+
+ /*
+ * Input validation.
+ */
+ AssertReturn(SUP_IS_SESSION_VALID(pSession), VERR_INVALID_PARAMETER);
+ if (hEvent == NIL_SUPSEMEVENT)
+ return VINF_SUCCESS;
+ h32 = (uint32_t)(uintptr_t)hEvent;
+ if (h32 != (uintptr_t)hEvent)
+ return VERR_INVALID_HANDLE;
+
+ /*
+ * Do the job.
+ */
+ pObj = (PSUPDRVOBJ)RTHandleTableFreeWithCtx(pSession->hHandleTable, h32, SUPDRV_HANDLE_CTX_EVENT);
+ if (!pObj)
+ return VERR_INVALID_HANDLE;
+
+ Assert(pObj->cUsage >= 2);
+ SUPR0ObjRelease(pObj, pSession); /* The free call above. */
+ return SUPR0ObjRelease(pObj, pSession); /* The handle table reference. */
+}
+SUPR0_EXPORT_SYMBOL(SUPSemEventClose);
+
+
+SUPDECL(int) SUPSemEventSignal(PSUPDRVSESSION pSession, SUPSEMEVENT hEvent)
+{
+ int rc;
+ uint32_t h32;
+ PSUPDRVOBJ pObj;
+
+ /*
+ * Input validation.
+ */
+ AssertReturn(SUP_IS_SESSION_VALID(pSession), VERR_INVALID_PARAMETER);
+ h32 = (uint32_t)(uintptr_t)hEvent;
+ if (h32 != (uintptr_t)hEvent)
+ return VERR_INVALID_HANDLE;
+ pObj = (PSUPDRVOBJ)RTHandleTableLookupWithCtx(pSession->hHandleTable, h32, SUPDRV_HANDLE_CTX_EVENT);
+ if (!pObj)
+ return VERR_INVALID_HANDLE;
+
+ /*
+ * Do the job.
+ */
+ rc = RTSemEventSignal((RTSEMEVENT)pObj->pvUser1);
+
+ SUPR0ObjRelease(pObj, pSession);
+ return rc;
+}
+SUPR0_EXPORT_SYMBOL(SUPSemEventSignal);
+
+
+static int supR0SemEventWaitEx(PSUPDRVSESSION pSession, SUPSEMEVENT hEvent, uint32_t fFlags, uint64_t uTimeout)
+{
+ int rc;
+ uint32_t h32;
+ PSUPDRVOBJ pObj;
+
+ /*
+ * Input validation.
+ */
+ AssertReturn(SUP_IS_SESSION_VALID(pSession), VERR_INVALID_PARAMETER);
+ h32 = (uint32_t)(uintptr_t)hEvent;
+ if (h32 != (uintptr_t)hEvent)
+ return VERR_INVALID_HANDLE;
+ pObj = (PSUPDRVOBJ)RTHandleTableLookupWithCtx(pSession->hHandleTable, h32, SUPDRV_HANDLE_CTX_EVENT);
+ if (!pObj)
+ return VERR_INVALID_HANDLE;
+
+ /*
+ * Do the job.
+ */
+ rc = RTSemEventWaitEx((RTSEMEVENT)pObj->pvUser1, fFlags, uTimeout);
+
+ SUPR0ObjRelease(pObj, pSession);
+ return rc;
+}
+
+
+SUPDECL(int) SUPSemEventWait(PSUPDRVSESSION pSession, SUPSEMEVENT hEvent, uint32_t cMillies)
+{
+ uint32_t fFlags = RTSEMWAIT_FLAGS_RELATIVE | RTSEMWAIT_FLAGS_MILLISECS | RTSEMWAIT_FLAGS_UNINTERRUPTIBLE;
+ if (cMillies == RT_INDEFINITE_WAIT)
+ fFlags |= RTSEMWAIT_FLAGS_INDEFINITE;
+ return supR0SemEventWaitEx(pSession, hEvent, fFlags, cMillies);
+}
+SUPR0_EXPORT_SYMBOL(SUPSemEventWait);
+
+
+SUPDECL(int) SUPSemEventWaitNoResume(PSUPDRVSESSION pSession, SUPSEMEVENT hEvent, uint32_t cMillies)
+{
+ uint32_t fFlags = RTSEMWAIT_FLAGS_RELATIVE | RTSEMWAIT_FLAGS_MILLISECS | RTSEMWAIT_FLAGS_INTERRUPTIBLE;
+ if (cMillies == RT_INDEFINITE_WAIT)
+ fFlags |= RTSEMWAIT_FLAGS_INDEFINITE;
+ return supR0SemEventWaitEx(pSession, hEvent, fFlags, cMillies);
+}
+SUPR0_EXPORT_SYMBOL(SUPSemEventWaitNoResume);
+
+
+SUPDECL(int) SUPSemEventWaitNsAbsIntr(PSUPDRVSESSION pSession, SUPSEMEVENT hEvent, uint64_t uNsTimeout)
+{
+ uint32_t fFlags = RTSEMWAIT_FLAGS_ABSOLUTE | RTSEMWAIT_FLAGS_NANOSECS | RTSEMWAIT_FLAGS_INTERRUPTIBLE;
+ return supR0SemEventWaitEx(pSession, hEvent, fFlags, uNsTimeout);
+}
+SUPR0_EXPORT_SYMBOL(SUPSemEventWaitNsAbsIntr);
+
+
+SUPDECL(int) SUPSemEventWaitNsRelIntr(PSUPDRVSESSION pSession, SUPSEMEVENT hEvent, uint64_t cNsTimeout)
+{
+ uint32_t fFlags = RTSEMWAIT_FLAGS_RELATIVE | RTSEMWAIT_FLAGS_NANOSECS | RTSEMWAIT_FLAGS_INTERRUPTIBLE;
+ return supR0SemEventWaitEx(pSession, hEvent, fFlags, cNsTimeout);
+}
+SUPR0_EXPORT_SYMBOL(SUPSemEventWaitNsRelIntr);
+
+
+SUPDECL(uint32_t) SUPSemEventGetResolution(PSUPDRVSESSION pSession)
+{
+ RT_NOREF1(pSession);
+ Assert(SUP_IS_SESSION_VALID(pSession));
+ return RTSemEventGetResolution();
+}
+SUPR0_EXPORT_SYMBOL(SUPSemEventGetResolution);
+
+
+/**
+ * Destructor for objects created by SUPSemEventMultiCreate.
+ *
+ * @param pvObj The object handle.
+ * @param pvUser1 The IPRT event handle.
+ * @param pvUser2 NULL.
+ */
+static DECLCALLBACK(void) supR0SemEventMultiDestructor(void *pvObj, void *pvUser1, void *pvUser2)
+{
+ Assert(pvUser2 == NULL);
+ RT_NOREF2(pvObj, pvUser2);
+ RTSemEventMultiDestroy((RTSEMEVENTMULTI)pvUser1);
+}
+
+
+SUPDECL(int) SUPSemEventMultiCreate(PSUPDRVSESSION pSession, PSUPSEMEVENTMULTI phEventMulti)
+{
+ int rc;
+ RTSEMEVENTMULTI hEventMultReal;
+
+ /*
+ * Input validation.
+ */
+ AssertReturn(SUP_IS_SESSION_VALID(pSession), VERR_INVALID_PARAMETER);
+ AssertPtrReturn(phEventMulti, VERR_INVALID_POINTER);
+
+ /*
+ * Create the event semaphore object.
+ */
+ rc = RTSemEventMultiCreate(&hEventMultReal);
+ if (RT_SUCCESS(rc))
+ {
+ void *pvObj = SUPR0ObjRegister(pSession, SUPDRVOBJTYPE_SEM_EVENT_MULTI, supR0SemEventMultiDestructor, hEventMultReal, NULL);
+ if (pvObj)
+ {
+ uint32_t h32;
+ rc = RTHandleTableAllocWithCtx(pSession->hHandleTable, pvObj, SUPDRV_HANDLE_CTX_EVENT_MULTI, &h32);
+ if (RT_SUCCESS(rc))
+ {
+ *phEventMulti = (SUPSEMEVENTMULTI)(uintptr_t)h32;
+ return VINF_SUCCESS;
+ }
+ SUPR0ObjRelease(pvObj, pSession);
+ }
+ else
+ RTSemEventMultiDestroy(hEventMultReal);
+ }
+ return rc;
+}
+SUPR0_EXPORT_SYMBOL(SUPSemEventMultiCreate);
+
+
+SUPDECL(int) SUPSemEventMultiClose(PSUPDRVSESSION pSession, SUPSEMEVENTMULTI hEventMulti)
+{
+ uint32_t h32;
+ PSUPDRVOBJ pObj;
+
+ /*
+ * Input validation.
+ */
+ AssertReturn(SUP_IS_SESSION_VALID(pSession), VERR_INVALID_PARAMETER);
+ if (hEventMulti == NIL_SUPSEMEVENTMULTI)
+ return VINF_SUCCESS;
+ h32 = (uint32_t)(uintptr_t)hEventMulti;
+ if (h32 != (uintptr_t)hEventMulti)
+ return VERR_INVALID_HANDLE;
+
+ /*
+ * Do the job.
+ */
+ pObj = (PSUPDRVOBJ)RTHandleTableFreeWithCtx(pSession->hHandleTable, h32, SUPDRV_HANDLE_CTX_EVENT_MULTI);
+ if (!pObj)
+ return VERR_INVALID_HANDLE;
+
+ Assert(pObj->cUsage >= 2);
+ SUPR0ObjRelease(pObj, pSession); /* The free call above. */
+ return SUPR0ObjRelease(pObj, pSession); /* The handle table reference. */
+}
+SUPR0_EXPORT_SYMBOL(SUPSemEventMultiClose);
+
+
+SUPDECL(int) SUPSemEventMultiSignal(PSUPDRVSESSION pSession, SUPSEMEVENTMULTI hEventMulti)
+{
+ int rc;
+ uint32_t h32;
+ PSUPDRVOBJ pObj;
+
+ /*
+ * Input validation.
+ */
+ AssertReturn(SUP_IS_SESSION_VALID(pSession), VERR_INVALID_PARAMETER);
+ h32 = (uint32_t)(uintptr_t)hEventMulti;
+ if (h32 != (uintptr_t)hEventMulti)
+ return VERR_INVALID_HANDLE;
+ pObj = (PSUPDRVOBJ)RTHandleTableLookupWithCtx(pSession->hHandleTable, h32, SUPDRV_HANDLE_CTX_EVENT_MULTI);
+ if (!pObj)
+ return VERR_INVALID_HANDLE;
+
+ /*
+ * Do the job.
+ */
+ rc = RTSemEventMultiSignal((RTSEMEVENTMULTI)pObj->pvUser1);
+
+ SUPR0ObjRelease(pObj, pSession);
+ return rc;
+}
+SUPR0_EXPORT_SYMBOL(SUPSemEventMultiSignal);
+
+
+SUPDECL(int) SUPSemEventMultiReset(PSUPDRVSESSION pSession, SUPSEMEVENTMULTI hEventMulti)
+{
+ int rc;
+ uint32_t h32;
+ PSUPDRVOBJ pObj;
+
+ /*
+ * Input validation.
+ */
+ AssertReturn(SUP_IS_SESSION_VALID(pSession), VERR_INVALID_PARAMETER);
+ h32 = (uint32_t)(uintptr_t)hEventMulti;
+ if (h32 != (uintptr_t)hEventMulti)
+ return VERR_INVALID_HANDLE;
+ pObj = (PSUPDRVOBJ)RTHandleTableLookupWithCtx(pSession->hHandleTable, h32, SUPDRV_HANDLE_CTX_EVENT_MULTI);
+ if (!pObj)
+ return VERR_INVALID_HANDLE;
+
+ /*
+ * Do the job.
+ */
+ rc = RTSemEventMultiReset((RTSEMEVENTMULTI)pObj->pvUser1);
+
+ SUPR0ObjRelease(pObj, pSession);
+ return rc;
+}
+SUPR0_EXPORT_SYMBOL(SUPSemEventMultiReset);
+
+
+static int supR0SemEventMultiWaitEx(PSUPDRVSESSION pSession, SUPSEMEVENTMULTI hEventMulti, uint32_t fFlags, uint64_t uTimeout)
+{
+ int rc;
+ uint32_t h32;
+ PSUPDRVOBJ pObj;
+
+ /*
+ * Input validation.
+ */
+ AssertReturn(SUP_IS_SESSION_VALID(pSession), VERR_INVALID_PARAMETER);
+ h32 = (uint32_t)(uintptr_t)hEventMulti;
+ if (h32 != (uintptr_t)hEventMulti)
+ return VERR_INVALID_HANDLE;
+ pObj = (PSUPDRVOBJ)RTHandleTableLookupWithCtx(pSession->hHandleTable, h32, SUPDRV_HANDLE_CTX_EVENT_MULTI);
+ if (!pObj)
+ return VERR_INVALID_HANDLE;
+
+ /*
+ * Do the job.
+ */
+ rc = RTSemEventMultiWaitEx((RTSEMEVENTMULTI)pObj->pvUser1, fFlags, uTimeout);
+
+ SUPR0ObjRelease(pObj, pSession);
+ return rc;
+}
+
+
+SUPDECL(int) SUPSemEventMultiWait(PSUPDRVSESSION pSession, SUPSEMEVENTMULTI hEventMulti, uint32_t cMillies)
+{
+ uint32_t fFlags = RTSEMWAIT_FLAGS_RELATIVE | RTSEMWAIT_FLAGS_MILLISECS | RTSEMWAIT_FLAGS_UNINTERRUPTIBLE;
+ if (cMillies == RT_INDEFINITE_WAIT)
+ fFlags |= RTSEMWAIT_FLAGS_INDEFINITE;
+ return supR0SemEventMultiWaitEx(pSession, hEventMulti, fFlags, cMillies);
+}
+SUPR0_EXPORT_SYMBOL(SUPSemEventMultiWait);
+
+
+
+SUPDECL(int) SUPSemEventMultiWaitNoResume(PSUPDRVSESSION pSession, SUPSEMEVENTMULTI hEventMulti, uint32_t cMillies)
+{
+ uint32_t fFlags = RTSEMWAIT_FLAGS_RELATIVE | RTSEMWAIT_FLAGS_MILLISECS | RTSEMWAIT_FLAGS_INTERRUPTIBLE;
+ if (cMillies == RT_INDEFINITE_WAIT)
+ fFlags |= RTSEMWAIT_FLAGS_INDEFINITE;
+ return supR0SemEventMultiWaitEx(pSession, hEventMulti, fFlags, cMillies);
+}
+SUPR0_EXPORT_SYMBOL(SUPSemEventMultiWaitNoResume);
+
+
+SUPDECL(int) SUPSemEventMultiWaitNsAbsIntr(PSUPDRVSESSION pSession, SUPSEMEVENTMULTI hEventMulti, uint64_t uNsTimeout)
+{
+ uint32_t fFlags = RTSEMWAIT_FLAGS_ABSOLUTE | RTSEMWAIT_FLAGS_NANOSECS | RTSEMWAIT_FLAGS_INTERRUPTIBLE;
+ return supR0SemEventMultiWaitEx(pSession, hEventMulti, fFlags, uNsTimeout);
+}
+SUPR0_EXPORT_SYMBOL(SUPSemEventMultiWaitNsAbsIntr);
+
+
+SUPDECL(int) SUPSemEventMultiWaitNsRelIntr(PSUPDRVSESSION pSession, SUPSEMEVENTMULTI hEventMulti, uint64_t cNsTimeout)
+{
+ uint32_t fFlags = RTSEMWAIT_FLAGS_RELATIVE | RTSEMWAIT_FLAGS_NANOSECS | RTSEMWAIT_FLAGS_INTERRUPTIBLE;
+ return supR0SemEventMultiWaitEx(pSession, hEventMulti, fFlags, cNsTimeout);
+}
+SUPR0_EXPORT_SYMBOL(SUPSemEventMultiWaitNsRelIntr);
+
+
+SUPDECL(uint32_t) SUPSemEventMultiGetResolution(PSUPDRVSESSION pSession)
+{
+ RT_NOREF1(pSession);
+ Assert(SUP_IS_SESSION_VALID(pSession));
+ return RTSemEventMultiGetResolution();
+}
+SUPR0_EXPORT_SYMBOL(SUPSemEventMultiGetResolution);
diff --git a/src/VBox/HostDrivers/Support/SUPDrvTracer.cpp b/src/VBox/HostDrivers/Support/SUPDrvTracer.cpp
new file mode 100644
index 00000000..7b25b5ce
--- /dev/null
+++ b/src/VBox/HostDrivers/Support/SUPDrvTracer.cpp
@@ -0,0 +1,2494 @@
+/* $Id: SUPDrvTracer.cpp $ */
+/** @file
+ * VBoxDrv - The VirtualBox Support Driver - Tracer Interface.
+ */
+
+/*
+ * Copyright (C) 2012-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#define LOG_GROUP LOG_GROUP_SUP_DRV
+#define SUPDRV_AGNOSTIC
+#include "SUPDrvInternal.h"
+
+#include <VBox/err.h>
+#include <VBox/log.h>
+#include <VBox/VBoxTpG.h>
+
+#include <iprt/assert.h>
+#include <iprt/ctype.h>
+#include <iprt/list.h>
+#include <iprt/mem.h>
+#include <iprt/semaphore.h>
+#include <iprt/thread.h>
+#include <iprt/param.h>
+#include <iprt/uuid.h>
+
+
+/*********************************************************************************************************************************
+* Structures and Typedefs *
+*********************************************************************************************************************************/
+/** Pointer to a user tracer module registration record. */
+typedef struct SUPDRVTRACERUMOD *PSUPDRVTRACERUMOD;
+
+/**
+ * Data for a tracepoint provider.
+ */
+typedef struct SUPDRVTPPROVIDER
+{
+ /** The entry in the provider list for this image. */
+ RTLISTNODE ListEntry;
+ /** The entry in the per session provider list for this image. */
+ RTLISTNODE SessionListEntry;
+
+ /** The core structure. */
+ SUPDRVVDTPROVIDERCORE Core;
+
+ /** Pointer to the image this provider resides in. NULL if it's a
+ * driver. */
+ PSUPDRVLDRIMAGE pImage;
+ /** The session this provider is associated with if registered via
+ * SUPR0VtgRegisterDrv. NULL if pImage is set. */
+ PSUPDRVSESSION pSession;
+ /** The user tracepoint module associated with this provider. NULL if
+ * pImage is set. */
+ PSUPDRVTRACERUMOD pUmod;
+
+ /** Used to indicate that we've called pfnProviderDeregistered already and it
+ * failed because the provider was busy. Next time we must try
+ * pfnProviderDeregisterZombie.
+ *
+ * @remarks This does not necessiarly mean the provider is in the zombie
+ * list. See supdrvTracerCommonDeregisterImpl. */
+ bool fZombie;
+ /** Set if the provider has been successfully registered with the
+ * tracer. */
+ bool fRegistered;
+ /** The provider name (for logging purposes). */
+ char szName[1];
+} SUPDRVTPPROVIDER;
+/** Pointer to the data for a tracepoint provider. */
+typedef SUPDRVTPPROVIDER *PSUPDRVTPPROVIDER;
+
+
+/**
+ * User tracer module VTG data copy.
+ */
+typedef struct SUPDRVVTGCOPY
+{
+ /** Magic (SUDPRVVTGCOPY_MAGIC). */
+ uint32_t u32Magic;
+ /** Refernece counter (we expect to share a lot of these). */
+ uint32_t cRefs;
+ /** The size of the */
+ uint32_t cbStrTab;
+ /** Image type flags. */
+ uint32_t fFlags;
+ /** Hash list entry (SUPDRVDEVEXT::aTrackerUmodHash). */
+ RTLISTNODE ListEntry;
+ /** The VTG object header.
+ * The rest of the data follows immediately afterwards. First the object,
+ * then the probe locations and finally the probe location string table. All
+ * pointers are fixed up to point within this data. */
+ VTGOBJHDR Hdr;
+} SUPDRVVTGCOPY;
+/** Pointer to a VTG object copy. */
+typedef SUPDRVVTGCOPY *PSUPDRVVTGCOPY;
+/** Magic value for SUPDRVVTGCOPY. */
+#define SUDPRVVTGCOPY_MAGIC UINT32_C(0x00080386)
+
+
+/**
+ * User tracer module registration record.
+ */
+typedef struct SUPDRVTRACERUMOD
+{
+ /** Magic (SUPDRVTRACERUMOD_MAGIC). */
+ uint32_t u32Magic;
+ /** List entry. This is anchored in SUPDRVSESSION::UmodList. */
+ RTLISTNODE ListEntry;
+ /** The address of the ring-3 VTG header. */
+ RTR3PTR R3PtrVtgHdr;
+ /** Pointer to the ring-0 copy of the VTG data. */
+ PSUPDRVVTGCOPY pVtgCopy;
+ /** The memory object that locks down the user memory. */
+ RTR0MEMOBJ hMemObjLock;
+ /** The memory object that maps the locked memory into kernel space. */
+ RTR0MEMOBJ hMemObjMap;
+ /** Pointer to the probe enabled-count array within the mapping. */
+ uint32_t *pacProbeEnabled;
+ /** Pointer to the probe location array within the mapping. */
+ void *pvProbeLocs;
+ /** The address of the ring-3 probe locations. */
+ RTR3PTR R3PtrProbeLocs;
+ /** The lookup table index. */
+ uint8_t iLookupTable;
+ /** The module bit count. */
+ uint8_t cBits;
+ /** The size of a probe location record. */
+ uint8_t cbProbeLoc;
+ /** The number of probe locations. */
+ uint32_t cProbeLocs;
+ /** Ring-0 probe location info. */
+ SUPDRVPROBELOC aProbeLocs[1];
+} SUPDRVTRACERUMOD;
+/** Magic value for SUPDRVVTGCOPY. */
+#define SUPDRVTRACERUMOD_MAGIC UINT32_C(0x00080486)
+
+
+/*********************************************************************************************************************************
+* Defined Constants And Macros *
+*********************************************************************************************************************************/
+/** Simple SUPR0Printf-style logging. */
+#ifdef DEBUG_bird
+# define LOG_TRACER(a_Args) SUPR0Printf a_Args
+#else
+# define LOG_TRACER(a_Args) do { } while (0)
+#endif
+
+
+/*********************************************************************************************************************************
+* Global Variables *
+*********************************************************************************************************************************/
+/** The address of the current probe fire routine for kernel mode. */
+PFNRT g_pfnSupdrvProbeFireKernel = supdrvTracerProbeFireStub;
+
+
+/*********************************************************************************************************************************
+* Internal Functions *
+*********************************************************************************************************************************/
+static void supdrvVtgReleaseObjectCopy(PSUPDRVDEVEXT pDevExt, PSUPDRVVTGCOPY pThis);
+
+
+
+/**
+ * Validates a VTG string against length and characterset limitations.
+ *
+ * @returns VINF_SUCCESS, VERR_SUPDRV_VTG_BAD_STRING or
+ * VERR_SUPDRV_VTG_STRING_TOO_LONG.
+ * @param psz The string.
+ */
+static int supdrvVtgValidateString(const char *psz)
+{
+ size_t off = 0;
+ while (off < _4K)
+ {
+ char const ch = psz[off++];
+ if (!ch)
+ return VINF_SUCCESS;
+ if ( !RTLocCIsAlNum(ch)
+ && ch != ' '
+ && ch != '_'
+ && ch != '-'
+ && ch != '('
+ && ch != ')'
+ && ch != ','
+ && ch != '*'
+ && ch != '&'
+ )
+ {
+ /*RTAssertMsg2("off=%u '%s'\n", off, psz);*/
+ return VERR_SUPDRV_VTG_BAD_STRING;
+ }
+ }
+ return VERR_SUPDRV_VTG_STRING_TOO_LONG;
+}
+
+
+/** Used by the validation code below. */
+#define MY_CHECK_RET(a_Expr, a_rc) \
+ MY_CHECK_MSG_RET(a_Expr, ("%s: Validation failed on line " RT_XSTR(__LINE__) ": " #a_Expr "\n", __FUNCTION__), a_rc)
+
+/** Used by the validation code below. */
+#define MY_CHECK_MSG_RET(a_Expr, a_PrintfArgs, a_rc) \
+ do { if (RT_UNLIKELY(!(a_Expr))) { SUPR0Printf a_PrintfArgs; return (a_rc); } } while (0)
+
+/** Used by the validation code below. */
+#define MY_WITHIN_IMAGE(p, rc) \
+ do { \
+ if (pbImage) \
+ { \
+ if ((uintptr_t)(p) - (uintptr_t)pbImage > cbImage) \
+ { \
+ SUPR0Printf("supdrvVtgValidate: " #rc " - p=%p pbImage=%p cbImage=%#zxline=%u %s\n", \
+ p, pbImage, cbImage, #p); \
+ return (rc); \
+ } \
+ } \
+ else if (!RT_VALID_PTR(p)) \
+ return (rc); \
+ } while (0)
+
+
+/**
+ * Validates the VTG object header.
+ *
+ * @returns VBox status code.
+ * @param pVtgHdr The header.
+ * @param uVtgHdrAddr The address where the header is actually
+ * loaded.
+ * @param pbImage The image base, if available.
+ * @param cbImage The image size, if available.
+ * @param fUmod Whether this is a user module.
+ */
+static int supdrvVtgValidateHdr(PVTGOBJHDR pVtgHdr, RTUINTPTR uVtgHdrAddr, const uint8_t *pbImage, size_t cbImage, bool fUmod)
+{
+ struct VTGAREAS
+ {
+ uint32_t off;
+ uint32_t cb;
+ } const *paAreas;
+ unsigned cAreas;
+ unsigned i;
+ uint32_t cbVtgObj;
+ uint32_t off;
+
+#define MY_VALIDATE_SIZE(cb, cMin, cMax, cbUnit, rcBase) \
+ do { \
+ if ((cb) < (cMin) * (cbUnit)) \
+ { \
+ SUPR0Printf("supdrvVtgValidateHdr: " #rcBase "_TOO_FEW - cb=%#zx cMin=%#zx cbUnit=%#zx line=%u %s\n", \
+ (size_t)(cb), (size_t)(cMin), (size_t)cbUnit, __LINE__, #cb); \
+ return rcBase ## _TOO_FEW; \
+ } \
+ if ((cb) >= (cMax) * (cbUnit)) \
+ { \
+ SUPR0Printf("supdrvVtgValidateHdr: " #rcBase "_TOO_MUCH - cb=%#zx cMax=%#zx cbUnit=%#zx line=%u %s\n", \
+ (size_t)(cb), (size_t)(cMax), (size_t)cbUnit, __LINE__, #cb); \
+ return rcBase ## _TOO_MUCH; \
+ } \
+ if ((cb) / (cbUnit) * (cbUnit) != (cb)) \
+ { \
+ SUPR0Printf("supdrvVtgValidateHdr: " #rcBase "_NOT_MULTIPLE - cb=%#zx cbUnit=%#zx line=%u %s\n", \
+ (size_t)(cb), (size_t)cbUnit, __LINE__, #cb); \
+ return rcBase ## _NOT_MULTIPLE; \
+ } \
+ } while (0)
+
+#define MY_VALIDATE_OFF(off, cb, cMin, cMax, cbUnit, cbAlign, rcBase) \
+ do { \
+ if ( (cb) >= cbVtgObj \
+ || off > cbVtgObj - (cb) ) \
+ { \
+ SUPR0Printf("supdrvVtgValidateHdr: " #rcBase "_OFF - off=%#x cb=%#x pVtgHdr=%p cbVtgHdr=%#zx line=%u %s\n", \
+ (off), (cb), pVtgHdr, cbVtgObj, __LINE__, #off); \
+ return rcBase ## _OFF; \
+ } \
+ if (RT_ALIGN(off, cbAlign) != (off)) \
+ { \
+ SUPR0Printf("supdrvVtgValidateHdr: " #rcBase "_OFF - off=%#x align=%#zx line=%u %s\n", \
+ (off), (size_t)(cbAlign), __LINE__, #off); \
+ return rcBase ## _OFF; \
+ } \
+ MY_VALIDATE_SIZE(cb, cMin, cMax, cbUnit, rcBase); \
+ } while (0)
+
+ /*
+ * Make sure both pbImage and cbImage are NULL/0 if one if of them is.
+ */
+ if (!pbImage || !cbImage)
+ {
+ pbImage = NULL;
+ cbImage = 0;
+ cbVtgObj = pVtgHdr->cbObj;
+ }
+ else
+ {
+ MY_WITHIN_IMAGE(pVtgHdr, VERR_SUPDRV_VTG_BAD_HDR_PTR);
+ cbVtgObj = pVtgHdr->cbObj;
+ MY_WITHIN_IMAGE((uint8_t *)pVtgHdr + cbVtgObj - 1, VERR_SUPDRV_VTG_BAD_HDR_PTR);
+ }
+
+ if (cbVtgObj > _1M)
+ {
+ SUPR0Printf("supdrvVtgValidateHdr: VERR_SUPDRV_TRACER_TOO_LARGE - cbVtgObj=%#x\n", cbVtgObj);
+ return VERR_SUPDRV_TRACER_TOO_LARGE;
+ }
+
+ /*
+ * Set the probe location array offset and size members.
+ */
+ if (!pVtgHdr->offProbeLocs)
+ {
+ uint64_t u64Tmp = pVtgHdr->uProbeLocsEnd.u64 - pVtgHdr->uProbeLocs.u64;
+ if (u64Tmp >= UINT32_MAX)
+ {
+ SUPR0Printf("supdrvVtgValidateHdr: VERR_SUPDRV_VTG_BAD_HDR_TOO_MUCH - u64Tmp=%#llx ProbeLocs=%#llx ProbeLocsEnd=%#llx\n",
+ u64Tmp, pVtgHdr->uProbeLocs.u64, pVtgHdr->uProbeLocsEnd.u64);
+ return VERR_SUPDRV_VTG_BAD_HDR_TOO_MUCH;
+ }
+ /*SUPR0Printf("supdrvVtgValidateHdr: cbProbeLocs %#x -> %#x\n", pVtgHdr->cbProbeLocs, (uint32_t)u64Tmp);*/
+ pVtgHdr->cbProbeLocs = (uint32_t)u64Tmp;
+
+ u64Tmp = pVtgHdr->uProbeLocs.u64 - uVtgHdrAddr;
+#ifdef RT_OS_DARWIN
+ /* The loader and/or ld64-97.17 seems not to generate fixups for our
+ __VTGObj section. Detect this by comparing them with the
+ u64VtgObjSectionStart member and assume max image size of 4MB.
+ Seems to be worked around by the __VTGPrLc.End and __VTGPrLc.Begin
+ padding fudge, meaning that the linker misplaced the relocations. */
+ if ( (int64_t)u64Tmp != (int32_t)u64Tmp
+ && pVtgHdr->u64VtgObjSectionStart != uVtgHdrAddr
+ && pVtgHdr->u64VtgObjSectionStart < _4M
+ && pVtgHdr->uProbeLocsEnd.u64 < _4M
+ && !fUmod)
+ {
+ uint64_t offDelta = uVtgHdrAddr - pVtgHdr->u64VtgObjSectionStart;
+ /*SUPR0Printf("supdrvVtgValidateHdr: offDelta=%#llx\n", offDelta);*/
+ pVtgHdr->uProbeLocs.u64 += offDelta;
+ pVtgHdr->uProbeLocsEnd.u64 += offDelta;
+ u64Tmp += offDelta;
+ }
+#endif
+ if ((int64_t)u64Tmp != (int32_t)u64Tmp)
+ {
+ SUPR0Printf("supdrvVtgValidateHdr: VERR_SUPDRV_VTG_BAD_HDR_PTR - u64Tmp=%#llx uProbeLocs=%#llx uVtgHdrAddr=%RTptr\n",
+ u64Tmp, pVtgHdr->uProbeLocs.u64, uVtgHdrAddr);
+ return VERR_SUPDRV_VTG_BAD_HDR_PTR;
+ }
+ /*SUPR0Printf("supdrvVtgValidateHdr: offProbeLocs %#x -> %#x\n", pVtgHdr->offProbeLocs, (int32_t)u64Tmp);*/
+ pVtgHdr->offProbeLocs = (int32_t)u64Tmp;
+ }
+
+ /*
+ * The non-area description fields.
+ */
+ if (memcmp(pVtgHdr->szMagic, VTGOBJHDR_MAGIC, sizeof(pVtgHdr->szMagic)))
+ {
+ SUPR0Printf("supdrvVtgValidateHdr: %p: %.16Rhxs\n", pVtgHdr, pVtgHdr->szMagic);
+ return VERR_SUPDRV_VTG_MAGIC;
+ }
+ if ( pVtgHdr->cBits != ARCH_BITS
+ && ( !fUmod
+ || ( pVtgHdr->cBits != 32
+ && pVtgHdr->cBits != 64)) )
+ return VERR_SUPDRV_VTG_BITS;
+ MY_CHECK_RET(pVtgHdr->au32Reserved1[0] == 0, VERR_SUPDRV_VTG_BAD_HDR_MISC);
+ MY_CHECK_RET(pVtgHdr->au32Reserved1[1] == 0, VERR_SUPDRV_VTG_BAD_HDR_MISC);
+ MY_CHECK_RET(!RTUuidIsNull(&pVtgHdr->Uuid), VERR_SUPDRV_VTG_BAD_HDR_MISC);
+
+ /*
+ * Check the individual area descriptors.
+ */
+ MY_VALIDATE_OFF(pVtgHdr->offStrTab, pVtgHdr->cbStrTab, 4, _1M, sizeof(char), sizeof(uint8_t), VERR_SUPDRV_VTG_BAD_HDR);
+ MY_VALIDATE_OFF(pVtgHdr->offArgLists, pVtgHdr->cbArgLists, 1, _32K, sizeof(uint32_t), sizeof(uint32_t), VERR_SUPDRV_VTG_BAD_HDR);
+ MY_VALIDATE_OFF(pVtgHdr->offProbes, pVtgHdr->cbProbes, 1, _32K, sizeof(VTGDESCPROBE), sizeof(uint32_t), VERR_SUPDRV_VTG_BAD_HDR);
+ MY_VALIDATE_OFF(pVtgHdr->offProviders, pVtgHdr->cbProviders, 1, 16, sizeof(VTGDESCPROVIDER), sizeof(uint32_t), VERR_SUPDRV_VTG_BAD_HDR);
+ MY_VALIDATE_OFF(pVtgHdr->offProbeEnabled, pVtgHdr->cbProbeEnabled, 1, _32K, sizeof(uint32_t), sizeof(uint32_t), VERR_SUPDRV_VTG_BAD_HDR);
+ if (!fUmod)
+ {
+ MY_WITHIN_IMAGE(pVtgHdr->uProbeLocs.p, VERR_SUPDRV_VTG_BAD_HDR_PTR);
+ MY_WITHIN_IMAGE(pVtgHdr->uProbeLocsEnd.p, VERR_SUPDRV_VTG_BAD_HDR_PTR);
+ MY_VALIDATE_SIZE( pVtgHdr->cbProbeLocs, 1, _128K, sizeof(VTGPROBELOC), VERR_SUPDRV_VTG_BAD_HDR);
+ }
+ else
+ {
+ if (pVtgHdr->cBits == 32)
+ MY_VALIDATE_SIZE( pVtgHdr->cbProbeLocs, 1, _8K, sizeof(VTGPROBELOC32), VERR_SUPDRV_VTG_BAD_HDR);
+ else
+ MY_VALIDATE_SIZE( pVtgHdr->cbProbeLocs, 1, _8K, sizeof(VTGPROBELOC64), VERR_SUPDRV_VTG_BAD_HDR);
+ /* Will check later that offProbeLocs are following closely on the
+ enable count array, so no need to validate the offset here. */
+ }
+
+ /*
+ * Some additional consistency checks.
+ */
+ if ( pVtgHdr->uProbeLocsEnd.u64 - pVtgHdr->uProbeLocs.u64 != pVtgHdr->cbProbeLocs
+ || (int64_t)(pVtgHdr->uProbeLocs.u64 - uVtgHdrAddr) != pVtgHdr->offProbeLocs)
+ {
+ SUPR0Printf("supdrvVtgValidateHdr: VERR_SUPDRV_VTG_BAD_HDR_MISC - uProbeLocs=%#llx uProbeLocsEnd=%#llx offProbeLocs=%#llx cbProbeLocs=%#x uVtgHdrAddr=%RTptr\n",
+ pVtgHdr->uProbeLocs.u64, pVtgHdr->uProbeLocsEnd.u64, pVtgHdr->offProbeLocs, pVtgHdr->cbProbeLocs, uVtgHdrAddr);
+ return VERR_SUPDRV_VTG_BAD_HDR_MISC;
+ }
+
+ if (pVtgHdr->cbProbes / sizeof(VTGDESCPROBE) != pVtgHdr->cbProbeEnabled / sizeof(uint32_t))
+ {
+ SUPR0Printf("supdrvVtgValidateHdr: VERR_SUPDRV_VTG_BAD_HDR_MISC - cbProbeEnabled=%#zx cbProbes=%#zx\n",
+ pVtgHdr->cbProbeEnabled, pVtgHdr->cbProbes);
+ return VERR_SUPDRV_VTG_BAD_HDR_MISC;
+ }
+
+ /*
+ * Check that there are no overlapping areas. This is a little bit ugly...
+ */
+ paAreas = (struct VTGAREAS const *)&pVtgHdr->offStrTab;
+ cAreas = pVtgHdr->offProbeLocs >= 0 ? 6 : 5;
+ off = sizeof(VTGOBJHDR);
+ for (i = 0; i < cAreas; i++)
+ {
+ if (paAreas[i].off < off)
+ {
+ SUPR0Printf("supdrvVtgValidateHdr: VERR_SUPDRV_VTG_BAD_HDR_MISC - overlapping areas %d and %d\n", i, i-1);
+ return VERR_SUPDRV_VTG_BAD_HDR_MISC;
+ }
+ off = paAreas[i].off + paAreas[i].cb;
+ }
+ if ( pVtgHdr->offProbeLocs > 0
+ && (uint32_t)-pVtgHdr->offProbeLocs < pVtgHdr->cbProbeLocs)
+ {
+ SUPR0Printf("supdrvVtgValidateHdr: VERR_SUPDRV_VTG_BAD_HDR_MISC - probe locations overlaps the header\n");
+ return VERR_SUPDRV_VTG_BAD_HDR_MISC;
+ }
+
+ /*
+ * Check that the object size is correct.
+ */
+ if (pVtgHdr->cbObj != pVtgHdr->offProbeEnabled + pVtgHdr->cbProbeEnabled)
+ {
+ SUPR0Printf("supdrvVtgValidateHdr: VERR_SUPDRV_VTG_BAD_HDR_MISC - bad header size %#x, expected %#x\n",
+ pVtgHdr->cbObj, pVtgHdr->offProbeEnabled + pVtgHdr->cbProbeEnabled);
+ return VERR_SUPDRV_VTG_BAD_HDR_MISC;
+ }
+
+
+ return VINF_SUCCESS;
+#undef MY_VALIDATE_OFF
+#undef MY_VALIDATE_SIZE
+}
+
+
+/**
+ * Validates the VTG data.
+ *
+ * @returns VBox status code.
+ * @param pVtgHdr The VTG object header of the data to validate.
+ * @param uVtgHdrAddr The address where the header is actually
+ * loaded.
+ * @param pbImage The image base. For validating the probe
+ * locations.
+ * @param cbImage The image size to go with @a pbImage.
+ * @param fUmod Whether this is a user module.
+ */
+static int supdrvVtgValidate(PVTGOBJHDR pVtgHdr, RTUINTPTR uVtgHdrAddr, const uint8_t *pbImage, size_t cbImage, bool fUmod)
+{
+ uintptr_t offTmp;
+ uintptr_t i;
+ uintptr_t cProviders;
+ int rc;
+
+ if (!pbImage || !cbImage)
+ {
+ pbImage = NULL;
+ cbImage = 0;
+ }
+
+#define MY_VALIDATE_STR(a_offStrTab) \
+ do { \
+ if ((a_offStrTab) >= pVtgHdr->cbStrTab) \
+ return VERR_SUPDRV_VTG_STRTAB_OFF; \
+ rc = supdrvVtgValidateString((char *)pVtgHdr + pVtgHdr->offStrTab + (a_offStrTab)); \
+ if (rc != VINF_SUCCESS) \
+ return rc; \
+ } while (0)
+#define MY_VALIDATE_ATTR(Attr) \
+ do { \
+ if ((Attr).u8Code <= (uint8_t)kVTGStability_Invalid || (Attr).u8Code >= (uint8_t)kVTGStability_End) \
+ return VERR_SUPDRV_VTG_BAD_ATTR; \
+ if ((Attr).u8Data <= (uint8_t)kVTGStability_Invalid || (Attr).u8Data >= (uint8_t)kVTGStability_End) \
+ return VERR_SUPDRV_VTG_BAD_ATTR; \
+ if ((Attr).u8DataDep <= (uint8_t)kVTGClass_Invalid || (Attr).u8DataDep >= (uint8_t)kVTGClass_End) \
+ return VERR_SUPDRV_VTG_BAD_ATTR; \
+ } while (0)
+
+ /*
+ * The header.
+ */
+ rc = supdrvVtgValidateHdr(pVtgHdr, uVtgHdrAddr, pbImage, cbImage, fUmod);
+ if (RT_FAILURE(rc))
+ return rc;
+
+ /*
+ * Validate the providers.
+ */
+ cProviders = i = pVtgHdr->cbProviders / sizeof(VTGDESCPROVIDER);
+ while (i-- > 0)
+ {
+ PCVTGDESCPROVIDER pProvider = (PCVTGDESCPROVIDER)((uintptr_t)pVtgHdr + pVtgHdr->offProviders) + i;
+
+ MY_VALIDATE_STR(pProvider->offName);
+ MY_CHECK_RET(pProvider->iFirstProbe < pVtgHdr->cbProbeEnabled / sizeof(uint32_t), VERR_SUPDRV_VTG_BAD_PROVIDER);
+ MY_CHECK_RET((uint32_t)pProvider->iFirstProbe + pProvider->cProbes <= pVtgHdr->cbProbeEnabled / sizeof(uint32_t),
+ VERR_SUPDRV_VTG_BAD_PROVIDER);
+ MY_VALIDATE_ATTR(pProvider->AttrSelf);
+ MY_VALIDATE_ATTR(pProvider->AttrModules);
+ MY_VALIDATE_ATTR(pProvider->AttrFunctions);
+ MY_VALIDATE_ATTR(pProvider->AttrNames);
+ MY_VALIDATE_ATTR(pProvider->AttrArguments);
+ MY_CHECK_RET(pProvider->bReserved == 0, VERR_SUPDRV_VTG_BAD_PROVIDER);
+ MY_CHECK_RET(pProvider->cProbesEnabled == 0, VERR_SUPDRV_VTG_BAD_PROVIDER);
+ MY_CHECK_RET(pProvider->uSettingsSerialNo == 0, VERR_SUPDRV_VTG_BAD_PROVIDER);
+ }
+
+ /*
+ * Validate probes.
+ */
+ i = pVtgHdr->cbProbes / sizeof(VTGDESCPROBE);
+ while (i-- > 0)
+ {
+ PCVTGDESCPROBE pProbe = (PCVTGDESCPROBE)( (uintptr_t)pVtgHdr + pVtgHdr->offProbes) + i;
+ PCVTGDESCPROVIDER pProvider = (PCVTGDESCPROVIDER)((uintptr_t)pVtgHdr + pVtgHdr->offProviders) + pProbe->idxProvider;
+ PCVTGDESCARGLIST pArgList = (PCVTGDESCARGLIST)( (uintptr_t)pVtgHdr + pVtgHdr->offArgLists + pProbe->offArgList );
+ unsigned iArg;
+ bool fHaveLargeArgs;
+
+
+ MY_VALIDATE_STR(pProbe->offName);
+ MY_CHECK_RET(pProbe->offArgList < pVtgHdr->cbArgLists, VERR_SUPDRV_VTG_BAD_PROBE);
+ MY_CHECK_RET((pProbe->offArgList & 3) == 0, VERR_SUPDRV_VTG_BAD_PROBE);
+ MY_CHECK_RET(pProbe->idxEnabled == i, VERR_SUPDRV_VTG_BAD_PROBE); /* The lists are parallel. */
+ MY_CHECK_RET(pProbe->idxProvider < cProviders, VERR_SUPDRV_VTG_BAD_PROBE);
+ MY_CHECK_RET(i - pProvider->iFirstProbe < pProvider->cProbes, VERR_SUPDRV_VTG_BAD_PROBE);
+ if (pProbe->offObjHdr != (intptr_t)pVtgHdr - (intptr_t)pProbe)
+ {
+ SUPR0Printf("supdrvVtgValidate: VERR_SUPDRV_VTG_BAD_PROBE - iProbe=%u offObjHdr=%d expected %zd\n",
+ i, pProbe->offObjHdr, (intptr_t)pVtgHdr - (intptr_t)pProbe);
+ return VERR_SUPDRV_VTG_BAD_PROBE;
+ }
+
+ /* The referenced argument list. */
+ if (pArgList->cArgs > 16)
+ {
+ SUPR0Printf("supdrvVtgValidate: VERR_SUPDRV_VTG_BAD_ARGLIST - iProbe=%u cArgs=%u\n", i, pArgList->cArgs);
+ return VERR_SUPDRV_VTG_BAD_ARGLIST;
+ }
+ if (pArgList->fHaveLargeArgs >= 2)
+ {
+ SUPR0Printf("supdrvVtgValidate: VERR_SUPDRV_VTG_BAD_ARGLIST - iProbe=%u fHaveLargeArgs=%d\n", i, pArgList->fHaveLargeArgs);
+ return VERR_SUPDRV_VTG_BAD_ARGLIST;
+ }
+ if ( pArgList->abReserved[0]
+ || pArgList->abReserved[1])
+ {
+ SUPR0Printf("supdrvVtgValidate: VERR_SUPDRV_VTG_BAD_ARGLIST - reserved MBZ iProbe=%u\n", i);
+ return VERR_SUPDRV_VTG_BAD_ARGLIST;
+ }
+ fHaveLargeArgs = false;
+ iArg = pArgList->cArgs;
+ while (iArg-- > 0)
+ {
+ uint32_t const fType = pArgList->aArgs[iArg].fType;
+ if (fType & ~VTG_TYPE_VALID_MASK)
+ {
+ SUPR0Printf("supdrvVtgValidate: VERR_SUPDRV_TRACER_BAD_ARG_FLAGS - fType=%#x iArg=%u iProbe=%u (#0)\n", fType, iArg, i);
+ return VERR_SUPDRV_TRACER_BAD_ARG_FLAGS;
+ }
+
+ switch (pArgList->aArgs[iArg].fType & VTG_TYPE_SIZE_MASK)
+ {
+ case 0:
+ if (pArgList->aArgs[iArg].fType & VTG_TYPE_FIXED_SIZED)
+ {
+ SUPR0Printf("supdrvVtgValidate: VERR_SUPDRV_TRACER_BAD_ARG_FLAGS - fType=%#x iArg=%u iProbe=%u (#1)\n", fType, iArg, i);
+ return VERR_SUPDRV_TRACER_BAD_ARG_FLAGS;
+ }
+ break;
+ case 1: case 2: case 4: case 8:
+ break;
+ default:
+ SUPR0Printf("supdrvVtgValidate: VERR_SUPDRV_TRACER_BAD_ARG_FLAGS - fType=%#x iArg=%u iProbe=%u (#2)\n", fType, iArg, i);
+ return VERR_SUPDRV_TRACER_BAD_ARG_FLAGS;
+ }
+ if (VTG_TYPE_IS_LARGE(pArgList->aArgs[iArg].fType))
+ fHaveLargeArgs = true;
+
+ MY_VALIDATE_STR(pArgList->aArgs[iArg].offType);
+ }
+ if ((uint8_t)fHaveLargeArgs != pArgList->fHaveLargeArgs)
+ {
+ SUPR0Printf("supdrvVtgValidate: VERR_SUPDRV_TRACER_BAD_ARG_FLAGS - iProbe=%u fHaveLargeArgs=%d expected %d\n",
+ i, pArgList->fHaveLargeArgs, fHaveLargeArgs);
+ return VERR_SUPDRV_VTG_BAD_PROBE;
+ }
+ }
+
+ /*
+ * Check that pacProbeEnabled is all zeros.
+ */
+ {
+ uint32_t const *pcProbeEnabled = (uint32_t const *)((uintptr_t)pVtgHdr + pVtgHdr->offProbeEnabled);
+ i = pVtgHdr->cbProbeEnabled / sizeof(uint32_t);
+ while (i-- > 0)
+ MY_CHECK_RET(pcProbeEnabled[0] == 0, VERR_SUPDRV_VTG_BAD_PROBE_ENABLED);
+ }
+
+ /*
+ * Probe locations.
+ */
+ {
+ PVTGPROBELOC paProbeLocs = (PVTGPROBELOC)((intptr_t)pVtgHdr + pVtgHdr->offProbeLocs);
+ i = pVtgHdr->cbProbeLocs / sizeof(VTGPROBELOC);
+ while (i-- > 0)
+ {
+ MY_CHECK_RET(paProbeLocs[i].uLine < _1G, VERR_SUPDRV_VTG_BAD_PROBE_LOC);
+ MY_CHECK_RET(paProbeLocs[i].fEnabled == false, VERR_SUPDRV_VTG_BAD_PROBE_LOC);
+ MY_CHECK_RET(paProbeLocs[i].idProbe == 0, VERR_SUPDRV_VTG_BAD_PROBE_LOC);
+ offTmp = (uintptr_t)paProbeLocs[i].pProbe - (uintptr_t)pVtgHdr->offProbes - (uintptr_t)pVtgHdr;
+#ifdef RT_OS_DARWIN /* See header validation code. */
+ if ( offTmp >= pVtgHdr->cbProbes
+ && pVtgHdr->u64VtgObjSectionStart != uVtgHdrAddr
+ && pVtgHdr->u64VtgObjSectionStart < _4M
+ && (uintptr_t)paProbeLocs[i].pProbe < _4M
+ && !fUmod )
+ {
+ uint64_t offDelta = uVtgHdrAddr - pVtgHdr->u64VtgObjSectionStart;
+
+ paProbeLocs[i].pProbe = (PVTGDESCPROBE)((uintptr_t)paProbeLocs[i].pProbe + offDelta);
+ if ((uintptr_t)paProbeLocs[i].pszFunction < _4M)
+ paProbeLocs[i].pszFunction = (const char *)((uintptr_t)paProbeLocs[i].pszFunction + offDelta);
+
+ offTmp += offDelta;
+ }
+#endif
+ MY_CHECK_RET(offTmp < pVtgHdr->cbProbes, VERR_SUPDRV_VTG_BAD_PROBE_LOC);
+ MY_CHECK_RET(offTmp / sizeof(VTGDESCPROBE) * sizeof(VTGDESCPROBE) == offTmp, VERR_SUPDRV_VTG_BAD_PROBE_LOC);
+ MY_WITHIN_IMAGE(paProbeLocs[i].pszFunction, VERR_SUPDRV_VTG_BAD_PROBE_LOC);
+ }
+ }
+
+ return VINF_SUCCESS;
+}
+
+#undef MY_VALIDATE_STR
+#undef MY_VALIDATE_ATTR
+#undef MY_WITHIN_IMAGE
+
+
+/**
+ * Gets a string from the string table.
+ *
+ * @returns Pointer to the string.
+ * @param pVtgHdr The VTG object header.
+ * @param offStrTab The string table offset.
+ */
+static const char *supdrvVtgGetString(PVTGOBJHDR pVtgHdr, uint32_t offStrTab)
+{
+ Assert(offStrTab < pVtgHdr->cbStrTab);
+ return (char *)pVtgHdr + pVtgHdr->offStrTab + offStrTab;
+}
+
+
+/**
+ * Frees the provider structure and associated resources.
+ *
+ * @param pProv The provider to free.
+ */
+static void supdrvTracerFreeProvider(PSUPDRVTPPROVIDER pProv)
+{
+ LOG_TRACER(("Freeing tracepoint provider '%s' / %p\n", pProv->szName, pProv->Core.TracerData.DTrace.idProvider));
+ pProv->fRegistered = false;
+ pProv->fZombie = true;
+ pProv->Core.pDesc = NULL;
+ pProv->Core.pHdr = NULL;
+ pProv->Core.paProbeLocsRO = NULL;
+ pProv->Core.pvProbeLocsEn = NULL;
+ pProv->Core.pacProbeEnabled = NULL;
+ pProv->Core.paR0ProbeLocs = NULL;
+ pProv->Core.paR0Probes = NULL;
+ RT_ZERO(pProv->Core.TracerData);
+ RTMemFree(pProv);
+}
+
+
+/**
+ * Unlinks and deregisters a provider.
+ *
+ * If the provider is still busy, it will be put in the zombie list.
+ *
+ * @param pDevExt The device extension.
+ * @param pProv The provider.
+ *
+ * @remarks The caller owns mtxTracer.
+ */
+static void supdrvTracerDeregisterVtgObj(PSUPDRVDEVEXT pDevExt, PSUPDRVTPPROVIDER pProv)
+{
+ int rc;
+
+ RTListNodeRemove(&pProv->ListEntry);
+ if (pProv->pSession)
+ {
+ RTListNodeRemove(&pProv->SessionListEntry);
+ RTListInit(&pProv->SessionListEntry);
+ pProv->pSession->cTpProviders--;
+ }
+
+ if (!pProv->fRegistered || !pDevExt->pTracerOps)
+ rc = VINF_SUCCESS;
+ else
+ rc = pDevExt->pTracerOps->pfnProviderDeregister(pDevExt->pTracerOps, &pProv->Core);
+ if (RT_SUCCESS(rc))
+ {
+ supdrvTracerFreeProvider(pProv);
+ return;
+ }
+
+ pProv->fZombie = true;
+ pProv->pImage = NULL;
+ pProv->pSession = NULL;
+ pProv->pUmod = NULL;
+ pProv->Core.pDesc = NULL;
+ pProv->Core.pHdr = NULL;
+ pProv->Core.paProbeLocsRO = NULL;
+ pProv->Core.pvProbeLocsEn = NULL;
+ pProv->Core.pacProbeEnabled = NULL;
+ pProv->Core.paR0ProbeLocs = NULL;
+
+ RTListAppend(&pDevExt->TracerProviderZombieList, &pProv->ListEntry);
+ LOG_TRACER(("Invalidated provider '%s' / %p and put it on the zombie list (rc=%Rrc)\n",
+ pProv->szName, pProv->Core.TracerData.DTrace.idProvider, rc));
+}
+
+
+/**
+ * Processes the zombie list.
+ *
+ * @param pDevExt The device extension.
+ */
+static void supdrvTracerProcessZombies(PSUPDRVDEVEXT pDevExt)
+{
+ PSUPDRVTPPROVIDER pProv, pProvNext;
+
+ RTSemFastMutexRequest(pDevExt->mtxTracer);
+ RTListForEachSafe(&pDevExt->TracerProviderZombieList, pProv, pProvNext, SUPDRVTPPROVIDER, ListEntry)
+ {
+ int rc = pDevExt->pTracerOps->pfnProviderDeregisterZombie(pDevExt->pTracerOps, &pProv->Core);
+ if (RT_SUCCESS(rc))
+ {
+ RTListNodeRemove(&pProv->ListEntry);
+ supdrvTracerFreeProvider(pProv);
+ }
+ }
+ RTSemFastMutexRelease(pDevExt->mtxTracer);
+}
+
+
+/**
+ * Unregisters all providers, including zombies, waiting for busy providers to
+ * go idle and unregister smoothly.
+ *
+ * This may block.
+ *
+ * @param pDevExt The device extension.
+ */
+static void supdrvTracerRemoveAllProviders(PSUPDRVDEVEXT pDevExt)
+{
+ uint32_t i;
+ PSUPDRVTPPROVIDER pProv;
+ PSUPDRVTPPROVIDER pProvNext;
+
+ /*
+ * Unregister all probes (there should only be one).
+ */
+ RTSemFastMutexRequest(pDevExt->mtxTracer);
+ RTListForEachSafe(&pDevExt->TracerProviderList, pProv, pProvNext, SUPDRVTPPROVIDER, ListEntry)
+ {
+ supdrvTracerDeregisterVtgObj(pDevExt, pProv);
+ }
+ RTSemFastMutexRelease(pDevExt->mtxTracer);
+
+ /*
+ * Try unregister zombies now, sleep on busy ones and tracer opens.
+ */
+ for (i = 0; ; i++)
+ {
+ bool fEmpty;
+
+ RTSemFastMutexRequest(pDevExt->mtxTracer);
+
+ /* Zombies */
+ RTListForEachSafe(&pDevExt->TracerProviderZombieList, pProv, pProvNext, SUPDRVTPPROVIDER, ListEntry)
+ {
+ int rc;
+ LOG_TRACER(("supdrvTracerRemoveAllProviders: Attemting to unregister '%s' / %p...\n",
+ pProv->szName, pProv->Core.TracerData.DTrace.idProvider));
+
+ if (pDevExt->pTracerOps)
+ rc = pDevExt->pTracerOps->pfnProviderDeregisterZombie(pDevExt->pTracerOps, &pProv->Core);
+ else
+ rc = VINF_SUCCESS;
+ if (!rc)
+ {
+ RTListNodeRemove(&pProv->ListEntry);
+ supdrvTracerFreeProvider(pProv);
+ }
+ else if (!(i & 0xf))
+ SUPR0Printf("supdrvTracerRemoveAllProviders: Waiting on busy provider '%s' / %p (rc=%d)\n",
+ pProv->szName, pProv->Core.TracerData.DTrace.idProvider, rc);
+ else
+ LOG_TRACER(("supdrvTracerRemoveAllProviders: Failed to unregister provider '%s' / %p - rc=%d\n",
+ pProv->szName, pProv->Core.TracerData.DTrace.idProvider, rc));
+ }
+
+ fEmpty = RTListIsEmpty(&pDevExt->TracerProviderZombieList);
+
+ /* Tracer opens. */
+ if ( pDevExt->cTracerOpens
+ && pDevExt->pTracerOps)
+ {
+ fEmpty = false;
+ if (!(i & 0xf))
+ SUPR0Printf("supdrvTracerRemoveAllProviders: Waiting on %u opens\n", pDevExt->cTracerOpens);
+ else
+ LOG_TRACER(("supdrvTracerRemoveAllProviders: Waiting on %u opens\n", pDevExt->cTracerOpens));
+ }
+
+ RTSemFastMutexRelease(pDevExt->mtxTracer);
+
+ if (fEmpty)
+ break;
+
+ /* Delay...*/
+ RTThreadSleep(1000);
+ }
+}
+
+
+/**
+ * Registers the VTG tracepoint providers of a driver.
+ *
+ * @returns VBox status code.
+ * @param pDevExt The device instance data.
+ * @param pVtgHdr The VTG object header.
+ * @param pImage The image if applicable.
+ * @param pSession The session if applicable.
+ * @param pUmod The associated user tracepoint module if
+ * applicable.
+ * @param pszModName The module name.
+ */
+static int supdrvTracerRegisterVtgObj(PSUPDRVDEVEXT pDevExt, PVTGOBJHDR pVtgHdr, PSUPDRVLDRIMAGE pImage,
+ PSUPDRVSESSION pSession, PSUPDRVTRACERUMOD pUmod, const char *pszModName)
+{
+ int rc;
+ uintptr_t i;
+ PSUPDRVTPPROVIDER pProv;
+ size_t cchModName;
+
+ /*
+ * Validate input.
+ */
+ AssertPtrReturn(pDevExt, VERR_INVALID_POINTER);
+ AssertPtrReturn(pVtgHdr, VERR_INVALID_POINTER);
+ AssertPtrNullReturn(pImage, VERR_INVALID_POINTER);
+ AssertPtrNullReturn(pSession, VERR_INVALID_POINTER);
+ AssertPtrReturn(pszModName, VERR_INVALID_POINTER);
+ cchModName = strlen(pszModName);
+
+ if (pImage)
+ rc = supdrvVtgValidate(pVtgHdr, (uintptr_t)pVtgHdr,
+ (const uint8_t *)pImage->pvImage, pImage->cbImageBits,
+ false /*fUmod*/);
+ else
+ rc = supdrvVtgValidate(pVtgHdr, (uintptr_t)pVtgHdr, NULL, 0, pUmod != NULL);
+ if (RT_FAILURE(rc))
+ return rc;
+
+ /*
+ * Check that there aren't any obvious duplicates.
+ * (Yes, this isn't race free, but it's good enough for now.)
+ */
+ rc = RTSemFastMutexRequest(pDevExt->mtxTracer);
+ if (RT_FAILURE(rc))
+ return rc;
+ if (pImage || !pSession || pSession->R0Process == NIL_RTPROCESS)
+ {
+ RTListForEach(&pDevExt->TracerProviderList, pProv, SUPDRVTPPROVIDER, ListEntry)
+ {
+ if (pProv->Core.pHdr == pVtgHdr)
+ {
+ rc = VERR_SUPDRV_VTG_ALREADY_REGISTERED;
+ break;
+ }
+
+ if ( pProv->pSession == pSession
+ && pProv->pImage == pImage)
+ {
+ rc = VERR_SUPDRV_VTG_ONLY_ONCE_PER_SESSION;
+ break;
+ }
+ }
+ }
+ else
+ {
+ RTListForEach(&pSession->TpProviders, pProv, SUPDRVTPPROVIDER, SessionListEntry)
+ {
+ if (pProv->Core.pHdr == pVtgHdr)
+ {
+ rc = VERR_SUPDRV_VTG_ALREADY_REGISTERED;
+ break;
+ }
+ }
+ }
+ RTSemFastMutexRelease(pDevExt->mtxTracer);
+ if (RT_FAILURE(rc))
+ return rc;
+
+ /*
+ * Register the providers.
+ */
+ i = pVtgHdr->cbProviders / sizeof(VTGDESCPROVIDER);
+ while (i-- > 0)
+ {
+ PVTGDESCPROVIDER pDesc = (PVTGDESCPROVIDER)((uintptr_t)pVtgHdr + pVtgHdr->offProviders) + i;
+ const char *pszName = supdrvVtgGetString(pVtgHdr, pDesc->offName);
+ size_t const cchName = strlen(pszName) + (pUmod ? 16 : 0);
+
+ pProv = (PSUPDRVTPPROVIDER)RTMemAllocZ(RT_UOFFSETOF_DYN(SUPDRVTPPROVIDER, szName[cchName + 1 + cchModName + 1]));
+ if (pProv)
+ {
+ pProv->Core.pszName = &pProv->szName[0];
+ pProv->Core.pszModName = &pProv->szName[cchName + 1];
+ pProv->Core.pDesc = pDesc;
+ pProv->Core.pHdr = pVtgHdr;
+ pProv->Core.paProbeLocsRO = (PCVTGPROBELOC )((uintptr_t)pVtgHdr + pVtgHdr->offProbeLocs);
+ if (!pUmod)
+ {
+ pProv->Core.pvProbeLocsEn = (void *)((uintptr_t)pVtgHdr + pVtgHdr->offProbeLocs);
+ pProv->Core.pacProbeEnabled = (uint32_t *)((uintptr_t)pVtgHdr + pVtgHdr->offProbeEnabled);
+ pProv->Core.paR0ProbeLocs = NULL;
+ pProv->Core.paR0Probes = NULL;
+ pProv->Core.cbProbeLocsEn = sizeof(VTGPROBELOC);
+ pProv->Core.cBits = ARCH_BITS;
+ pProv->Core.fUmod = false;
+ }
+ else
+ {
+ pProv->Core.pvProbeLocsEn = pUmod->pvProbeLocs;
+ pProv->Core.pacProbeEnabled = pUmod->pacProbeEnabled;
+ pProv->Core.paR0ProbeLocs = &pUmod->aProbeLocs[0];
+ pProv->Core.paR0Probes = (PSUPDRVPROBEINFO)&pUmod->aProbeLocs[pUmod->cProbeLocs];
+ pProv->Core.cbProbeLocsEn = pUmod->cbProbeLoc;
+ pProv->Core.cBits = pUmod->cBits;
+ pProv->Core.fUmod = true;
+ }
+ pProv->pImage = pImage;
+ pProv->pSession = pSession;
+ pProv->pUmod = pUmod;
+ pProv->fZombie = false;
+ pProv->fRegistered = true;
+
+ if (!pUmod)
+ RT_BCOPY_UNFORTIFIED(pProv->szName, pszName, cchName + 1);
+ else
+ RTStrPrintf(pProv->szName, cchName + 1, "%s%u", pszName, (uint32_t)pSession->Process);
+ RT_BCOPY_UNFORTIFIED((void *)pProv->Core.pszModName, pszModName, cchModName + 1);
+
+ /*
+ * Do the actual registration and list manipulations while holding
+ * down the lock.
+ */
+ rc = RTSemFastMutexRequest(pDevExt->mtxTracer);
+ if (RT_SUCCESS(rc))
+ {
+ if ( pDevExt->pTracerOps
+ && !pDevExt->fTracerUnloading)
+ rc = pDevExt->pTracerOps->pfnProviderRegister(pDevExt->pTracerOps, &pProv->Core);
+ else
+ {
+ pProv->fRegistered = false;
+ rc = VINF_SUCCESS;
+ }
+ if (RT_SUCCESS(rc))
+ {
+ RTListAppend(&pDevExt->TracerProviderList, &pProv->ListEntry);
+ if (pSession)
+ {
+ RTListAppend(&pSession->TpProviders, &pProv->SessionListEntry);
+ pSession->cTpProviders++;
+ }
+ else
+ RTListInit(&pProv->SessionListEntry);
+ RTSemFastMutexRelease(pDevExt->mtxTracer);
+ LOG_TRACER(("Registered tracepoint provider '%s' in '%s' -> %p\n",
+ pProv->szName, pszModName, pProv->Core.TracerData.DTrace.idProvider));
+ }
+ else
+ {
+ RTSemFastMutexRelease(pDevExt->mtxTracer);
+ LOG_TRACER(("Failed to register tracepoint provider '%s' in '%s' -> %Rrc\n",
+ pProv->szName, pszModName, rc));
+ }
+ }
+ }
+ else
+ rc = VERR_NO_MEMORY;
+
+ /*
+ * In case of failure, we have to undo any providers we already
+ * managed to register.
+ */
+ if (RT_FAILURE(rc))
+ {
+ PSUPDRVTPPROVIDER pProvNext;
+
+ if (pProv)
+ supdrvTracerFreeProvider(pProv);
+
+ RTSemFastMutexRequest(pDevExt->mtxTracer);
+ if (pImage)
+ {
+ RTListForEachReverseSafe(&pDevExt->TracerProviderList, pProv, pProvNext, SUPDRVTPPROVIDER, ListEntry)
+ {
+ if (pProv->Core.pHdr == pVtgHdr)
+ supdrvTracerDeregisterVtgObj(pDevExt, pProv);
+ }
+ }
+ else
+ {
+ RTListForEachSafe(&pSession->TpProviders, pProv, pProvNext, SUPDRVTPPROVIDER, SessionListEntry)
+ {
+ if (pProv->Core.pHdr == pVtgHdr)
+ supdrvTracerDeregisterVtgObj(pDevExt, pProv);
+ }
+ }
+ RTSemFastMutexRelease(pDevExt->mtxTracer);
+ return rc;
+ }
+ }
+
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * Registers the VTG tracepoint providers of a driver.
+ *
+ * @returns VBox status code.
+ * @param pSession The support driver session handle.
+ * @param pVtgHdr The VTG header.
+ * @param pszName The driver name.
+ */
+SUPR0DECL(int) SUPR0TracerRegisterDrv(PSUPDRVSESSION pSession, PVTGOBJHDR pVtgHdr, const char *pszName)
+{
+ int rc;
+
+ AssertReturn(SUP_IS_SESSION_VALID(pSession), VERR_INVALID_PARAMETER);
+ AssertPtrReturn(pszName, VERR_INVALID_POINTER);
+ AssertPtrReturn(pVtgHdr, VERR_INVALID_POINTER);
+ AssertReturn(pSession->R0Process == NIL_RTR0PROCESS, VERR_INVALID_PARAMETER);
+ LOG_TRACER(("SUPR0TracerRegisterDrv: pSession=%p pVtgHdr=%p pszName=%s\n", pSession, pVtgHdr, pszName));
+
+ rc = supdrvTracerRegisterVtgObj(pSession->pDevExt, pVtgHdr, NULL /*pImage*/, pSession, NULL /*pUmod*/, pszName);
+
+ /*
+ * Try unregister zombies while we have a chance.
+ */
+ supdrvTracerProcessZombies(pSession->pDevExt);
+
+ return rc;
+}
+SUPR0_EXPORT_SYMBOL(SUPR0TracerRegisterDrv);
+
+
+/**
+ * Deregister the VTG tracepoint providers of a driver.
+ *
+ * @param pSession The support driver session handle.
+ */
+SUPR0DECL(void) SUPR0TracerDeregisterDrv(PSUPDRVSESSION pSession)
+{
+ PSUPDRVTPPROVIDER pProv, pProvNext;
+ PSUPDRVDEVEXT pDevExt;
+ AssertReturnVoid(SUP_IS_SESSION_VALID(pSession));
+ AssertReturnVoid(pSession->R0Process == NIL_RTR0PROCESS);
+ LOG_TRACER(("SUPR0TracerDeregisterDrv: pSession=%p\n", pSession));
+
+ pDevExt = pSession->pDevExt;
+
+ /*
+ * Search for providers belonging to this driver session.
+ */
+ RTSemFastMutexRequest(pDevExt->mtxTracer);
+ RTListForEachSafe(&pSession->TpProviders, pProv, pProvNext, SUPDRVTPPROVIDER, SessionListEntry)
+ {
+ supdrvTracerDeregisterVtgObj(pDevExt, pProv);
+ }
+ RTSemFastMutexRelease(pDevExt->mtxTracer);
+
+ /*
+ * Try unregister zombies while we have a chance.
+ */
+ supdrvTracerProcessZombies(pDevExt);
+}
+SUPR0_EXPORT_SYMBOL(SUPR0TracerDeregisterDrv);
+
+
+/**
+ * Registers the VTG tracepoint providers of a module loaded by
+ * the support driver.
+ *
+ * This should be called from the ModuleInit code.
+ *
+ * @returns VBox status code.
+ * @param hMod The module handle.
+ * @param pVtgHdr The VTG header.
+ */
+SUPR0DECL(int) SUPR0TracerRegisterModule(void *hMod, PVTGOBJHDR pVtgHdr)
+{
+ PSUPDRVLDRIMAGE pImage = (PSUPDRVLDRIMAGE)hMod;
+ PSUPDRVDEVEXT pDevExt;
+ int rc;
+
+ LOG_TRACER(("SUPR0TracerRegisterModule: %p\n", pVtgHdr));
+
+ /*
+ * Validate input and context.
+ */
+ AssertPtrReturn(pImage, VERR_INVALID_HANDLE);
+ AssertPtrReturn(pVtgHdr, VERR_INVALID_POINTER);
+
+ AssertPtrReturn(pImage, VERR_INVALID_POINTER);
+ pDevExt = pImage->pDevExt;
+ AssertPtrReturn(pDevExt, VERR_INVALID_POINTER);
+ AssertReturn(pDevExt->pLdrInitImage == pImage, VERR_WRONG_ORDER);
+ AssertReturn(pDevExt->hLdrInitThread == RTThreadNativeSelf(), VERR_WRONG_ORDER);
+ AssertReturn((uintptr_t)pVtgHdr - (uintptr_t)pImage->pvImage < pImage->cbImageBits, VERR_INVALID_PARAMETER);
+
+ /*
+ * Do the job.
+ */
+ rc = supdrvTracerRegisterVtgObj(pDevExt, pVtgHdr, pImage, NULL /*pSession*/, NULL /*pUmod*/, pImage->szName);
+ LOG_TRACER(("SUPR0TracerRegisterModule: rc=%d\n", rc));
+
+ /*
+ * Try unregister zombies while we have a chance.
+ */
+ supdrvTracerProcessZombies(pDevExt);
+
+ return rc;
+}
+SUPR0_EXPORT_SYMBOL(SUPR0TracerRegisterModule);
+
+
+/**
+ * Registers the tracer implementation.
+ *
+ * This should be called from the ModuleInit code or from a ring-0 session.
+ *
+ * @returns VBox status code.
+ * @param hMod The module handle.
+ * @param pSession Ring-0 session handle.
+ * @param pReg Pointer to the tracer registration structure.
+ * @param ppHlp Where to return the tracer helper method table.
+ */
+SUPR0DECL(int) SUPR0TracerRegisterImpl(void *hMod, PSUPDRVSESSION pSession, PCSUPDRVTRACERREG pReg, PCSUPDRVTRACERHLP *ppHlp)
+{
+ PSUPDRVLDRIMAGE pImage = (PSUPDRVLDRIMAGE)hMod;
+ PSUPDRVDEVEXT pDevExt;
+ PSUPDRVTPPROVIDER pProv;
+ int rc;
+ int rc2;
+
+ /*
+ * Validate input and context.
+ */
+ AssertPtrReturn(ppHlp, VERR_INVALID_POINTER);
+ *ppHlp = NULL;
+ AssertPtrReturn(pReg, VERR_INVALID_HANDLE);
+
+ if (pImage)
+ {
+ AssertPtrReturn(pImage, VERR_INVALID_POINTER);
+ AssertReturn(pSession == NULL, VERR_INVALID_PARAMETER);
+ pDevExt = pImage->pDevExt;
+ AssertPtrReturn(pDevExt, VERR_INVALID_POINTER);
+ AssertReturn(pDevExt->pLdrInitImage == pImage, VERR_WRONG_ORDER);
+ AssertReturn(pDevExt->hLdrInitThread == RTThreadNativeSelf(), VERR_WRONG_ORDER);
+ }
+ else
+ {
+ AssertReturn(SUP_IS_SESSION_VALID(pSession), VERR_INVALID_PARAMETER);
+ AssertReturn(pSession->R0Process == NIL_RTR0PROCESS, VERR_INVALID_PARAMETER);
+ pDevExt = pSession->pDevExt;
+ AssertPtrReturn(pDevExt, VERR_INVALID_POINTER);
+ }
+
+ AssertReturn(pReg->u32Magic == SUPDRVTRACERREG_MAGIC, VERR_INVALID_MAGIC);
+ AssertReturn(pReg->u32Version == SUPDRVTRACERREG_VERSION, VERR_VERSION_MISMATCH);
+ AssertReturn(pReg->uEndMagic == SUPDRVTRACERREG_MAGIC, VERR_VERSION_MISMATCH);
+ AssertPtrReturn(pReg->pfnProbeFireKernel, VERR_INVALID_POINTER);
+ AssertPtrReturn(pReg->pfnProbeFireUser, VERR_INVALID_POINTER);
+ AssertPtrReturn(pReg->pfnTracerOpen, VERR_INVALID_POINTER);
+ AssertPtrReturn(pReg->pfnTracerIoCtl, VERR_INVALID_POINTER);
+ AssertPtrReturn(pReg->pfnTracerClose, VERR_INVALID_POINTER);
+ AssertPtrReturn(pReg->pfnProviderRegister, VERR_INVALID_POINTER);
+ AssertPtrReturn(pReg->pfnProviderDeregister, VERR_INVALID_POINTER);
+ AssertPtrReturn(pReg->pfnProviderDeregisterZombie, VERR_INVALID_POINTER);
+
+ /*
+ * Do the job.
+ */
+ rc = RTSemFastMutexRequest(pDevExt->mtxTracer);
+ if (RT_SUCCESS(rc))
+ {
+ if (!pDevExt->pTracerOps)
+ {
+ LOG_TRACER(("SUPR0TracerRegisterImpl: pReg=%p\n", pReg));
+ pDevExt->pTracerOps = pReg;
+ pDevExt->pTracerSession = pSession;
+ pDevExt->pTracerImage = pImage;
+
+ g_pfnSupdrvProbeFireKernel = (PFNRT)pDevExt->pTracerOps->pfnProbeFireKernel;
+
+ *ppHlp = &pDevExt->TracerHlp;
+ rc = VINF_SUCCESS;
+
+ /*
+ * Iterate the already loaded modules and register their providers.
+ */
+ RTListForEach(&pDevExt->TracerProviderList, pProv, SUPDRVTPPROVIDER, ListEntry)
+ {
+ Assert(!pProv->fRegistered);
+ pProv->fRegistered = true;
+ rc2 = pDevExt->pTracerOps->pfnProviderRegister(pDevExt->pTracerOps, &pProv->Core);
+ if (RT_FAILURE(rc2))
+ {
+ pProv->fRegistered = false;
+ SUPR0Printf("SUPR0TracerRegisterImpl: Failed to register provider %s::%s - rc=%d\n",
+ pProv->Core.pszModName, pProv->szName, rc2);
+ }
+ }
+ }
+ else
+ rc = VERR_SUPDRV_TRACER_ALREADY_REGISTERED;
+ RTSemFastMutexRelease(pDevExt->mtxTracer);
+ }
+
+ return rc;
+
+}
+SUPR0_EXPORT_SYMBOL(SUPR0TracerRegisterImpl);
+
+
+/**
+ * Common tracer implementation deregistration code.
+ *
+ * The caller sets fTracerUnloading prior to calling this function.
+ *
+ * @param pDevExt The device extension structure.
+ */
+static void supdrvTracerCommonDeregisterImpl(PSUPDRVDEVEXT pDevExt)
+{
+ uint32_t i;
+ PSUPDRVTPPROVIDER pProv;
+ PSUPDRVTPPROVIDER pProvNext;
+
+ RTSemFastMutexRequest(pDevExt->mtxTracer);
+
+ /*
+ * Reinstall the stub probe-fire function.
+ */
+ g_pfnSupdrvProbeFireKernel = supdrvTracerProbeFireStub;
+
+ /*
+ * Disassociate the tracer implementation from all providers.
+ * We will have to wait on busy providers.
+ */
+ for (i = 0; ; i++)
+ {
+ uint32_t cZombies = 0;
+
+ /* Live providers. */
+ RTListForEachSafe(&pDevExt->TracerProviderList, pProv, pProvNext, SUPDRVTPPROVIDER, ListEntry)
+ {
+ int rc;
+ LOG_TRACER(("supdrvTracerCommonDeregisterImpl: Attemting to unregister '%s' / %p...\n",
+ pProv->szName, pProv->Core.TracerData.DTrace.idProvider));
+
+ if (!pProv->fRegistered)
+ continue;
+ if (!pProv->fZombie)
+ {
+ rc = pDevExt->pTracerOps->pfnProviderDeregister(pDevExt->pTracerOps, &pProv->Core);
+ if (RT_FAILURE(rc))
+ pProv->fZombie = true;
+ }
+ else
+ rc = pDevExt->pTracerOps->pfnProviderDeregisterZombie(pDevExt->pTracerOps, &pProv->Core);
+ if (RT_SUCCESS(rc))
+ pProv->fZombie = pProv->fRegistered = false;
+ else
+ {
+ cZombies++;
+ if (!(i & 0xf))
+ SUPR0Printf("supdrvTracerCommonDeregisterImpl: Waiting on busy provider '%s' / %p (rc=%d)\n",
+ pProv->szName, pProv->Core.TracerData.DTrace.idProvider, rc);
+ else
+ LOG_TRACER(("supdrvTracerCommonDeregisterImpl: Failed to unregister provider '%s' / %p - rc=%d\n",
+ pProv->szName, pProv->Core.TracerData.DTrace.idProvider, rc));
+ }
+ }
+
+ /* Zombies providers. */
+ RTListForEachSafe(&pDevExt->TracerProviderZombieList, pProv, pProvNext, SUPDRVTPPROVIDER, ListEntry)
+ {
+ int rc;
+ LOG_TRACER(("supdrvTracerCommonDeregisterImpl: Attemting to unregister '%s' / %p (zombie)...\n",
+ pProv->szName, pProv->Core.TracerData.DTrace.idProvider));
+
+ rc = pDevExt->pTracerOps->pfnProviderDeregisterZombie(pDevExt->pTracerOps, &pProv->Core);
+ if (RT_SUCCESS(rc))
+ {
+ RTListNodeRemove(&pProv->ListEntry);
+ supdrvTracerFreeProvider(pProv);
+ }
+ else
+ {
+ cZombies++;
+ if (!(i & 0xf))
+ SUPR0Printf("supdrvTracerCommonDeregisterImpl: Waiting on busy provider '%s' / %p (rc=%d)\n",
+ pProv->szName, pProv->Core.TracerData.DTrace.idProvider, rc);
+ else
+ LOG_TRACER(("supdrvTracerCommonDeregisterImpl: Failed to unregister provider '%s' / %p - rc=%d\n",
+ pProv->szName, pProv->Core.TracerData.DTrace.idProvider, rc));
+ }
+ }
+
+ /* Tracer opens. */
+ if (pDevExt->cTracerOpens)
+ {
+ cZombies++;
+ if (!(i & 0xf))
+ SUPR0Printf("supdrvTracerCommonDeregisterImpl: Waiting on %u opens\n", pDevExt->cTracerOpens);
+ else
+ LOG_TRACER(("supdrvTracerCommonDeregisterImpl: Waiting on %u opens\n", pDevExt->cTracerOpens));
+ }
+
+ /* Tracer calls. */
+ if (pDevExt->cTracerCallers)
+ {
+ cZombies++;
+ if (!(i & 0xf))
+ SUPR0Printf("supdrvTracerCommonDeregisterImpl: Waiting on %u callers\n", pDevExt->cTracerCallers);
+ else
+ LOG_TRACER(("supdrvTracerCommonDeregisterImpl: Waiting on %u callers\n", pDevExt->cTracerCallers));
+ }
+
+ /* Done? */
+ if (cZombies == 0)
+ break;
+
+ /* Delay...*/
+ RTSemFastMutexRelease(pDevExt->mtxTracer);
+ RTThreadSleep(1000);
+ RTSemFastMutexRequest(pDevExt->mtxTracer);
+ }
+
+ /*
+ * Deregister the tracer implementation.
+ */
+ pDevExt->pTracerImage = NULL;
+ pDevExt->pTracerSession = NULL;
+ pDevExt->pTracerOps = NULL;
+ pDevExt->fTracerUnloading = false;
+
+ RTSemFastMutexRelease(pDevExt->mtxTracer);
+}
+
+
+/**
+ * Deregister a tracer implementation.
+ *
+ * This should be called from the ModuleTerm code or from a ring-0 session.
+ *
+ * @returns VBox status code.
+ * @param hMod The module handle.
+ * @param pSession Ring-0 session handle.
+ */
+SUPR0DECL(int) SUPR0TracerDeregisterImpl(void *hMod, PSUPDRVSESSION pSession)
+{
+ PSUPDRVLDRIMAGE pImage = (PSUPDRVLDRIMAGE)hMod;
+ PSUPDRVDEVEXT pDevExt;
+ int rc;
+
+ /*
+ * Validate input and context.
+ */
+ if (pImage)
+ {
+ AssertPtrReturn(pImage, VERR_INVALID_POINTER);
+ AssertReturn(pSession == NULL, VERR_INVALID_PARAMETER);
+ pDevExt = pImage->pDevExt;
+ }
+ else
+ {
+ AssertReturn(SUP_IS_SESSION_VALID(pSession), VERR_INVALID_PARAMETER);
+ AssertReturn(pSession->R0Process == NIL_RTR0PROCESS, VERR_INVALID_PARAMETER);
+ pDevExt = pSession->pDevExt;
+ }
+ AssertPtrReturn(pDevExt, VERR_INVALID_POINTER);
+
+ /*
+ * Do the job.
+ */
+ rc = RTSemFastMutexRequest(pDevExt->mtxTracer);
+ if (RT_SUCCESS(rc))
+ {
+ if ( pImage
+ ? pDevExt->pTracerImage == pImage
+ : pDevExt->pTracerSession == pSession)
+ {
+ LOG_TRACER(("SUPR0TracerDeregisterImpl: Unloading ...\n"));
+ pDevExt->fTracerUnloading = true;
+ RTSemFastMutexRelease(pDevExt->mtxTracer);
+ supdrvTracerCommonDeregisterImpl(pDevExt);
+ LOG_TRACER(("SUPR0TracerDeregisterImpl: ... done.\n"));
+ }
+ else
+ {
+ rc = VERR_SUPDRV_TRACER_NOT_REGISTERED;
+ RTSemFastMutexRelease(pDevExt->mtxTracer);
+ }
+ }
+
+ return rc;
+}
+SUPR0_EXPORT_SYMBOL(SUPR0TracerDeregisterImpl);
+
+
+/*
+ * The probe function is a bit more fun since we need tail jump optimizating.
+ *
+ * Since we cannot ship yasm sources for linux and freebsd, owing to the cursed
+ * rebuilding of the kernel module from scratch at install time, we have to
+ * deploy some ugly gcc inline assembly here.
+ */
+#if defined(__GNUC__) && (defined(RT_OS_FREEBSD) || defined(RT_OS_LINUX))
+__asm__("\
+ .section .text \n\
+ \n\
+ .p2align 4 \n\
+ .global SUPR0TracerFireProbe \n\
+ .type SUPR0TracerFireProbe, @function \n\
+SUPR0TracerFireProbe: \n\
+");
+# if defined(RT_ARCH_AMD64)
+__asm__("\
+ movq g_pfnSupdrvProbeFireKernel(%rip), %rax \n\
+ jmp *%rax \n\
+");
+# elif defined(RT_ARCH_X86)
+__asm__("\
+ movl g_pfnSupdrvProbeFireKernel, %eax \n\
+ jmp *%eax \n\
+");
+# else
+# error "Which arch is this?"
+# endif
+__asm__("\
+ .size SUPR0TracerFireProbe, . - SUPR0TracerFireProbe \n\
+ \n\
+ .type supdrvTracerProbeFireStub,@function \n\
+ .global supdrvTracerProbeFireStub \n\
+supdrvTracerProbeFireStub: \n\
+ ret \n\
+ .size supdrvTracerProbeFireStub, . - supdrvTracerProbeFireStub \n\
+ \n\
+ .previous \n\
+");
+# if 0 /* Slickedit on windows highlighting fix */
+ )
+# endif
+#endif
+SUPR0_EXPORT_SYMBOL(SUPR0TracerFireProbe);
+
+
+/**
+ * Module unloading hook, called after execution in the module have ceased.
+ *
+ * @param pDevExt The device extension structure.
+ * @param pImage The image being unloaded.
+ */
+void VBOXCALL supdrvTracerModuleUnloading(PSUPDRVDEVEXT pDevExt, PSUPDRVLDRIMAGE pImage)
+{
+ PSUPDRVTPPROVIDER pProv, pProvNext;
+ AssertPtrReturnVoid(pImage); /* paranoia */
+
+ RTSemFastMutexRequest(pDevExt->mtxTracer);
+
+ /*
+ * If it is the tracer image, we have to unload all the providers.
+ */
+ if (pDevExt->pTracerImage == pImage)
+ {
+ LOG_TRACER(("supdrvTracerModuleUnloading: Unloading tracer ...\n"));
+ pDevExt->fTracerUnloading = true;
+ RTSemFastMutexRelease(pDevExt->mtxTracer);
+ supdrvTracerCommonDeregisterImpl(pDevExt);
+ LOG_TRACER(("supdrvTracerModuleUnloading: ... done.\n"));
+ }
+ else
+ {
+ /*
+ * Unregister all providers belonging to this image.
+ */
+ RTListForEachSafe(&pDevExt->TracerProviderList, pProv, pProvNext, SUPDRVTPPROVIDER, ListEntry)
+ {
+ if (pProv->pImage == pImage)
+ supdrvTracerDeregisterVtgObj(pDevExt, pProv);
+ }
+
+ RTSemFastMutexRelease(pDevExt->mtxTracer);
+
+ /*
+ * Try unregister zombies while we have a chance.
+ */
+ supdrvTracerProcessZombies(pDevExt);
+ }
+}
+
+
+/**
+ * Called when a session is being cleaned up.
+ *
+ * @param pDevExt The device extension structure.
+ * @param pSession The session that is being torn down.
+ */
+void VBOXCALL supdrvTracerCleanupSession(PSUPDRVDEVEXT pDevExt, PSUPDRVSESSION pSession)
+{
+ /*
+ * Deregister all providers.
+ */
+ SUPDRVTPPROVIDER *pProvNext;
+ SUPDRVTPPROVIDER *pProv;
+ RTSemFastMutexRequest(pDevExt->mtxTracer);
+ RTListForEachSafe(&pSession->TpProviders, pProv, pProvNext, SUPDRVTPPROVIDER, SessionListEntry)
+ {
+ supdrvTracerDeregisterVtgObj(pDevExt, pProv);
+ }
+ RTSemFastMutexRelease(pDevExt->mtxTracer);
+
+ /*
+ * Clean up instance data the trace may have associated with the session.
+ */
+ if (pSession->uTracerData)
+ supdrvIOCtl_TracerClose(pDevExt, pSession);
+
+ /*
+ * Deregister any tracer implementation.
+ */
+ if (pSession->R0Process == NIL_RTR0PROCESS)
+ (void)SUPR0TracerDeregisterImpl(NULL, pSession);
+
+ if (pSession->R0Process != NIL_RTR0PROCESS)
+ {
+ /*
+ * Free any lingering user modules. We don't bother holding the lock
+ * here as there shouldn't be anyone messing with the session at this
+ * point.
+ */
+ PSUPDRVTRACERUMOD pUmodNext;
+ PSUPDRVTRACERUMOD pUmod;
+ RTListForEachSafe(&pSession->TpUmods, pUmod, pUmodNext, SUPDRVTRACERUMOD, ListEntry)
+ {
+ RTR0MemObjFree(pUmod->hMemObjMap, false /*fFreeMappings*/);
+ RTR0MemObjFree(pUmod->hMemObjLock, false /*fFreeMappings*/);
+ supdrvVtgReleaseObjectCopy(pDevExt, pUmod->pVtgCopy);
+ RTMemFree(pUmod);
+ }
+ }
+}
+
+
+static void supdrvVtgReleaseObjectCopy(PSUPDRVDEVEXT pDevExt, PSUPDRVVTGCOPY pThis)
+{
+ uint32_t cRefs = ASMAtomicDecU32(&pThis->cRefs);
+ if (!cRefs)
+ {
+ RTSemFastMutexRequest(pDevExt->mtxTracer);
+ pThis->u32Magic = ~SUDPRVVTGCOPY_MAGIC;
+ RTListNodeRemove(&pThis->ListEntry);
+ RTSemFastMutexRelease(pDevExt->mtxTracer);
+
+ RTMemFree(pThis);
+ }
+}
+
+
+/**
+ * Finds a matching VTG object copy, caller owns the lock already.
+ *
+ * @returns Copy with reference. NULL if not found.
+ * @param pHashList The hash list to search.
+ * @param pHdr The VTG header (valid).
+ * @param cbStrTab The string table size.
+ * @param fFlags The user module flags.
+ */
+static PSUPDRVVTGCOPY supdrvVtgFindObjectCopyLocked(PRTLISTANCHOR pHashList, PCVTGOBJHDR pHdr, uint32_t cbStrTab, uint32_t fFlags)
+{
+ PSUPDRVVTGCOPY pCur;
+
+ fFlags &= SUP_TRACER_UMOD_FLAGS_TYPE_MASK;
+ RTListForEach(pHashList, pCur, SUPDRVVTGCOPY, ListEntry)
+ {
+#define HDR_EQUALS(member) pCur->Hdr.member == pHdr->member
+ if ( HDR_EQUALS(Uuid.au32[0])
+ && HDR_EQUALS(Uuid.au32[1])
+ && HDR_EQUALS(Uuid.au32[2])
+ && HDR_EQUALS(Uuid.au32[3])
+ && HDR_EQUALS(cbObj)
+ && HDR_EQUALS(cBits)
+ && pCur->cbStrTab == cbStrTab
+ && pCur->fFlags == fFlags
+ )
+ {
+ if (RT_LIKELY( HDR_EQUALS(offStrTab)
+ && HDR_EQUALS(cbStrTab)
+ && HDR_EQUALS(offArgLists)
+ && HDR_EQUALS(cbArgLists)
+ && HDR_EQUALS(offProbes)
+ && HDR_EQUALS(cbProbes)
+ && HDR_EQUALS(offProviders)
+ && HDR_EQUALS(cbProviders)
+ && HDR_EQUALS(offProbeEnabled)
+ && HDR_EQUALS(cbProbeEnabled)
+ && HDR_EQUALS(offProbeLocs)
+ && HDR_EQUALS(cbProbeLocs)
+ )
+ )
+ {
+ Assert(pCur->cRefs > 0);
+ Assert(pCur->cRefs < _1M);
+ pCur->cRefs++;
+ return pCur;
+ }
+ }
+#undef HDR_EQUALS
+ }
+
+ return NULL;
+}
+
+
+/**
+ * Finds a matching VTG object copy.
+ *
+ * @returns Copy with reference. NULL if not found.
+ * @param pDevExt The device extension.
+ * @param pHdr The VTG header (valid).
+ * @param cbStrTab The string table size.
+ * @param fFlags The user module flags.
+ */
+static PSUPDRVVTGCOPY supdrvVtgFindObjectCopy(PSUPDRVDEVEXT pDevExt, PCVTGOBJHDR pHdr, uint32_t cbStrTab, uint32_t fFlags)
+{
+ PRTLISTANCHOR pHashList = &pDevExt->aTrackerUmodHash[pHdr->Uuid.au8[3] % RT_ELEMENTS(pDevExt->aTrackerUmodHash)];
+ PSUPDRVVTGCOPY pRet;
+
+ int rc = RTSemFastMutexRequest(pDevExt->mtxTracer);
+ AssertRCReturn(rc, NULL);
+
+ pRet = supdrvVtgFindObjectCopyLocked(pHashList, pHdr, cbStrTab, fFlags);
+
+ RTSemFastMutexRelease(pDevExt->mtxTracer);
+ return pRet;
+}
+
+
+/**
+ * Makes a shared copy of the VTG object.
+ *
+ * @returns VBox status code.
+ * @param pDevExt The device extension.
+ * @param pVtgHdr The VTG header (valid).
+ * @param R3PtrVtgHdr The ring-3 VTG header address.
+ * @param uVtgHdrAddr The address of the VTG header in the context
+ * where it is actually used.
+ * @param R3PtrStrTab The ring-3 address of the probe location string
+ * table. The probe location array have offsets
+ * into this instead of funciton name pointers.
+ * @param cbStrTab The size of the probe location string table.
+ * @param fFlags The user module flags.
+ * @param pUmod The structure we've allocated to track the
+ * module. This have a valid kernel mapping of the
+ * probe location array. Upon successful return,
+ * the pVtgCopy member will hold the address of our
+ * copy (with a referenced of course).
+ */
+static int supdrvVtgCreateObjectCopy(PSUPDRVDEVEXT pDevExt, PCVTGOBJHDR pVtgHdr, RTR3PTR R3PtrVtgHdr, RTUINTPTR uVtgHdrAddr,
+ RTR3PTR R3PtrStrTab, uint32_t cbStrTab, uint32_t fFlags, PSUPDRVTRACERUMOD pUmod)
+{
+ /*
+ * Calculate the space required, allocate and copy in the data.
+ */
+ int rc;
+ uint32_t const cProbeLocs = pVtgHdr->cbProbeLocs / (pVtgHdr->cBits == 32 ? sizeof(VTGPROBELOC32) : sizeof(VTGPROBELOC64));
+ uint32_t const cbProbeLocs = cProbeLocs * sizeof(VTGPROBELOC);
+ uint32_t const offProbeLocs = RT_ALIGN(pVtgHdr->cbObj, 8);
+ size_t const cb = offProbeLocs + cbProbeLocs + cbStrTab + 1;
+ PSUPDRVVTGCOPY pThis = (PSUPDRVVTGCOPY)RTMemAlloc(RT_UOFFSETOF(SUPDRVVTGCOPY, Hdr) + cb);
+ if (!pThis)
+ return VERR_NO_MEMORY;
+
+ pThis->u32Magic = SUDPRVVTGCOPY_MAGIC;
+ pThis->cRefs = 1;
+ pThis->cbStrTab = cbStrTab;
+ pThis->fFlags = fFlags & SUP_TRACER_UMOD_FLAGS_TYPE_MASK;
+ RTListInit(&pThis->ListEntry);
+
+ rc = RTR0MemUserCopyFrom(&pThis->Hdr, R3PtrVtgHdr, pVtgHdr->cbObj);
+ if (RT_SUCCESS(rc))
+ {
+ char *pchStrTab = (char *)&pThis->Hdr + offProbeLocs + cbProbeLocs;
+ rc = RTR0MemUserCopyFrom(pchStrTab, R3PtrStrTab, cbStrTab);
+ if (RT_SUCCESS(rc))
+ {
+ PVTGPROBELOC paDst = (PVTGPROBELOC)((char *)&pThis->Hdr + offProbeLocs);
+ uint32_t i;
+
+ /*
+ * Some paranoia: Overwrite the header with the copy we've already
+ * validated and zero terminate the string table.
+ */
+ pThis->Hdr = *pVtgHdr;
+ pchStrTab[cbStrTab] = '\0';
+
+ /*
+ * Set the probe location array related header members since we're
+ * making our own copy in a different location.
+ */
+ pThis->Hdr.uProbeLocs.u64 = (uintptr_t)paDst;
+ pThis->Hdr.uProbeLocsEnd.u64 = (uintptr_t)paDst + cbProbeLocs;
+ pThis->Hdr.offProbeLocs = offProbeLocs;
+ pThis->Hdr.cbProbeLocs = cbProbeLocs;
+ pThis->Hdr.cBits = ARCH_BITS;
+
+ /*
+ * Copy, convert and fix up the probe location table.
+ */
+ if (pVtgHdr->cBits == 32)
+ {
+ uintptr_t const offDelta = (uintptr_t)&pThis->Hdr - uVtgHdrAddr;
+ PCVTGPROBELOC32 paSrc = (PCVTGPROBELOC32)pUmod->pvProbeLocs;
+
+ for (i = 0; i < cProbeLocs; i++)
+ {
+ paDst[i].uLine = paSrc[i].uLine;
+ paDst[i].fEnabled = paSrc[i].fEnabled;
+ paDst[i].idProbe = paSrc[i].idProbe;
+ if (paSrc[i].pszFunction > cbStrTab)
+ {
+ rc = VERR_SUPDRV_TRACER_UMOD_STRTAB_OFF_BAD;
+ break;
+ }
+ paDst[i].pszFunction = pchStrTab + paSrc[i].pszFunction;
+ paDst[i].pProbe = (PVTGDESCPROBE)(uintptr_t)(paSrc[i].pProbe + offDelta);
+ }
+ }
+ else
+ {
+ uint64_t const offDelta = (uintptr_t)&pThis->Hdr - uVtgHdrAddr;
+ PCVTGPROBELOC64 paSrc = (PCVTGPROBELOC64)pUmod->pvProbeLocs;
+
+ for (i = 0; i < cProbeLocs; i++)
+ {
+ paDst[i].uLine = paSrc[i].uLine;
+ paDst[i].fEnabled = paSrc[i].fEnabled;
+ paDst[i].idProbe = paSrc[i].idProbe;
+ if (paSrc[i].pszFunction > cbStrTab)
+ {
+ rc = VERR_SUPDRV_TRACER_UMOD_STRTAB_OFF_BAD;
+ break;
+ }
+ paDst[i].pszFunction = pchStrTab + (uintptr_t)paSrc[i].pszFunction;
+ paDst[i].pProbe = (PVTGDESCPROBE)(uintptr_t)(paSrc[i].pProbe + offDelta);
+ }
+ }
+
+ /*
+ * Validate it
+ *
+ * Note! fUmod is false as this is a kernel copy with all native
+ * structures.
+ */
+ if (RT_SUCCESS(rc))
+ rc = supdrvVtgValidate(&pThis->Hdr, (uintptr_t)&pThis->Hdr, (uint8_t *)&pThis->Hdr, cb, false /*fUmod*/);
+ if (RT_SUCCESS(rc))
+ {
+ /*
+ * Add it to the hash list, making sure nobody raced us.
+ */
+ PRTLISTANCHOR pHashList = &pDevExt->aTrackerUmodHash[ pVtgHdr->Uuid.au8[3]
+ % RT_ELEMENTS(pDevExt->aTrackerUmodHash)];
+
+ rc = RTSemFastMutexRequest(pDevExt->mtxTracer);
+ if (RT_SUCCESS(rc))
+ {
+ pUmod->pVtgCopy = supdrvVtgFindObjectCopyLocked(pHashList, pVtgHdr, cbStrTab, fFlags);
+ if (!pUmod->pVtgCopy)
+ {
+ pUmod->pVtgCopy = pThis;
+ RTListAppend(pHashList, &pThis->ListEntry);
+ RTSemFastMutexRelease(pDevExt->mtxTracer);
+ return rc;
+ }
+
+ /*
+ * Someone raced us, free our copy and return the existing
+ * one instead.
+ */
+ RTSemFastMutexRelease(pDevExt->mtxTracer);
+ }
+ }
+ }
+ }
+ RTMemFree(pThis);
+ return rc;
+}
+
+
+/**
+ * Undoes what supdrvTracerUmodSetProbeIds did.
+ *
+ * @param pDevExt The device extension.
+ * @param pSession The current session.
+ * @param pUmod The user tracepoint module.
+ */
+static void supdrvTracerUmodClearProbeIds(PSUPDRVDEVEXT pDevExt, PSUPDRVSESSION pSession, PSUPDRVTRACERUMOD pUmod)
+{
+ uint32_t i;
+
+ AssertReturnVoid(pUmod->iLookupTable < RT_ELEMENTS(pSession->apTpLookupTable));
+ AssertReturnVoid(pSession->apTpLookupTable[pUmod->iLookupTable] == pUmod);
+
+ /*
+ * Clear the probe IDs and disable the probes.
+ */
+ i = pUmod->cProbeLocs;
+ if (pUmod->cBits == 32)
+ {
+ PVTGPROBELOC32 paProbeLocs = (PVTGPROBELOC32)pUmod->pvProbeLocs;
+ while (i-- > 0)
+ paProbeLocs[i].idProbe = 0;
+ }
+ else
+ {
+ PVTGPROBELOC64 paProbeLocs = (PVTGPROBELOC64)pUmod->pvProbeLocs;
+ while (i-- > 0)
+ paProbeLocs[i].idProbe = 0;
+ }
+
+ /*
+ * Free the lookup table entry. We'll have to wait for the table to go
+ * idle to make sure there are no current users of pUmod.
+ */
+ RTSemFastMutexRequest(pDevExt->mtxTracer);
+ if (pSession->apTpLookupTable[pUmod->iLookupTable] == pUmod)
+ {
+ if (pSession->cTpProbesFiring > 0)
+ {
+ i = 0;
+ while (pSession->cTpProbesFiring > 0)
+ {
+ RTSemFastMutexRelease(pDevExt->mtxTracer);
+ i++;
+ if (!(i & 0xff))
+ SUPR0Printf("supdrvTracerUmodClearProbeIds: waiting for lookup table to go idle (i=%u)\n", i);
+ RTThreadSleep(10);
+ RTSemFastMutexRequest(pDevExt->mtxTracer);
+ }
+ }
+ ASMAtomicWriteNullPtr(&pSession->apTpLookupTable[pUmod->iLookupTable]);
+ }
+ RTSemFastMutexRelease(pDevExt->mtxTracer);
+}
+
+
+/**
+ * Allocates a lookup table entry for the Umod and sets the
+ * VTGPROBELOC::idProbe fields in user mode.
+ *
+ * @returns VINF_SUCCESS or VERR_SUPDRV_TRACER_TOO_MANY_PROVIDERS.
+ * @param pDevExt The device extension.
+ * @param pSession The current session.
+ * @param pUmod The user tracepoint module.
+ */
+static int supdrvTracerUmodSetProbeIds(PSUPDRVDEVEXT pDevExt, PSUPDRVSESSION pSession, PSUPDRVTRACERUMOD pUmod)
+{
+ uint32_t iBase;
+ uint32_t i;
+
+ /*
+ * Allocate a lookup table entry.
+ */
+ RTSemFastMutexRequest(pDevExt->mtxTracer);
+ for (i = 0; i < RT_ELEMENTS(pSession->apTpLookupTable); i++)
+ {
+ if (!pSession->apTpLookupTable[i])
+ {
+ pSession->apTpLookupTable[i] = pUmod;
+ pUmod->iLookupTable = i;
+ break;
+ }
+ }
+ RTSemFastMutexRelease(pDevExt->mtxTracer);
+ if (i >= RT_ELEMENTS(pSession->apTpLookupTable))
+ return VERR_SUPDRV_TRACER_TOO_MANY_PROVIDERS;
+
+ /*
+ * Set probe IDs of the usermode probe location to indicate our lookup
+ * table entry as well as the probe location array entry.
+ */
+ iBase = (uint32_t)pUmod->iLookupTable << 24;
+ i = pUmod->cProbeLocs;
+ if (pUmod->cBits == 32)
+ {
+ PVTGPROBELOC32 paProbeLocs = (PVTGPROBELOC32)pUmod->pvProbeLocs;
+ while (i-- > 0)
+ paProbeLocs[i].idProbe = iBase | i;
+ }
+ else
+ {
+ PVTGPROBELOC64 paProbeLocs = (PVTGPROBELOC64)pUmod->pvProbeLocs;
+ while (i-- > 0)
+ paProbeLocs[i].idProbe = iBase | i;
+ }
+
+ return VINF_SUCCESS;
+}
+
+
+int VBOXCALL supdrvIOCtl_TracerUmodRegister(PSUPDRVDEVEXT pDevExt, PSUPDRVSESSION pSession,
+ RTR3PTR R3PtrVtgHdr, RTUINTPTR uVtgHdrAddr,
+ RTR3PTR R3PtrStrTab, uint32_t cbStrTab,
+ const char *pszModName, uint32_t fFlags)
+{
+ VTGOBJHDR Hdr;
+ PSUPDRVTRACERUMOD pUmod;
+ RTR3PTR R3PtrLock;
+ size_t cbLock;
+ uint32_t cProbeLocs;
+ int rc;
+
+ /*
+ * Validate input.
+ */
+ if (pSession->R0Process == NIL_RTR0PROCESS)
+ return VERR_INVALID_CONTEXT;
+ if ( fFlags != SUP_TRACER_UMOD_FLAGS_EXE
+ && fFlags != SUP_TRACER_UMOD_FLAGS_SHARED)
+ return VERR_INVALID_PARAMETER;
+
+ if (pSession->cTpProviders >= RT_ELEMENTS(pSession->apTpLookupTable))
+ return VERR_SUPDRV_TRACER_TOO_MANY_PROVIDERS;
+
+ if ( cbStrTab < 2
+ || cbStrTab > _1M)
+ return VERR_SUPDRV_TRACER_UMOD_STRTAB_TOO_BIG;
+
+ /*
+ * Read the VTG header into a temporary buffer and perform some simple
+ * validations to make sure we aren't wasting our time here.
+ */
+ rc = RTR0MemUserCopyFrom(&Hdr, R3PtrVtgHdr, sizeof(Hdr));
+ if (RT_FAILURE(rc))
+ return rc;
+ rc = supdrvVtgValidateHdr(&Hdr, uVtgHdrAddr, NULL, 0, true /*fUmod*/);
+ if (RT_FAILURE(rc))
+ return rc;
+ if (Hdr.cbProviders / sizeof(VTGDESCPROVIDER) > 2)
+ return VERR_SUPDRV_TRACER_TOO_MANY_PROVIDERS;
+
+ /*
+ * Check how much needs to be locked down and how many probe locations
+ * there are.
+ */
+ if ( Hdr.offProbeLocs <= 0
+ || Hdr.offProbeEnabled > (uint32_t)Hdr.offProbeLocs
+ || (uint32_t)Hdr.offProbeLocs - Hdr.offProbeEnabled - Hdr.cbProbeEnabled > 128)
+ return VERR_SUPDRV_TRACER_UMOD_NOT_ADJACENT;
+ R3PtrLock = R3PtrVtgHdr + Hdr.offProbeEnabled;
+ cbLock = Hdr.offProbeLocs + Hdr.cbProbeLocs - Hdr.offProbeEnabled + (R3PtrLock & PAGE_OFFSET_MASK);
+ R3PtrLock &= ~(RTR3PTR)PAGE_OFFSET_MASK;
+ if (cbLock > _64K)
+ return VERR_SUPDRV_TRACER_UMOD_TOO_MANY_PROBES;
+
+ cProbeLocs = Hdr.cbProbeLocs / (Hdr.cBits == 32 ? sizeof(VTGPROBELOC32) : sizeof(VTGPROBELOC64));
+
+ /*
+ * Allocate the tracker data we keep in the session.
+ */
+ pUmod = (PSUPDRVTRACERUMOD)RTMemAllocZ( RT_UOFFSETOF_DYN(SUPDRVTRACERUMOD, aProbeLocs[cProbeLocs])
+ + (Hdr.cbProbeEnabled / sizeof(uint32_t) * sizeof(SUPDRVPROBEINFO)) );
+ if (!pUmod)
+ return VERR_NO_MEMORY;
+ pUmod->u32Magic = SUPDRVTRACERUMOD_MAGIC;
+ RTListInit(&pUmod->ListEntry);
+ pUmod->R3PtrVtgHdr = R3PtrVtgHdr;
+ pUmod->pVtgCopy = NULL;
+ pUmod->hMemObjLock = NIL_RTR0MEMOBJ;
+ pUmod->hMemObjMap = NIL_RTR0MEMOBJ;
+ pUmod->R3PtrProbeLocs = (RTR3INTPTR)R3PtrVtgHdr + Hdr.offProbeLocs;
+ pUmod->iLookupTable = UINT8_MAX;
+ pUmod->cBits = Hdr.cBits;
+ pUmod->cbProbeLoc = Hdr.cBits == 32 ? sizeof(VTGPROBELOC32) : sizeof(VTGPROBELOC64);
+ pUmod->cProbeLocs = cProbeLocs;
+
+ /*
+ * Lock down and map the user-mode structures.
+ */
+ rc = RTR0MemObjLockUser(&pUmod->hMemObjLock, R3PtrLock, cbLock, RTMEM_PROT_READ | RTMEM_PROT_WRITE, NIL_RTR0PROCESS);
+ if (RT_SUCCESS(rc))
+ {
+ rc = RTR0MemObjMapKernel(&pUmod->hMemObjMap, pUmod->hMemObjLock, (void *)-1, 0, RTMEM_PROT_READ | RTMEM_PROT_WRITE);
+ if (RT_SUCCESS(rc))
+ {
+ pUmod->pacProbeEnabled = (uint32_t *)( (uintptr_t)RTR0MemObjAddress(pUmod->hMemObjMap)
+ + ((uintptr_t)(R3PtrVtgHdr + Hdr.offProbeEnabled) & PAGE_OFFSET_MASK));
+ pUmod->pvProbeLocs = (uint8_t *)pUmod->pacProbeEnabled + Hdr.offProbeLocs - Hdr.offProbeEnabled;
+
+ /*
+ * Does some other process use the same module already? If so,
+ * share the VTG data with it. Otherwise, make a ring-0 copy it.
+ */
+ pUmod->pVtgCopy = supdrvVtgFindObjectCopy(pDevExt, &Hdr, cbStrTab, fFlags);
+ if (!pUmod->pVtgCopy)
+ rc = supdrvVtgCreateObjectCopy(pDevExt, &Hdr, R3PtrVtgHdr, uVtgHdrAddr, R3PtrStrTab, cbStrTab, fFlags, pUmod);
+ if (RT_SUCCESS(rc))
+ {
+ AssertPtr(pUmod->pVtgCopy);
+
+ /*
+ * Grabe a place in apTpLookupTable and set the probe IDs
+ * accordingly.
+ */
+ rc = supdrvTracerUmodSetProbeIds(pDevExt, pSession, pUmod);
+ if (RT_SUCCESS(rc))
+ {
+ /*
+ * Register the providers.
+ */
+ rc = supdrvTracerRegisterVtgObj(pDevExt, &pUmod->pVtgCopy->Hdr,
+ NULL /*pImage*/, pSession, pUmod, pszModName);
+ if (RT_SUCCESS(rc))
+ {
+ RTSemFastMutexRequest(pDevExt->mtxTracer);
+ RTListAppend(&pSession->TpUmods, &pUmod->ListEntry);
+ RTSemFastMutexRelease(pDevExt->mtxTracer);
+
+ return VINF_SUCCESS;
+ }
+
+ /* bail out. */
+ supdrvTracerUmodClearProbeIds(pDevExt, pSession, pUmod);
+ }
+ supdrvVtgReleaseObjectCopy(pDevExt, pUmod->pVtgCopy);
+ }
+ RTR0MemObjFree(pUmod->hMemObjMap, false /*fFreeMappings*/);
+ }
+ RTR0MemObjFree(pUmod->hMemObjLock, false /*fFreeMappings*/);
+ }
+ pUmod->u32Magic = ~SUPDRVTRACERUMOD_MAGIC;
+ RTMemFree(pUmod);
+ return rc;
+}
+
+
+int VBOXCALL supdrvIOCtl_TracerUmodDeregister(PSUPDRVDEVEXT pDevExt, PSUPDRVSESSION pSession, RTR3PTR R3PtrVtgHdr)
+{
+ PSUPDRVTRACERUMOD pUmod = NULL;
+ uint32_t i;
+ int rc;
+
+ /*
+ * Validate the request.
+ */
+ RTSemFastMutexRequest(pDevExt->mtxTracer);
+ for (i = 0; i < RT_ELEMENTS(pSession->apTpLookupTable); i++)
+ {
+ pUmod = pSession->apTpLookupTable[i];
+ if ( pUmod
+ && pUmod->u32Magic == SUPDRVTRACERUMOD_MAGIC
+ && pUmod->R3PtrVtgHdr == R3PtrVtgHdr)
+ break;
+ }
+ RTSemFastMutexRelease(pDevExt->mtxTracer);
+ if (pUmod)
+ {
+ SUPDRVTPPROVIDER *pProvNext;
+ SUPDRVTPPROVIDER *pProv;
+
+ /*
+ * Remove ourselves from the lookup table and clean up the ring-3 bits
+ * we've dirtied. We do this first to make sure no probes are firing
+ * when we're destroying the providers in the next step.
+ */
+ supdrvTracerUmodClearProbeIds(pDevExt, pSession, pUmod);
+
+ /*
+ * Deregister providers related to the VTG object.
+ */
+ RTSemFastMutexRequest(pDevExt->mtxTracer);
+ RTListForEachSafe(&pSession->TpProviders, pProv, pProvNext, SUPDRVTPPROVIDER, SessionListEntry)
+ {
+ if (pProv->pUmod == pUmod)
+ supdrvTracerDeregisterVtgObj(pDevExt, pProv);
+ }
+ RTSemFastMutexRelease(pDevExt->mtxTracer);
+
+ /*
+ * Destroy the Umod object.
+ */
+ pUmod->u32Magic = ~SUPDRVTRACERUMOD_MAGIC;
+ supdrvVtgReleaseObjectCopy(pDevExt, pUmod->pVtgCopy);
+ RTR0MemObjFree(pUmod->hMemObjMap, false /*fFreeMappings*/);
+ RTR0MemObjFree(pUmod->hMemObjLock, false /*fFreeMappings*/);
+ RTMemFree(pUmod);
+ rc = VINF_SUCCESS;
+ }
+ else
+ rc = VERR_NOT_FOUND;
+ return rc;
+}
+
+
+/**
+ * Implementation of supdrvIOCtl_TracerUmodProbeFire and
+ * SUPR0TracerUmodProbeFire.
+ *
+ * @param pDevExt The device extension.
+ * @param pSession The calling session.
+ * @param pCtx The context record.
+ */
+static void supdrvTracerUmodProbeFire(PSUPDRVDEVEXT pDevExt, PSUPDRVSESSION pSession, PSUPDRVTRACERUSRCTX pCtx)
+{
+ /*
+ * We cannot trust user mode to hand us the right bits nor not calling us
+ * when disabled. So, we have to check for our selves.
+ */
+ PSUPDRVTRACERUMOD pUmod;
+ uint32_t const iLookupTable = pCtx->idProbe >> 24;
+ uint32_t const iProbeLoc = pCtx->idProbe & UINT32_C(0x00ffffff);
+
+ if (RT_UNLIKELY( !pDevExt->pTracerOps
+ || pDevExt->fTracerUnloading))
+ return;
+ if (RT_UNLIKELY(iLookupTable >= RT_ELEMENTS(pSession->apTpLookupTable)))
+ return;
+ if (RT_UNLIKELY( pCtx->cBits != 32
+ && pCtx->cBits != 64))
+ return;
+
+ ASMAtomicIncU32(&pSession->cTpProviders);
+
+ pUmod = pSession->apTpLookupTable[iLookupTable];
+ if (RT_LIKELY(pUmod))
+ {
+ if (RT_LIKELY( pUmod->u32Magic == SUPDRVTRACERUMOD_MAGIC
+ && iProbeLoc < pUmod->cProbeLocs
+ && pCtx->cBits == pUmod->cBits))
+ {
+#if 0 /* This won't work for RC modules. */
+ RTR3PTR R3PtrProbeLoc = pUmod->R3PtrProbeLocs + iProbeLoc * pUmod->cbProbeLoc;
+ if (RT_LIKELY( (pCtx->cBits == 32 ? (RTR3PTR)pCtx->u.X86.uVtgProbeLoc : pCtx->u.Amd64.uVtgProbeLoc)
+ == R3PtrProbeLoc))
+#endif
+ {
+ if (RT_LIKELY(pUmod->aProbeLocs[iProbeLoc].fEnabled))
+ {
+ PSUPDRVVTGCOPY pVtgCopy;
+ ASMAtomicIncU32(&pDevExt->cTracerCallers);
+ pVtgCopy = pUmod->pVtgCopy;
+ if (RT_LIKELY( pDevExt->pTracerOps
+ && !pDevExt->fTracerUnloading
+ && pVtgCopy))
+ {
+ PCVTGPROBELOC pProbeLocRO;
+ pProbeLocRO = (PCVTGPROBELOC)((uintptr_t)&pVtgCopy->Hdr + pVtgCopy->Hdr.offProbeLocs) + iProbeLoc;
+
+ pCtx->idProbe = pUmod->aProbeLocs[iProbeLoc].idProbe;
+ pDevExt->pTracerOps->pfnProbeFireUser(pDevExt->pTracerOps, pSession, pCtx, &pVtgCopy->Hdr, pProbeLocRO);
+ }
+ ASMAtomicDecU32(&pDevExt->cTracerCallers);
+ }
+ }
+ }
+ }
+
+ ASMAtomicDecU32(&pSession->cTpProviders);
+}
+
+
+SUPR0DECL(void) SUPR0TracerUmodProbeFire(PSUPDRVSESSION pSession, PSUPDRVTRACERUSRCTX pCtx)
+{
+ AssertReturnVoid(SUP_IS_SESSION_VALID(pSession));
+ AssertPtrReturnVoid(pCtx);
+
+ supdrvTracerUmodProbeFire(pSession->pDevExt, pSession, pCtx);
+}
+SUPR0_EXPORT_SYMBOL(SUPR0TracerUmodProbeFire);
+
+
+void VBOXCALL supdrvIOCtl_TracerUmodProbeFire(PSUPDRVDEVEXT pDevExt, PSUPDRVSESSION pSession, PSUPDRVTRACERUSRCTX pCtx)
+{
+ supdrvTracerUmodProbeFire(pDevExt, pSession, pCtx);
+}
+
+
+/**
+ * Open the tracer.
+ *
+ * @returns VBox status code
+ * @param pDevExt The device extension structure.
+ * @param pSession The current session.
+ * @param uCookie The tracer cookie.
+ * @param uArg The tracer open argument.
+ */
+int VBOXCALL supdrvIOCtl_TracerOpen(PSUPDRVDEVEXT pDevExt, PSUPDRVSESSION pSession, uint32_t uCookie, uintptr_t uArg)
+{
+ RTNATIVETHREAD hNativeSelf = RTThreadNativeSelf();
+ int rc;
+
+ RTSemFastMutexRequest(pDevExt->mtxTracer);
+
+ if (!pSession->uTracerData)
+ {
+ if (pDevExt->pTracerOps)
+ {
+ if (pDevExt->pTracerSession != pSession)
+ {
+ if (!pDevExt->fTracerUnloading)
+ {
+ if (pSession->hTracerCaller == NIL_RTNATIVETHREAD)
+ {
+ pDevExt->cTracerOpens++;
+ pSession->uTracerData = ~(uintptr_t)0;
+ pSession->hTracerCaller = hNativeSelf;
+ RTSemFastMutexRelease(pDevExt->mtxTracer);
+
+ rc = pDevExt->pTracerOps->pfnTracerOpen(pDevExt->pTracerOps, pSession, uCookie, uArg, &pSession->uTracerData);
+
+ RTSemFastMutexRequest(pDevExt->mtxTracer);
+ if (RT_FAILURE(rc))
+ {
+ pDevExt->cTracerOpens--;
+ pSession->uTracerData = 0;
+ }
+ pSession->hTracerCaller = NIL_RTNATIVETHREAD;
+ }
+ else
+ rc = VERR_SUPDRV_TRACER_SESSION_BUSY;
+ }
+ else
+ rc = VERR_SUPDRV_TRACER_UNLOADING;
+ }
+ else
+ rc = VERR_SUPDRV_TRACER_CANNOT_OPEN_SELF;
+ }
+ else
+ rc = VERR_SUPDRV_TRACER_NOT_PRESENT;
+ }
+ else
+ rc = VERR_SUPDRV_TRACER_ALREADY_OPENED;
+
+ RTSemFastMutexRelease(pDevExt->mtxTracer);
+ return rc;
+}
+
+
+/**
+ * Closes the tracer.
+ *
+ * @returns VBox status code.
+ * @param pDevExt The device extension structure.
+ * @param pSession The current session.
+ */
+int VBOXCALL supdrvIOCtl_TracerClose(PSUPDRVDEVEXT pDevExt, PSUPDRVSESSION pSession)
+{
+ RTNATIVETHREAD hNativeSelf = RTThreadNativeSelf();
+ int rc;
+
+ RTSemFastMutexRequest(pDevExt->mtxTracer);
+
+ if (pSession->uTracerData)
+ {
+ Assert(pDevExt->cTracerOpens > 0);
+
+ if (pDevExt->pTracerOps)
+ {
+ if (pSession->hTracerCaller == NIL_RTNATIVETHREAD)
+ {
+ uintptr_t uTracerData = pSession->uTracerData;
+ pSession->uTracerData = 0;
+ pSession->hTracerCaller = hNativeSelf;
+ RTSemFastMutexRelease(pDevExt->mtxTracer);
+
+ pDevExt->pTracerOps->pfnTracerClose(pDevExt->pTracerOps, pSession, uTracerData);
+ rc = VINF_SUCCESS;
+
+ RTSemFastMutexRequest(pDevExt->mtxTracer);
+ pSession->hTracerCaller = NIL_RTNATIVETHREAD;
+ Assert(pDevExt->cTracerOpens > 0);
+ pDevExt->cTracerOpens--;
+ }
+ else
+ rc = VERR_SUPDRV_TRACER_SESSION_BUSY;
+ }
+ else
+ {
+ rc = VERR_SUPDRV_TRACER_NOT_PRESENT;
+ pSession->uTracerData = 0;
+ Assert(pDevExt->cTracerOpens > 0);
+ pDevExt->cTracerOpens--;
+ }
+ }
+ else
+ rc = VERR_SUPDRV_TRACER_NOT_OPENED;
+
+ RTSemFastMutexRelease(pDevExt->mtxTracer);
+ return rc;
+}
+
+
+/**
+ * Performs a tracer I/O control request.
+ *
+ * @returns VBox status code.
+ * @param pDevExt The device extension structure.
+ * @param pSession The current session.
+ * @param uCmd The tracer command.
+ * @param uArg The tracer argument.
+ * @param piRetVal Where to store the tracer specific return value.
+ */
+int VBOXCALL supdrvIOCtl_TracerIOCtl(PSUPDRVDEVEXT pDevExt, PSUPDRVSESSION pSession, uintptr_t uCmd, uintptr_t uArg, int32_t *piRetVal)
+{
+ RTNATIVETHREAD hNativeSelf = RTThreadNativeSelf();
+ int rc;
+
+ *piRetVal = 0;
+ RTSemFastMutexRequest(pDevExt->mtxTracer);
+
+ if (pSession->uTracerData)
+ {
+ Assert(pDevExt->cTracerOpens > 0);
+ if (pDevExt->pTracerOps)
+ {
+ if (!pDevExt->fTracerUnloading)
+ {
+ if (pSession->hTracerCaller == NIL_RTNATIVETHREAD)
+ {
+ uintptr_t uTracerData = pSession->uTracerData;
+ pDevExt->cTracerOpens++;
+ pSession->hTracerCaller = hNativeSelf;
+ RTSemFastMutexRelease(pDevExt->mtxTracer);
+
+ rc = pDevExt->pTracerOps->pfnTracerIoCtl(pDevExt->pTracerOps, pSession, uTracerData, uCmd, uArg, piRetVal);
+
+ RTSemFastMutexRequest(pDevExt->mtxTracer);
+ pSession->hTracerCaller = NIL_RTNATIVETHREAD;
+ Assert(pDevExt->cTracerOpens > 0);
+ pDevExt->cTracerOpens--;
+ }
+ else
+ rc = VERR_SUPDRV_TRACER_SESSION_BUSY;
+ }
+ else
+ rc = VERR_SUPDRV_TRACER_UNLOADING;
+ }
+ else
+ rc = VERR_SUPDRV_TRACER_NOT_PRESENT;
+ }
+ else
+ rc = VERR_SUPDRV_TRACER_NOT_OPENED;
+
+ RTSemFastMutexRelease(pDevExt->mtxTracer);
+ return rc;
+}
+
+
+/**
+ * Early module initialization hook.
+ *
+ * @returns VBox status code.
+ * @param pDevExt The device extension structure.
+ */
+int VBOXCALL supdrvTracerInit(PSUPDRVDEVEXT pDevExt)
+{
+ /*
+ * Initialize the tracer.
+ */
+ int rc = RTSemFastMutexCreate(&pDevExt->mtxTracer);
+ if (RT_SUCCESS(rc))
+ {
+ uint32_t i;
+
+ pDevExt->TracerHlp.uVersion = SUPDRVTRACERHLP_VERSION;
+ /** @todo */
+ pDevExt->TracerHlp.uEndVersion = SUPDRVTRACERHLP_VERSION;
+ RTListInit(&pDevExt->TracerProviderList);
+ RTListInit(&pDevExt->TracerProviderZombieList);
+ for (i = 0; i < RT_ELEMENTS(pDevExt->aTrackerUmodHash); i++)
+ RTListInit(&pDevExt->aTrackerUmodHash[i]);
+
+#ifdef VBOX_WITH_NATIVE_DTRACE
+ pDevExt->pTracerOps = supdrvDTraceInit();
+ if (pDevExt->pTracerOps)
+ g_pfnSupdrvProbeFireKernel = (PFNRT)pDevExt->pTracerOps->pfnProbeFireKernel;
+#endif
+
+ /*
+ * Register the provider for this module, if compiled in.
+ */
+#ifdef VBOX_WITH_DTRACE_R0DRV
+ rc = supdrvTracerRegisterVtgObj(pDevExt, &g_VTGObjHeader, NULL /*pImage*/, NULL /*pSession*/, NULL /*pUmod*/, "vboxdrv");
+ if (RT_SUCCESS(rc))
+ return rc;
+ SUPR0Printf("supdrvTracerInit: supdrvTracerRegisterVtgObj failed with rc=%d\n", rc);
+ RTSemFastMutexDestroy(pDevExt->mtxTracer);
+#else
+
+ return VINF_SUCCESS;
+#endif
+ }
+ pDevExt->mtxTracer = NIL_RTSEMFASTMUTEX;
+ return rc;
+}
+
+
+/**
+ * Late module termination hook.
+ *
+ * @param pDevExt The device extension structure.
+ */
+void VBOXCALL supdrvTracerTerm(PSUPDRVDEVEXT pDevExt)
+{
+ LOG_TRACER(("supdrvTracerTerm\n"));
+
+ supdrvTracerRemoveAllProviders(pDevExt);
+#ifdef VBOX_WITH_NATIVE_DTRACE
+ supdrvDTraceFini();
+#endif
+ RTSemFastMutexDestroy(pDevExt->mtxTracer);
+ pDevExt->mtxTracer = NIL_RTSEMFASTMUTEX;
+ LOG_TRACER(("supdrvTracerTerm: Done\n"));
+}
+
diff --git a/src/VBox/HostDrivers/Support/SUPDrvTracerA.asm b/src/VBox/HostDrivers/Support/SUPDrvTracerA.asm
new file mode 100644
index 00000000..58fe94ea
--- /dev/null
+++ b/src/VBox/HostDrivers/Support/SUPDrvTracerA.asm
@@ -0,0 +1,65 @@
+; $Id: SUPDrvTracerA.asm $
+;; @file
+; VirtualBox Support Driver - Tracer Interface, Assembly bits.
+;
+
+;
+; Copyright (C) 2012-2023 Oracle and/or its affiliates.
+;
+; This file is part of VirtualBox base platform packages, as
+; available from https://www.virtualbox.org.
+;
+; This program is free software; you can redistribute it and/or
+; modify it under the terms of the GNU General Public License
+; as published by the Free Software Foundation, in version 3 of the
+; License.
+;
+; This program is distributed in the hope that it will be useful, but
+; WITHOUT ANY WARRANTY; without even the implied warranty of
+; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+; General Public License for more details.
+;
+; You should have received a copy of the GNU General Public License
+; along with this program; if not, see <https://www.gnu.org/licenses>.
+;
+; The contents of this file may alternatively be used under the terms
+; of the Common Development and Distribution License Version 1.0
+; (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+; in the VirtualBox distribution, in which case the provisions of the
+; CDDL are applicable instead of those of the GPL.
+;
+; You may elect to license modified versions of this file under the
+; terms and conditions of either the GPL or the CDDL or both.
+;
+; SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+;
+
+;*******************************************************************************
+;* Header Files *
+;*******************************************************************************
+%include "iprt/asmdefs.mac"
+
+
+; External data.
+extern NAME(g_pfnSupdrvProbeFireKernel)
+
+
+BEGINCODE
+
+;; Dummy stub function that just returns.
+BEGINPROC supdrvTracerProbeFireStub
+ ret
+ENDPROC supdrvTracerProbeFireStub
+
+
+;; Tail jump function.
+EXPORTEDNAME SUPR0TracerFireProbe
+%ifdef RT_ARCH_AMD64
+ mov rax, [NAME(g_pfnSupdrvProbeFireKernel) wrt rip]
+ jmp rax
+%else
+ mov eax, [NAME(g_pfnSupdrvProbeFireKernel)]
+ jmp eax
+%endif
+ENDPROC SUPR0TracerFireProbe
+
diff --git a/src/VBox/HostDrivers/Support/SUPLib.cpp b/src/VBox/HostDrivers/Support/SUPLib.cpp
new file mode 100644
index 00000000..8f5835ff
--- /dev/null
+++ b/src/VBox/HostDrivers/Support/SUPLib.cpp
@@ -0,0 +1,2438 @@
+/* $Id: SUPLib.cpp $ */
+/** @file
+ * VirtualBox Support Library - Common code.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+/** @page pg_sup SUP - The Support Library
+ *
+ * The support library is responsible for providing facilities to load
+ * VMM Host Ring-0 code, to call Host VMM Ring-0 code from Ring-3 Host
+ * code, to pin down physical memory, and more.
+ *
+ * The VMM Host Ring-0 code can be combined in the support driver if
+ * permitted by kernel module license policies. If it is not combined
+ * it will be externalized in a .r0 module that will be loaded using
+ * the IPRT loader.
+ *
+ * The Ring-0 calling is done thru a generic SUP interface which will
+ * transfer an argument set and call a predefined entry point in the Host
+ * VMM Ring-0 code.
+ *
+ * See @ref grp_sup "SUP - Support APIs" for API details.
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#define LOG_GROUP LOG_GROUP_SUP
+#include <VBox/sup.h>
+#include <VBox/err.h>
+#include <VBox/param.h>
+#include <VBox/log.h>
+#include <VBox/VBoxTpG.h>
+
+#include <iprt/assert.h>
+#include <iprt/alloc.h>
+#include <iprt/alloca.h>
+#include <iprt/ldr.h>
+#include <iprt/asm.h>
+#include <iprt/mp.h>
+#include <iprt/cpuset.h>
+#include <iprt/thread.h>
+#include <iprt/process.h>
+#include <iprt/path.h>
+#include <iprt/string.h>
+#include <iprt/env.h>
+#include <iprt/rand.h>
+#include <iprt/x86.h>
+
+#include "SUPDrvIOC.h"
+#include "SUPLibInternal.h"
+
+
+/*********************************************************************************************************************************
+* Defined Constants And Macros *
+*********************************************************************************************************************************/
+/** R0 VMM module name. */
+#define VMMR0_NAME "VMMR0"
+
+
+/*********************************************************************************************************************************
+* Structures and Typedefs *
+*********************************************************************************************************************************/
+typedef DECLCALLBACKTYPE(int, FNCALLVMMR0,(PVMR0 pVMR0, unsigned uOperation, void *pvArg));
+typedef FNCALLVMMR0 *PFNCALLVMMR0;
+
+
+/*********************************************************************************************************************************
+* Global Variables *
+*********************************************************************************************************************************/
+/** Init counter. */
+static uint32_t g_cInits = 0;
+/** Whether we've been preinitied. */
+static bool g_fPreInited = false;
+/** The SUPLib instance data.
+ * Well, at least parts of it, specifically the parts that are being handed over
+ * via the pre-init mechanism from the hardened executable stub. */
+DECL_HIDDEN_DATA(SUPLIBDATA) g_supLibData =
+{
+ /*.hDevice = */ SUP_HDEVICE_NIL,
+ /*.fUnrestricted = */ true,
+ /*.fDriverless = */ false
+#if defined(RT_OS_DARWIN)
+ ,/* .uConnection = */ 0
+#elif defined(RT_OS_LINUX)
+ ,/* .fSysMadviseWorks = */ false
+#endif
+};
+
+/** Pointer to the Global Information Page.
+ *
+ * This pointer is valid as long as SUPLib has a open session. Anyone using
+ * the page must treat this pointer as highly volatile and not trust it beyond
+ * one transaction.
+ *
+ * @todo This will probably deserve it's own session or some other good solution...
+ */
+DECLEXPORT(PSUPGLOBALINFOPAGE) g_pSUPGlobalInfoPage;
+/** Address of the ring-0 mapping of the GIP. */
+PSUPGLOBALINFOPAGE g_pSUPGlobalInfoPageR0;
+/** The physical address of the GIP. */
+static RTHCPHYS g_HCPhysSUPGlobalInfoPage = NIL_RTHCPHYS;
+
+/** The negotiated cookie. */
+DECL_HIDDEN_DATA(uint32_t) g_u32Cookie = 0;
+/** The negotiated session cookie. */
+DECL_HIDDEN_DATA(uint32_t) g_u32SessionCookie;
+/** The session version. */
+DECL_HIDDEN_DATA(uint32_t) g_uSupSessionVersion = 0;
+/** Session handle. */
+DECL_HIDDEN_DATA(PSUPDRVSESSION) g_pSession;
+/** R0 SUP Functions used for resolving referenced to the SUPR0 module. */
+DECL_HIDDEN_DATA(PSUPQUERYFUNCS) g_pSupFunctions;
+
+/** PAGE_ALLOC_EX sans kernel mapping support indicator. */
+static bool g_fSupportsPageAllocNoKernel = true;
+/** Fake mode indicator. (~0 at first, 0 or 1 after first test) */
+DECL_HIDDEN_DATA(uint32_t) g_uSupFakeMode = UINT32_MAX;
+
+
+/*********************************************************************************************************************************
+* Internal Functions *
+*********************************************************************************************************************************/
+static int supInitFake(PSUPDRVSESSION *ppSession);
+
+
+/** Touch a range of pages. */
+DECLINLINE(void) supR3TouchPages(void *pv, size_t cPages)
+{
+ uint32_t volatile *pu32 = (uint32_t volatile *)pv;
+ while (cPages-- > 0)
+ {
+ ASMAtomicCmpXchgU32(pu32, 0, 0);
+ pu32 += PAGE_SIZE / sizeof(uint32_t);
+ }
+}
+
+
+SUPR3DECL(int) SUPR3Install(void)
+{
+ return suplibOsInstall();
+}
+
+
+SUPR3DECL(int) SUPR3Uninstall(void)
+{
+ return suplibOsUninstall();
+}
+
+
+DECL_NOTHROW(DECLEXPORT(int)) supR3PreInit(PSUPPREINITDATA pPreInitData, uint32_t fFlags)
+{
+ /*
+ * The caller is kind of trustworthy, just perform some basic checks.
+ *
+ * Note! Do not do any fancy stuff here because IPRT has NOT been
+ * initialized at this point.
+ */
+ if (!RT_VALID_PTR(pPreInitData))
+ return VERR_INVALID_POINTER;
+ if (g_fPreInited || g_cInits > 0)
+ return VERR_WRONG_ORDER;
+
+ if ( pPreInitData->u32Magic != SUPPREINITDATA_MAGIC
+ || pPreInitData->u32EndMagic != SUPPREINITDATA_MAGIC)
+ return VERR_INVALID_MAGIC;
+ if ( !(fFlags & SUPSECMAIN_FLAGS_DONT_OPEN_DEV)
+ && pPreInitData->Data.hDevice == SUP_HDEVICE_NIL
+ && !pPreInitData->Data.fDriverless)
+ return VERR_INVALID_HANDLE;
+ if ( ( (fFlags & SUPSECMAIN_FLAGS_DONT_OPEN_DEV)
+ || pPreInitData->Data.fDriverless)
+ && pPreInitData->Data.hDevice != SUP_HDEVICE_NIL)
+ return VERR_INVALID_PARAMETER;
+
+ /*
+ * Hand out the data.
+ */
+ int rc = supR3HardenedRecvPreInitData(pPreInitData);
+ if (RT_FAILURE(rc))
+ return rc;
+
+ /** @todo This may need some small restructuring later, it doesn't quite work with a root service flag... */
+ if (!(fFlags & SUPSECMAIN_FLAGS_DONT_OPEN_DEV))
+ {
+ g_supLibData = pPreInitData->Data;
+ g_fPreInited = true;
+ }
+
+ return VINF_SUCCESS;
+}
+
+
+SUPR3DECL(int) SUPR3InitEx(uint32_t fFlags, PSUPDRVSESSION *ppSession)
+{
+ /*
+ * Perform some sanity checks.
+ * (Got some trouble with compile time member alignment assertions.)
+ */
+ Assert(!(RT_UOFFSETOF(SUPGLOBALINFOPAGE, u64NanoTSLastUpdateHz) & 0x7));
+ Assert(!(RT_UOFFSETOF(SUPGLOBALINFOPAGE, aCPUs) & 0x1f));
+ Assert(!(RT_UOFFSETOF(SUPGLOBALINFOPAGE, aCPUs[1]) & 0x1f));
+ Assert(!(RT_UOFFSETOF(SUPGLOBALINFOPAGE, aCPUs[0].u64NanoTS) & 0x7));
+ Assert(!(RT_UOFFSETOF(SUPGLOBALINFOPAGE, aCPUs[0].u64TSC) & 0x7));
+ Assert(!(RT_UOFFSETOF(SUPGLOBALINFOPAGE, aCPUs[0].u64CpuHz) & 0x7));
+
+#ifdef VBOX_WITH_DRIVERLESS_FORCED
+ fFlags |= SUPR3INIT_F_DRIVERLESS;
+ fFlags &= ~SUPR3INIT_F_UNRESTRICTED;
+#endif
+
+ /*
+ * Check if already initialized.
+ */
+ if (ppSession)
+ *ppSession = g_pSession;
+ if (g_cInits++ > 0)
+ {
+ if ( (fFlags & SUPR3INIT_F_UNRESTRICTED)
+ && !g_supLibData.fUnrestricted
+ && !g_supLibData.fDriverless)
+ {
+ g_cInits--;
+ if (ppSession)
+ *ppSession = NIL_RTR0PTR;
+ return VERR_VM_DRIVER_NOT_ACCESSIBLE; /** @todo different status code? */
+ }
+ return VINF_SUCCESS;
+ }
+
+ /*
+ * Check for fake mode.
+ *
+ * Fake mode is used when we're doing smoke testing and debugging.
+ * It's also useful on platforms where we haven't root access or which
+ * we haven't ported the support driver to.
+ */
+ if (g_uSupFakeMode == ~0U)
+ {
+ const char *psz = RTEnvGet("VBOX_SUPLIB_FAKE");
+ if (psz && !strcmp(psz, "fake"))
+ ASMAtomicCmpXchgU32(&g_uSupFakeMode, 1, ~0U);
+ else
+ ASMAtomicCmpXchgU32(&g_uSupFakeMode, 0, ~0U);
+ }
+ if (RT_UNLIKELY(g_uSupFakeMode))
+ return supInitFake(ppSession);
+
+ /*
+ * Open the support driver.
+ */
+ SUPINITOP enmWhat = kSupInitOp_Driver;
+ int rc = suplibOsInit(&g_supLibData, g_fPreInited, fFlags, &enmWhat, NULL);
+ if (RT_SUCCESS(rc) && !g_supLibData.fDriverless)
+ {
+ /*
+ * Negotiate the cookie.
+ */
+ SUPCOOKIE CookieReq;
+ memset(&CookieReq, 0xff, sizeof(CookieReq));
+ CookieReq.Hdr.u32Cookie = SUPCOOKIE_INITIAL_COOKIE;
+ CookieReq.Hdr.u32SessionCookie = RTRandU32();
+ CookieReq.Hdr.cbIn = SUP_IOCTL_COOKIE_SIZE_IN;
+ CookieReq.Hdr.cbOut = SUP_IOCTL_COOKIE_SIZE_OUT;
+ CookieReq.Hdr.fFlags = SUPREQHDR_FLAGS_DEFAULT;
+ CookieReq.Hdr.rc = VERR_INTERNAL_ERROR;
+ strcpy(CookieReq.u.In.szMagic, SUPCOOKIE_MAGIC);
+ CookieReq.u.In.u32ReqVersion = SUPDRV_IOC_VERSION;
+ const uint32_t uMinVersion = (SUPDRV_IOC_VERSION & 0xffff0000) == 0x00330000
+ ? 0x00330004
+ : SUPDRV_IOC_VERSION & 0xffff0000;
+ CookieReq.u.In.u32MinVersion = uMinVersion;
+ rc = suplibOsIOCtl(&g_supLibData, SUP_IOCTL_COOKIE, &CookieReq, SUP_IOCTL_COOKIE_SIZE);
+ if ( RT_SUCCESS(rc)
+ && RT_SUCCESS(CookieReq.Hdr.rc))
+ {
+ g_uSupSessionVersion = CookieReq.u.Out.u32SessionVersion;
+ if ( (CookieReq.u.Out.u32SessionVersion & 0xffff0000) == (SUPDRV_IOC_VERSION & 0xffff0000)
+ && CookieReq.u.Out.u32SessionVersion >= uMinVersion)
+ {
+ /*
+ * Query the functions.
+ */
+ PSUPQUERYFUNCS pFuncsReq = NULL;
+ if (g_supLibData.fUnrestricted)
+ {
+ pFuncsReq = (PSUPQUERYFUNCS)RTMemAllocZ(SUP_IOCTL_QUERY_FUNCS_SIZE(CookieReq.u.Out.cFunctions));
+ if (pFuncsReq)
+ {
+ pFuncsReq->Hdr.u32Cookie = CookieReq.u.Out.u32Cookie;
+ pFuncsReq->Hdr.u32SessionCookie = CookieReq.u.Out.u32SessionCookie;
+ pFuncsReq->Hdr.cbIn = SUP_IOCTL_QUERY_FUNCS_SIZE_IN;
+ pFuncsReq->Hdr.cbOut = SUP_IOCTL_QUERY_FUNCS_SIZE_OUT(CookieReq.u.Out.cFunctions);
+ pFuncsReq->Hdr.fFlags = SUPREQHDR_FLAGS_DEFAULT;
+ pFuncsReq->Hdr.rc = VERR_INTERNAL_ERROR;
+ rc = suplibOsIOCtl(&g_supLibData, SUP_IOCTL_QUERY_FUNCS(CookieReq.u.Out.cFunctions), pFuncsReq,
+ SUP_IOCTL_QUERY_FUNCS_SIZE(CookieReq.u.Out.cFunctions));
+ if (RT_SUCCESS(rc))
+ rc = pFuncsReq->Hdr.rc;
+ if (RT_SUCCESS(rc))
+ {
+ /*
+ * Map the GIP into userspace.
+ */
+ Assert(!g_pSUPGlobalInfoPage);
+ SUPGIPMAP GipMapReq;
+ GipMapReq.Hdr.u32Cookie = CookieReq.u.Out.u32Cookie;
+ GipMapReq.Hdr.u32SessionCookie = CookieReq.u.Out.u32SessionCookie;
+ GipMapReq.Hdr.cbIn = SUP_IOCTL_GIP_MAP_SIZE_IN;
+ GipMapReq.Hdr.cbOut = SUP_IOCTL_GIP_MAP_SIZE_OUT;
+ GipMapReq.Hdr.fFlags = SUPREQHDR_FLAGS_DEFAULT;
+ GipMapReq.Hdr.rc = VERR_INTERNAL_ERROR;
+ GipMapReq.u.Out.HCPhysGip = NIL_RTHCPHYS;
+ GipMapReq.u.Out.pGipR0 = NIL_RTR0PTR;
+ GipMapReq.u.Out.pGipR3 = NULL;
+ rc = suplibOsIOCtl(&g_supLibData, SUP_IOCTL_GIP_MAP, &GipMapReq, SUP_IOCTL_GIP_MAP_SIZE);
+ if (RT_SUCCESS(rc))
+ rc = GipMapReq.Hdr.rc;
+ if (RT_SUCCESS(rc))
+ {
+ /*
+ * Set the GIP globals.
+ */
+ AssertRelease(GipMapReq.u.Out.pGipR3->u32Magic == SUPGLOBALINFOPAGE_MAGIC);
+ AssertRelease(GipMapReq.u.Out.pGipR3->u32Version >= SUPGLOBALINFOPAGE_VERSION);
+
+ ASMAtomicXchgSize(&g_HCPhysSUPGlobalInfoPage, GipMapReq.u.Out.HCPhysGip);
+ ASMAtomicCmpXchgPtr((void * volatile *)&g_pSUPGlobalInfoPage, GipMapReq.u.Out.pGipR3, NULL);
+ ASMAtomicCmpXchgPtr((void * volatile *)&g_pSUPGlobalInfoPageR0, (void *)GipMapReq.u.Out.pGipR0, NULL);
+ }
+ }
+ }
+ else
+ rc = VERR_NO_MEMORY;
+ }
+
+ if (RT_SUCCESS(rc))
+ {
+ /*
+ * Set the globals and return success.
+ */
+ g_u32Cookie = CookieReq.u.Out.u32Cookie;
+ g_u32SessionCookie = CookieReq.u.Out.u32SessionCookie;
+ g_pSession = CookieReq.u.Out.pSession;
+ g_pSupFunctions = pFuncsReq;
+ if (ppSession)
+ *ppSession = CookieReq.u.Out.pSession;
+ return VINF_SUCCESS;
+ }
+
+ /* bailout */
+ RTMemFree(pFuncsReq);
+ }
+ else
+ {
+ LogRel(("Support driver version mismatch: SessionVersion=%#x DriverVersion=%#x ClientVersion=%#x MinVersion=%#x\n",
+ CookieReq.u.Out.u32SessionVersion, CookieReq.u.Out.u32DriverVersion, SUPDRV_IOC_VERSION, uMinVersion));
+ rc = VERR_VM_DRIVER_VERSION_MISMATCH;
+ }
+ }
+ else
+ {
+ if (RT_SUCCESS(rc))
+ {
+ rc = CookieReq.Hdr.rc;
+ LogRel(("Support driver version mismatch: DriverVersion=%#x ClientVersion=%#x rc=%Rrc\n",
+ CookieReq.u.Out.u32DriverVersion, SUPDRV_IOC_VERSION, rc));
+ if (rc != VERR_VM_DRIVER_VERSION_MISMATCH)
+ rc = VERR_VM_DRIVER_VERSION_MISMATCH;
+ }
+ else
+ {
+ /* for pre 0x00060000 drivers */
+ LogRel(("Support driver version mismatch: DriverVersion=too-old ClientVersion=%#x\n", SUPDRV_IOC_VERSION));
+ rc = VERR_VM_DRIVER_VERSION_MISMATCH;
+ }
+ }
+
+ suplibOsTerm(&g_supLibData);
+ }
+ else if (RT_SUCCESS(rc))
+ {
+ /*
+ * Driverless initialization.
+ */
+ Assert(fFlags & SUPR3INIT_F_DRIVERLESS_MASK);
+ LogRel(("SUP: In driverless mode.\n"));
+ return VINF_SUCCESS;
+ }
+
+ g_cInits--;
+
+ return rc;
+}
+
+
+SUPR3DECL(int) SUPR3Init(PSUPDRVSESSION *ppSession)
+{
+#ifndef VBOX_WITH_DRIVERLESS_FORCED
+ return SUPR3InitEx(SUPR3INIT_F_UNRESTRICTED, ppSession);
+#else
+ return SUPR3InitEx(SUPR3INIT_F_DRIVERLESS, ppSession);
+#endif
+}
+
+/**
+ * Fake mode init.
+ */
+static int supInitFake(PSUPDRVSESSION *ppSession)
+{
+ Log(("SUP: Fake mode!\n"));
+ static const SUPFUNC s_aFakeFunctions[] =
+ {
+ /* name 0, function */
+ { "SUPR0AbsIs64bit", 0, 0 },
+ { "SUPR0Abs64bitKernelCS", 0, 0 },
+ { "SUPR0Abs64bitKernelSS", 0, 0 },
+ { "SUPR0Abs64bitKernelDS", 0, 0 },
+ { "SUPR0AbsKernelCS", 0, 8 },
+ { "SUPR0AbsKernelSS", 0, 16 },
+ { "SUPR0AbsKernelDS", 0, 16 },
+ { "SUPR0AbsKernelES", 0, 16 },
+ { "SUPR0AbsKernelFS", 0, 24 },
+ { "SUPR0AbsKernelGS", 0, 32 },
+ { "SUPR0ComponentRegisterFactory", 0, 0xefeefffd },
+ { "SUPR0ComponentDeregisterFactory", 0, 0xefeefffe },
+ { "SUPR0ComponentQueryFactory", 0, 0xefeeffff },
+ { "SUPR0ObjRegister", 0, 0xefef0000 },
+ { "SUPR0ObjAddRef", 0, 0xefef0001 },
+ { "SUPR0ObjAddRefEx", 0, 0xefef0001 },
+ { "SUPR0ObjRelease", 0, 0xefef0002 },
+ { "SUPR0ObjVerifyAccess", 0, 0xefef0003 },
+ { "SUPR0LockMem", 0, 0xefef0004 },
+ { "SUPR0UnlockMem", 0, 0xefef0005 },
+ { "SUPR0ContAlloc", 0, 0xefef0006 },
+ { "SUPR0ContFree", 0, 0xefef0007 },
+ { "SUPR0MemAlloc", 0, 0xefef0008 },
+ { "SUPR0MemGetPhys", 0, 0xefef0009 },
+ { "SUPR0MemFree", 0, 0xefef000a },
+ { "SUPR0Printf", 0, 0xefef000b },
+ { "SUPR0GetPagingMode", 0, 0xefef000c },
+ { "SUPR0EnableVTx", 0, 0xefef000e },
+ { "RTMemAlloc", 0, 0xefef000f },
+ { "RTMemAllocZ", 0, 0xefef0010 },
+ { "RTMemFree", 0, 0xefef0011 },
+ { "RTR0MemObjAddress", 0, 0xefef0012 },
+ { "RTR0MemObjAddressR3", 0, 0xefef0013 },
+ { "RTR0MemObjAllocPage", 0, 0xefef0014 },
+ { "RTR0MemObjAllocPhysNC", 0, 0xefef0015 },
+ { "RTR0MemObjAllocLow", 0, 0xefef0016 },
+ { "RTR0MemObjEnterPhys", 0, 0xefef0017 },
+ { "RTR0MemObjFree", 0, 0xefef0018 },
+ { "RTR0MemObjGetPagePhysAddr", 0, 0xefef0019 },
+ { "RTR0MemObjMapUser", 0, 0xefef001a },
+ { "RTR0MemObjMapKernel", 0, 0xefef001b },
+ { "RTR0MemObjMapKernelEx", 0, 0xefef001c },
+ { "RTMpGetArraySize", 0, 0xefef001c },
+ { "RTProcSelf", 0, 0xefef001d },
+ { "RTR0ProcHandleSelf", 0, 0xefef001e },
+ { "RTSemEventCreate", 0, 0xefef001f },
+ { "RTSemEventSignal", 0, 0xefef0020 },
+ { "RTSemEventWait", 0, 0xefef0021 },
+ { "RTSemEventWaitNoResume", 0, 0xefef0022 },
+ { "RTSemEventDestroy", 0, 0xefef0023 },
+ { "RTSemEventMultiCreate", 0, 0xefef0024 },
+ { "RTSemEventMultiSignal", 0, 0xefef0025 },
+ { "RTSemEventMultiReset", 0, 0xefef0026 },
+ { "RTSemEventMultiWait", 0, 0xefef0027 },
+ { "RTSemEventMultiWaitNoResume", 0, 0xefef0028 },
+ { "RTSemEventMultiDestroy", 0, 0xefef0029 },
+ { "RTSemFastMutexCreate", 0, 0xefef002a },
+ { "RTSemFastMutexDestroy", 0, 0xefef002b },
+ { "RTSemFastMutexRequest", 0, 0xefef002c },
+ { "RTSemFastMutexRelease", 0, 0xefef002d },
+ { "RTSpinlockCreate", 0, 0xefef002e },
+ { "RTSpinlockDestroy", 0, 0xefef002f },
+ { "RTSpinlockAcquire", 0, 0xefef0030 },
+ { "RTSpinlockRelease", 0, 0xefef0031 },
+ { "RTSpinlockAcquireNoInts", 0, 0xefef0032 },
+ { "RTTimeNanoTS", 0, 0xefef0034 },
+ { "RTTimeMillieTS", 0, 0xefef0035 },
+ { "RTTimeSystemNanoTS", 0, 0xefef0036 },
+ { "RTTimeSystemMillieTS", 0, 0xefef0037 },
+ { "RTThreadNativeSelf", 0, 0xefef0038 },
+ { "RTThreadSleep", 0, 0xefef0039 },
+ { "RTThreadYield", 0, 0xefef003a },
+ { "RTTimerCreate", 0, 0xefef003a },
+ { "RTTimerCreateEx", 0, 0xefef003a },
+ { "RTTimerDestroy", 0, 0xefef003a },
+ { "RTTimerStart", 0, 0xefef003a },
+ { "RTTimerStop", 0, 0xefef003a },
+ { "RTTimerChangeInterval", 0, 0xefef003a },
+ { "RTTimerGetSystemGranularity", 0, 0xefef003a },
+ { "RTTimerRequestSystemGranularity", 0, 0xefef003a },
+ { "RTTimerReleaseSystemGranularity", 0, 0xefef003a },
+ { "RTTimerCanDoHighResolution", 0, 0xefef003a },
+ { "RTLogDefaultInstance", 0, 0xefef003b },
+ { "RTLogRelGetDefaultInstance", 0, 0xefef003c },
+ { "RTLogSetDefaultInstanceThread", 0, 0xefef003d },
+ { "RTLogLogger", 0, 0xefef003e },
+ { "RTLogLoggerEx", 0, 0xefef003f },
+ { "RTLogLoggerExV", 0, 0xefef0040 },
+ { "RTAssertMsg1", 0, 0xefef0041 },
+ { "RTAssertMsg2", 0, 0xefef0042 },
+ { "RTAssertMsg2V", 0, 0xefef0043 },
+ { "SUPR0QueryVTCaps", 0, 0xefef0044 },
+ };
+
+ /* fake r0 functions. */
+ g_pSupFunctions = (PSUPQUERYFUNCS)RTMemAllocZ(SUP_IOCTL_QUERY_FUNCS_SIZE(RT_ELEMENTS(s_aFakeFunctions)));
+ if (g_pSupFunctions)
+ {
+ g_pSupFunctions->u.Out.cFunctions = RT_ELEMENTS(s_aFakeFunctions);
+ memcpy(&g_pSupFunctions->u.Out.aFunctions[0], &s_aFakeFunctions[0], sizeof(s_aFakeFunctions));
+ g_pSession = (PSUPDRVSESSION)(void *)g_pSupFunctions;
+ if (ppSession)
+ *ppSession = g_pSession;
+
+ /* fake the GIP. */
+ g_pSUPGlobalInfoPage = (PSUPGLOBALINFOPAGE)RTMemPageAllocZ(PAGE_SIZE);
+ if (g_pSUPGlobalInfoPage)
+ {
+ g_pSUPGlobalInfoPageR0 = g_pSUPGlobalInfoPage;
+ g_HCPhysSUPGlobalInfoPage = NIL_RTHCPHYS & ~(RTHCPHYS)PAGE_OFFSET_MASK;
+ /* the page is supposed to be invalid, so don't set the magic. */
+ return VINF_SUCCESS;
+ }
+
+ RTMemFree(g_pSupFunctions);
+ g_pSupFunctions = NULL;
+ }
+ return VERR_NO_MEMORY;
+}
+
+
+SUPR3DECL(int) SUPR3Term(bool fForced)
+{
+ /*
+ * Verify state.
+ */
+ AssertMsg(g_cInits > 0, ("SUPR3Term() is called before SUPR3Init()!\n"));
+ if (g_cInits == 0)
+ return VERR_WRONG_ORDER;
+ if (g_cInits == 1 || fForced)
+ {
+ /*
+ * NULL the GIP pointer.
+ */
+ if (g_pSUPGlobalInfoPage)
+ {
+ ASMAtomicWriteNullPtr((void * volatile *)&g_pSUPGlobalInfoPage);
+ ASMAtomicWriteNullPtr((void * volatile *)&g_pSUPGlobalInfoPageR0);
+ ASMAtomicWriteU64(&g_HCPhysSUPGlobalInfoPage, NIL_RTHCPHYS);
+ /* just a little safe guard against threads using the page. */
+ RTThreadSleep(50);
+ }
+
+ /*
+ * Close the support driver.
+ */
+ int rc = suplibOsTerm(&g_supLibData);
+ if (rc)
+ return rc;
+
+ g_supLibData.hDevice = SUP_HDEVICE_NIL;
+ g_supLibData.fUnrestricted = true;
+ g_supLibData.fDriverless = false;
+ g_u32Cookie = 0;
+ g_u32SessionCookie = 0;
+ g_cInits = 0;
+ }
+ else
+ g_cInits--;
+
+ return 0;
+}
+
+
+SUPR3DECL(bool) SUPR3IsDriverless(void)
+{
+ /* Assert(g_cInits > 0); - tstSSM does not initialize SUP, but SSM calls to
+ check status, so return driverless if not initialized. */
+ return g_supLibData.fDriverless || g_cInits == 0;
+}
+
+
+SUPR3DECL(SUPPAGINGMODE) SUPR3GetPagingMode(void)
+{
+ /*
+ * Deal with driverless first.
+ */
+ if (g_supLibData.fDriverless)
+#if defined(RT_ARCH_AMD64)
+ return SUPPAGINGMODE_AMD64_GLOBAL_NX;
+#elif defined(RT_ARCH_X86)
+ return SUPPAGINGMODE_32_BIT_GLOBAL;
+#else
+ return SUPPAGINGMODE_INVALID;
+#endif
+
+ /*
+ * Issue IOCtl to the SUPDRV kernel module.
+ */
+ SUPGETPAGINGMODE Req;
+ Req.Hdr.u32Cookie = g_u32Cookie;
+ Req.Hdr.u32SessionCookie = g_u32SessionCookie;
+ Req.Hdr.cbIn = SUP_IOCTL_GET_PAGING_MODE_SIZE_IN;
+ Req.Hdr.cbOut = SUP_IOCTL_GET_PAGING_MODE_SIZE_OUT;
+ Req.Hdr.fFlags = SUPREQHDR_FLAGS_DEFAULT;
+ Req.Hdr.rc = VERR_INTERNAL_ERROR;
+ int rc = suplibOsIOCtl(&g_supLibData, SUP_IOCTL_GET_PAGING_MODE, &Req, SUP_IOCTL_GET_PAGING_MODE_SIZE);
+ if ( RT_FAILURE(rc)
+ || RT_FAILURE(Req.Hdr.rc))
+ {
+ LogRel(("SUPR3GetPagingMode: %Rrc %Rrc\n", rc, Req.Hdr.rc));
+ Req.u.Out.enmMode = SUPPAGINGMODE_INVALID;
+ }
+
+ return Req.u.Out.enmMode;
+}
+
+
+/**
+ * For later.
+ */
+static int supCallVMMR0ExFake(PVMR0 pVMR0, unsigned uOperation, uint64_t u64Arg, PSUPVMMR0REQHDR pReqHdr)
+{
+ AssertMsgFailed(("%d\n", uOperation)); NOREF(pVMR0); NOREF(uOperation); NOREF(u64Arg); NOREF(pReqHdr);
+ return VERR_NOT_SUPPORTED;
+}
+
+
+SUPR3DECL(int) SUPR3CallVMMR0Fast(PVMR0 pVMR0, unsigned uOperation, VMCPUID idCpu)
+{
+ NOREF(pVMR0);
+ static const uintptr_t s_auFunctions[3] =
+ {
+ SUP_IOCTL_FAST_DO_HM_RUN,
+ SUP_IOCTL_FAST_DO_NEM_RUN,
+ SUP_IOCTL_FAST_DO_NOP,
+ };
+ AssertCompile(SUP_VMMR0_DO_HM_RUN == 0);
+ AssertCompile(SUP_VMMR0_DO_NEM_RUN == 1);
+ AssertCompile(SUP_VMMR0_DO_NOP == 2);
+ AssertMsgReturn(uOperation < RT_ELEMENTS(s_auFunctions), ("%#x\n", uOperation), VERR_INTERNAL_ERROR);
+ return suplibOsIOCtlFast(&g_supLibData, s_auFunctions[uOperation], idCpu);
+}
+
+
+SUPR3DECL(int) SUPR3CallVMMR0Ex(PVMR0 pVMR0, VMCPUID idCpu, unsigned uOperation, uint64_t u64Arg, PSUPVMMR0REQHDR pReqHdr)
+{
+ /*
+ * The following operations don't belong here.
+ */
+ AssertMsgReturn( uOperation != SUP_VMMR0_DO_HM_RUN
+ && uOperation != SUP_VMMR0_DO_NEM_RUN
+ && uOperation != SUP_VMMR0_DO_NOP,
+ ("%#x\n", uOperation),
+ VERR_INTERNAL_ERROR);
+
+ /* fake */
+ if (RT_UNLIKELY(g_uSupFakeMode))
+ return supCallVMMR0ExFake(pVMR0, uOperation, u64Arg, pReqHdr);
+
+ int rc;
+ if (!pReqHdr)
+ {
+ /* no data. */
+ SUPCALLVMMR0 Req;
+ Req.Hdr.u32Cookie = g_u32Cookie;
+ Req.Hdr.u32SessionCookie = g_u32SessionCookie;
+ Req.Hdr.cbIn = SUP_IOCTL_CALL_VMMR0_SIZE_IN(0);
+ Req.Hdr.cbOut = SUP_IOCTL_CALL_VMMR0_SIZE_OUT(0);
+ Req.Hdr.fFlags = SUPREQHDR_FLAGS_DEFAULT;
+ Req.Hdr.rc = VERR_INTERNAL_ERROR;
+ Req.u.In.pVMR0 = pVMR0;
+ Req.u.In.idCpu = idCpu;
+ Req.u.In.uOperation = uOperation;
+ Req.u.In.u64Arg = u64Arg;
+ rc = suplibOsIOCtl(&g_supLibData, SUP_IOCTL_CALL_VMMR0(0), &Req, SUP_IOCTL_CALL_VMMR0_SIZE(0));
+ if (RT_SUCCESS(rc))
+ rc = Req.Hdr.rc;
+ }
+ else if (SUP_IOCTL_CALL_VMMR0_SIZE(pReqHdr->cbReq) < _4K) /* FreeBSD won't copy more than 4K. */
+ {
+ AssertPtrReturn(pReqHdr, VERR_INVALID_POINTER);
+ AssertReturn(pReqHdr->u32Magic == SUPVMMR0REQHDR_MAGIC, VERR_INVALID_MAGIC);
+ const size_t cbReq = pReqHdr->cbReq;
+
+ PSUPCALLVMMR0 pReq = (PSUPCALLVMMR0)alloca(SUP_IOCTL_CALL_VMMR0_SIZE(cbReq));
+ pReq->Hdr.u32Cookie = g_u32Cookie;
+ pReq->Hdr.u32SessionCookie = g_u32SessionCookie;
+ pReq->Hdr.cbIn = SUP_IOCTL_CALL_VMMR0_SIZE_IN(cbReq);
+ pReq->Hdr.cbOut = SUP_IOCTL_CALL_VMMR0_SIZE_OUT(cbReq);
+ pReq->Hdr.fFlags = SUPREQHDR_FLAGS_DEFAULT;
+ pReq->Hdr.rc = VERR_INTERNAL_ERROR;
+ pReq->u.In.pVMR0 = pVMR0;
+ pReq->u.In.idCpu = idCpu;
+ pReq->u.In.uOperation = uOperation;
+ pReq->u.In.u64Arg = u64Arg;
+ memcpy(&pReq->abReqPkt[0], pReqHdr, cbReq);
+ rc = suplibOsIOCtl(&g_supLibData, SUP_IOCTL_CALL_VMMR0(cbReq), pReq, SUP_IOCTL_CALL_VMMR0_SIZE(cbReq));
+ if (RT_SUCCESS(rc))
+ rc = pReq->Hdr.rc;
+ memcpy(pReqHdr, &pReq->abReqPkt[0], cbReq);
+ }
+ else if (pReqHdr->cbReq <= _512K)
+ {
+ AssertPtrReturn(pReqHdr, VERR_INVALID_POINTER);
+ AssertReturn(pReqHdr->u32Magic == SUPVMMR0REQHDR_MAGIC, VERR_INVALID_MAGIC);
+ const size_t cbReq = pReqHdr->cbReq;
+
+ PSUPCALLVMMR0 pReq = (PSUPCALLVMMR0)RTMemTmpAlloc(SUP_IOCTL_CALL_VMMR0_BIG_SIZE(cbReq));
+ pReq->Hdr.u32Cookie = g_u32Cookie;
+ pReq->Hdr.u32SessionCookie = g_u32SessionCookie;
+ pReq->Hdr.cbIn = SUP_IOCTL_CALL_VMMR0_BIG_SIZE_IN(cbReq);
+ pReq->Hdr.cbOut = SUP_IOCTL_CALL_VMMR0_BIG_SIZE_OUT(cbReq);
+ pReq->Hdr.fFlags = SUPREQHDR_FLAGS_DEFAULT;
+ pReq->Hdr.rc = VERR_INTERNAL_ERROR;
+ pReq->u.In.pVMR0 = pVMR0;
+ pReq->u.In.idCpu = idCpu;
+ pReq->u.In.uOperation = uOperation;
+ pReq->u.In.u64Arg = u64Arg;
+ memcpy(&pReq->abReqPkt[0], pReqHdr, cbReq);
+ rc = suplibOsIOCtl(&g_supLibData, SUP_IOCTL_CALL_VMMR0_BIG, pReq, SUP_IOCTL_CALL_VMMR0_BIG_SIZE(cbReq));
+ if (RT_SUCCESS(rc))
+ rc = pReq->Hdr.rc;
+ memcpy(pReqHdr, &pReq->abReqPkt[0], cbReq);
+ RTMemTmpFree(pReq);
+ }
+ else
+ AssertMsgFailedReturn(("cbReq=%#x\n", pReqHdr->cbReq), VERR_OUT_OF_RANGE);
+ return rc;
+}
+
+
+SUPR3DECL(int) SUPR3CallVMMR0(PVMR0 pVMR0, VMCPUID idCpu, unsigned uOperation, void *pvArg)
+{
+ /*
+ * The following operations don't belong here.
+ */
+ AssertMsgReturn( uOperation != SUP_VMMR0_DO_HM_RUN
+ && uOperation != SUP_VMMR0_DO_NEM_RUN
+ && uOperation != SUP_VMMR0_DO_NOP,
+ ("%#x\n", uOperation),
+ VERR_INTERNAL_ERROR);
+ return SUPR3CallVMMR0Ex(pVMR0, idCpu, uOperation, (uintptr_t)pvArg, NULL);
+}
+
+
+SUPR3DECL(int) SUPR3SetVMForFastIOCtl(PVMR0 pVMR0)
+{
+ if (RT_UNLIKELY(g_uSupFakeMode))
+ return VINF_SUCCESS;
+
+ SUPSETVMFORFAST Req;
+ Req.Hdr.u32Cookie = g_u32Cookie;
+ Req.Hdr.u32SessionCookie = g_u32SessionCookie;
+ Req.Hdr.cbIn = SUP_IOCTL_SET_VM_FOR_FAST_SIZE_IN;
+ Req.Hdr.cbOut = SUP_IOCTL_SET_VM_FOR_FAST_SIZE_OUT;
+ Req.Hdr.fFlags = SUPREQHDR_FLAGS_DEFAULT;
+ Req.Hdr.rc = VERR_INTERNAL_ERROR;
+ Req.u.In.pVMR0 = pVMR0;
+ int rc = suplibOsIOCtl(&g_supLibData, SUP_IOCTL_SET_VM_FOR_FAST, &Req, SUP_IOCTL_SET_VM_FOR_FAST_SIZE);
+ if (RT_SUCCESS(rc))
+ rc = Req.Hdr.rc;
+ return rc;
+}
+
+
+SUPR3DECL(int) SUPR3CallR0Service(const char *pszService, size_t cchService, uint32_t uOperation, uint64_t u64Arg, PSUPR0SERVICEREQHDR pReqHdr)
+{
+ AssertReturn(cchService < RT_SIZEOFMEMB(SUPCALLSERVICE, u.In.szName), VERR_INVALID_PARAMETER);
+ Assert(strlen(pszService) == cchService);
+
+ /* fake */
+ if (RT_UNLIKELY(g_uSupFakeMode))
+ return VERR_NOT_SUPPORTED;
+
+ int rc;
+ if (!pReqHdr)
+ {
+ /* no data. */
+ SUPCALLSERVICE Req;
+ Req.Hdr.u32Cookie = g_u32Cookie;
+ Req.Hdr.u32SessionCookie = g_u32SessionCookie;
+ Req.Hdr.cbIn = SUP_IOCTL_CALL_SERVICE_SIZE_IN(0);
+ Req.Hdr.cbOut = SUP_IOCTL_CALL_SERVICE_SIZE_OUT(0);
+ Req.Hdr.fFlags = SUPREQHDR_FLAGS_DEFAULT;
+ Req.Hdr.rc = VERR_INTERNAL_ERROR;
+ memcpy(Req.u.In.szName, pszService, cchService);
+ Req.u.In.szName[cchService] = '\0';
+ Req.u.In.uOperation = uOperation;
+ Req.u.In.u64Arg = u64Arg;
+ rc = suplibOsIOCtl(&g_supLibData, SUP_IOCTL_CALL_SERVICE(0), &Req, SUP_IOCTL_CALL_SERVICE_SIZE(0));
+ if (RT_SUCCESS(rc))
+ rc = Req.Hdr.rc;
+ }
+ else if (SUP_IOCTL_CALL_SERVICE_SIZE(pReqHdr->cbReq) < _4K) /* FreeBSD won't copy more than 4K. */
+ {
+ AssertPtrReturn(pReqHdr, VERR_INVALID_POINTER);
+ AssertReturn(pReqHdr->u32Magic == SUPR0SERVICEREQHDR_MAGIC, VERR_INVALID_MAGIC);
+ const size_t cbReq = pReqHdr->cbReq;
+
+ PSUPCALLSERVICE pReq = (PSUPCALLSERVICE)alloca(SUP_IOCTL_CALL_SERVICE_SIZE(cbReq));
+ pReq->Hdr.u32Cookie = g_u32Cookie;
+ pReq->Hdr.u32SessionCookie = g_u32SessionCookie;
+ pReq->Hdr.cbIn = SUP_IOCTL_CALL_SERVICE_SIZE_IN(cbReq);
+ pReq->Hdr.cbOut = SUP_IOCTL_CALL_SERVICE_SIZE_OUT(cbReq);
+ pReq->Hdr.fFlags = SUPREQHDR_FLAGS_DEFAULT;
+ pReq->Hdr.rc = VERR_INTERNAL_ERROR;
+ memcpy(pReq->u.In.szName, pszService, cchService);
+ pReq->u.In.szName[cchService] = '\0';
+ pReq->u.In.uOperation = uOperation;
+ pReq->u.In.u64Arg = u64Arg;
+ memcpy(&pReq->abReqPkt[0], pReqHdr, cbReq);
+ rc = suplibOsIOCtl(&g_supLibData, SUP_IOCTL_CALL_SERVICE(cbReq), pReq, SUP_IOCTL_CALL_SERVICE_SIZE(cbReq));
+ if (RT_SUCCESS(rc))
+ rc = pReq->Hdr.rc;
+ memcpy(pReqHdr, &pReq->abReqPkt[0], cbReq);
+ }
+ else /** @todo may have to remove the size limits one this request... */
+ AssertMsgFailedReturn(("cbReq=%#x\n", pReqHdr->cbReq), VERR_INTERNAL_ERROR);
+ return rc;
+}
+
+
+/**
+ * Worker for the SUPR3Logger* APIs.
+ *
+ * @returns VBox status code.
+ * @param enmWhich Which logger.
+ * @param fWhat What to do with the logger.
+ * @param pszFlags The flags settings.
+ * @param pszGroups The groups settings.
+ * @param pszDest The destination specificier.
+ */
+static int supR3LoggerSettings(SUPLOGGER enmWhich, uint32_t fWhat, const char *pszFlags, const char *pszGroups, const char *pszDest)
+{
+ uint32_t const cchFlags = pszFlags ? (uint32_t)strlen(pszFlags) : 0;
+ uint32_t const cchGroups = pszGroups ? (uint32_t)strlen(pszGroups) : 0;
+ uint32_t const cchDest = pszDest ? (uint32_t)strlen(pszDest) : 0;
+ uint32_t const cbStrTab = cchFlags + !!cchFlags
+ + cchGroups + !!cchGroups
+ + cchDest + !!cchDest
+ + (!cchFlags && !cchGroups && !cchDest);
+
+ PSUPLOGGERSETTINGS pReq = (PSUPLOGGERSETTINGS)alloca(SUP_IOCTL_LOGGER_SETTINGS_SIZE(cbStrTab));
+ pReq->Hdr.u32Cookie = g_u32Cookie;
+ pReq->Hdr.u32SessionCookie = g_u32SessionCookie;
+ pReq->Hdr.cbIn = SUP_IOCTL_LOGGER_SETTINGS_SIZE_IN(cbStrTab);
+ pReq->Hdr.cbOut = SUP_IOCTL_LOGGER_SETTINGS_SIZE_OUT;
+ pReq->Hdr.fFlags= SUPREQHDR_FLAGS_DEFAULT;
+ pReq->Hdr.rc = VERR_INTERNAL_ERROR;
+ switch (enmWhich)
+ {
+ case SUPLOGGER_DEBUG: pReq->u.In.fWhich = SUPLOGGERSETTINGS_WHICH_DEBUG; break;
+ case SUPLOGGER_RELEASE: pReq->u.In.fWhich = SUPLOGGERSETTINGS_WHICH_RELEASE; break;
+ default:
+ return VERR_INVALID_PARAMETER;
+ }
+ pReq->u.In.fWhat = fWhat;
+
+ uint32_t off = 0;
+ if (cchFlags)
+ {
+ pReq->u.In.offFlags = off;
+ memcpy(&pReq->u.In.szStrings[off], pszFlags, cchFlags + 1);
+ off += cchFlags + 1;
+ }
+ else
+ pReq->u.In.offFlags = cbStrTab - 1;
+
+ if (cchGroups)
+ {
+ pReq->u.In.offGroups = off;
+ memcpy(&pReq->u.In.szStrings[off], pszGroups, cchGroups + 1);
+ off += cchGroups + 1;
+ }
+ else
+ pReq->u.In.offGroups = cbStrTab - 1;
+
+ if (cchDest)
+ {
+ pReq->u.In.offDestination = off;
+ memcpy(&pReq->u.In.szStrings[off], pszDest, cchDest + 1);
+ off += cchDest + 1;
+ }
+ else
+ pReq->u.In.offDestination = cbStrTab - 1;
+
+ if (!off)
+ {
+ pReq->u.In.szStrings[0] = '\0';
+ off++;
+ }
+ Assert(off == cbStrTab);
+ Assert(pReq->u.In.szStrings[cbStrTab - 1] == '\0');
+
+
+ int rc = suplibOsIOCtl(&g_supLibData, SUP_IOCTL_LOGGER_SETTINGS(cbStrTab), pReq, SUP_IOCTL_LOGGER_SETTINGS_SIZE(cbStrTab));
+ if (RT_SUCCESS(rc))
+ rc = pReq->Hdr.rc;
+ return rc;
+}
+
+
+SUPR3DECL(int) SUPR3LoggerSettings(SUPLOGGER enmWhich, const char *pszFlags, const char *pszGroups, const char *pszDest)
+{
+ return supR3LoggerSettings(enmWhich, SUPLOGGERSETTINGS_WHAT_SETTINGS, pszFlags, pszGroups, pszDest);
+}
+
+
+SUPR3DECL(int) SUPR3LoggerCreate(SUPLOGGER enmWhich, const char *pszFlags, const char *pszGroups, const char *pszDest)
+{
+ return supR3LoggerSettings(enmWhich, SUPLOGGERSETTINGS_WHAT_CREATE, pszFlags, pszGroups, pszDest);
+}
+
+
+SUPR3DECL(int) SUPR3LoggerDestroy(SUPLOGGER enmWhich)
+{
+ return supR3LoggerSettings(enmWhich, SUPLOGGERSETTINGS_WHAT_DESTROY, NULL, NULL, NULL);
+}
+
+
+SUPR3DECL(int) SUPR3PageAlloc(size_t cPages, uint32_t fFlags, void **ppvPages)
+{
+ /*
+ * Validate.
+ */
+ AssertPtrReturn(ppvPages, VERR_INVALID_POINTER);
+ *ppvPages = NULL;
+ AssertReturn(cPages > 0, VERR_PAGE_COUNT_OUT_OF_RANGE);
+ AssertReturn(!(fFlags & ~SUP_PAGE_ALLOC_F_VALID_MASK), VERR_INVALID_FLAGS);
+
+ /*
+ * Call OS specific worker.
+ */
+ return suplibOsPageAlloc(&g_supLibData, cPages, fFlags, ppvPages);
+}
+
+
+SUPR3DECL(int) SUPR3PageFree(void *pvPages, size_t cPages)
+{
+ /*
+ * Validate.
+ */
+ AssertPtrReturn(pvPages, VERR_INVALID_POINTER);
+ AssertReturn(cPages > 0, VERR_PAGE_COUNT_OUT_OF_RANGE);
+
+ /*
+ * Call OS specific worker.
+ */
+ return suplibOsPageFree(&g_supLibData, pvPages, cPages);
+}
+
+
+/**
+ * Locks down the physical memory backing a virtual memory
+ * range in the current process.
+ *
+ * @returns VBox status code.
+ * @param pvStart Start of virtual memory range.
+ * Must be page aligned.
+ * @param cPages Number of pages.
+ * @param paPages Where to store the physical page addresses returned.
+ * On entry this will point to an array of with cbMemory >> PAGE_SHIFT entries.
+ */
+SUPR3DECL(int) supR3PageLock(void *pvStart, size_t cPages, PSUPPAGE paPages)
+{
+ /*
+ * Validate.
+ */
+ AssertPtr(pvStart);
+ AssertMsg(RT_ALIGN_P(pvStart, PAGE_SIZE) == pvStart, ("pvStart (%p) must be page aligned\n", pvStart));
+ AssertPtr(paPages);
+
+ /* fake */
+ if (RT_UNLIKELY(g_uSupFakeMode))
+ {
+ RTHCPHYS Phys = (uintptr_t)pvStart + PAGE_SIZE * 1024;
+ size_t iPage = cPages;
+ while (iPage-- > 0)
+ paPages[iPage].Phys = Phys + (iPage << PAGE_SHIFT);
+ return VINF_SUCCESS;
+ }
+
+ /*
+ * Issue IOCtl to the SUPDRV kernel module.
+ */
+ int rc;
+ PSUPPAGELOCK pReq = (PSUPPAGELOCK)RTMemTmpAllocZ(SUP_IOCTL_PAGE_LOCK_SIZE(cPages));
+ if (RT_LIKELY(pReq))
+ {
+ pReq->Hdr.u32Cookie = g_u32Cookie;
+ pReq->Hdr.u32SessionCookie = g_u32SessionCookie;
+ pReq->Hdr.cbIn = SUP_IOCTL_PAGE_LOCK_SIZE_IN;
+ pReq->Hdr.cbOut = SUP_IOCTL_PAGE_LOCK_SIZE_OUT(cPages);
+ pReq->Hdr.fFlags = SUPREQHDR_FLAGS_MAGIC | SUPREQHDR_FLAGS_EXTRA_OUT;
+ pReq->Hdr.rc = VERR_INTERNAL_ERROR;
+ pReq->u.In.pvR3 = pvStart;
+ pReq->u.In.cPages = (uint32_t)cPages; AssertRelease(pReq->u.In.cPages == cPages);
+ rc = suplibOsIOCtl(&g_supLibData, SUP_IOCTL_PAGE_LOCK, pReq, SUP_IOCTL_PAGE_LOCK_SIZE(cPages));
+ if (RT_SUCCESS(rc))
+ rc = pReq->Hdr.rc;
+ if (RT_SUCCESS(rc))
+ {
+ for (uint32_t iPage = 0; iPage < cPages; iPage++)
+ {
+ paPages[iPage].uReserved = 0;
+ paPages[iPage].Phys = pReq->u.Out.aPages[iPage];
+ Assert(!(paPages[iPage].Phys & ~X86_PTE_PAE_PG_MASK));
+ }
+ }
+ RTMemTmpFree(pReq);
+ }
+ else
+ rc = VERR_NO_TMP_MEMORY;
+
+ return rc;
+}
+
+
+/**
+ * Releases locked down pages.
+ *
+ * @returns VBox status code.
+ * @param pvStart Start of virtual memory range previously locked
+ * down by SUPPageLock().
+ */
+SUPR3DECL(int) supR3PageUnlock(void *pvStart)
+{
+ /*
+ * Validate.
+ */
+ AssertPtr(pvStart);
+ AssertMsg(RT_ALIGN_P(pvStart, PAGE_SIZE) == pvStart, ("pvStart (%p) must be page aligned\n", pvStart));
+
+ /* fake */
+ if (RT_UNLIKELY(g_uSupFakeMode))
+ return VINF_SUCCESS;
+
+ /*
+ * Issue IOCtl to the SUPDRV kernel module.
+ */
+ SUPPAGEUNLOCK Req;
+ Req.Hdr.u32Cookie = g_u32Cookie;
+ Req.Hdr.u32SessionCookie = g_u32SessionCookie;
+ Req.Hdr.cbIn = SUP_IOCTL_PAGE_UNLOCK_SIZE_IN;
+ Req.Hdr.cbOut = SUP_IOCTL_PAGE_UNLOCK_SIZE_OUT;
+ Req.Hdr.fFlags = SUPREQHDR_FLAGS_DEFAULT;
+ Req.Hdr.rc = VERR_INTERNAL_ERROR;
+ Req.u.In.pvR3 = pvStart;
+ int rc = suplibOsIOCtl(&g_supLibData, SUP_IOCTL_PAGE_UNLOCK, &Req, SUP_IOCTL_PAGE_UNLOCK_SIZE);
+ if (RT_SUCCESS(rc))
+ rc = Req.Hdr.rc;
+ return rc;
+}
+
+
+SUPR3DECL(int) SUPR3LockDownLoader(PRTERRINFO pErrInfo)
+{
+ /* fake */
+ if (RT_UNLIKELY(g_uSupFakeMode))
+ return VINF_SUCCESS;
+
+ /*
+ * Lock down the module loader interface.
+ */
+ SUPREQHDR ReqHdr;
+ ReqHdr.u32Cookie = g_u32Cookie;
+ ReqHdr.u32SessionCookie = g_u32SessionCookie;
+ ReqHdr.cbIn = SUP_IOCTL_LDR_LOCK_DOWN_SIZE_IN;
+ ReqHdr.cbOut = SUP_IOCTL_LDR_LOCK_DOWN_SIZE_OUT;
+ ReqHdr.fFlags = SUPREQHDR_FLAGS_DEFAULT;
+ ReqHdr.rc = VERR_INTERNAL_ERROR;
+ int rc = suplibOsIOCtl(&g_supLibData, SUP_IOCTL_LDR_LOCK_DOWN, &ReqHdr, SUP_IOCTL_LDR_LOCK_DOWN_SIZE);
+ if (RT_FAILURE(rc))
+ return RTErrInfoSetF(pErrInfo, rc,
+ "SUPR3LockDownLoader: SUP_IOCTL_LDR_LOCK_DOWN ioctl returned %Rrc", rc);
+
+ return ReqHdr.rc;
+}
+
+
+/**
+ * Fallback for SUPR3PageAllocEx on systems where RTR0MemObjPhysAllocNC isn't
+ * supported.
+ */
+static int supPagePageAllocNoKernelFallback(size_t cPages, void **ppvPages, PSUPPAGE paPages)
+{
+ int rc = suplibOsPageAlloc(&g_supLibData, cPages, 0, ppvPages);
+ if (RT_SUCCESS(rc))
+ {
+ Assert(ASMMemIsZero(*ppvPages, cPages << PAGE_SHIFT));
+ if (!paPages)
+ paPages = (PSUPPAGE)alloca(sizeof(paPages[0]) * cPages);
+ rc = supR3PageLock(*ppvPages, cPages, paPages);
+ if (RT_FAILURE(rc))
+ suplibOsPageFree(&g_supLibData, *ppvPages, cPages);
+ }
+ return rc;
+}
+
+
+SUPR3DECL(int) SUPR3PageAllocEx(size_t cPages, uint32_t fFlags, void **ppvPages, PRTR0PTR pR0Ptr, PSUPPAGE paPages)
+{
+ /*
+ * Validate.
+ */
+ AssertPtrReturn(ppvPages, VERR_INVALID_POINTER);
+ *ppvPages = NULL;
+ AssertPtrNullReturn(pR0Ptr, VERR_INVALID_POINTER);
+ if (pR0Ptr)
+ *pR0Ptr = NIL_RTR0PTR;
+ AssertPtrNullReturn(paPages, VERR_INVALID_POINTER);
+ AssertMsgReturn(cPages > 0 && cPages <= VBOX_MAX_ALLOC_PAGE_COUNT, ("cPages=%zu\n", cPages), VERR_PAGE_COUNT_OUT_OF_RANGE);
+ AssertReturn(!fFlags, VERR_INVALID_FLAGS);
+
+ /*
+ * Deal with driverless mode first.
+ */
+ if (g_supLibData.fDriverless)
+ {
+ int rc = SUPR3PageAlloc(cPages, 0 /*fFlags*/, ppvPages);
+ Assert(RT_FAILURE(rc) || ASMMemIsZero(*ppvPages, cPages << PAGE_SHIFT));
+ if (pR0Ptr)
+ *pR0Ptr = NIL_RTR0PTR;
+ if (paPages)
+ for (size_t iPage = 0; iPage < cPages; iPage++)
+ {
+ paPages[iPage].uReserved = 0;
+ paPages[iPage].Phys = NIL_RTHCPHYS;
+ }
+ return rc;
+ }
+
+ /* Check that we've got a kernel connection so rtMemSaferSupR3AllocPages
+ can do fallback without first having to hit assertions. */
+ if (g_supLibData.hDevice != SUP_HDEVICE_NIL)
+ { /* likely */ }
+ else
+ return VERR_WRONG_ORDER;
+
+ /*
+ * Use fallback for non-R0 mapping?
+ */
+ if ( !pR0Ptr
+ && !g_fSupportsPageAllocNoKernel)
+ return supPagePageAllocNoKernelFallback(cPages, ppvPages, paPages);
+
+ /*
+ * Issue IOCtl to the SUPDRV kernel module.
+ */
+ int rc;
+ PSUPPAGEALLOCEX pReq = (PSUPPAGEALLOCEX)RTMemTmpAllocZ(SUP_IOCTL_PAGE_ALLOC_EX_SIZE(cPages));
+ if (pReq)
+ {
+ pReq->Hdr.u32Cookie = g_u32Cookie;
+ pReq->Hdr.u32SessionCookie = g_u32SessionCookie;
+ pReq->Hdr.cbIn = SUP_IOCTL_PAGE_ALLOC_EX_SIZE_IN;
+ pReq->Hdr.cbOut = SUP_IOCTL_PAGE_ALLOC_EX_SIZE_OUT(cPages);
+ pReq->Hdr.fFlags = SUPREQHDR_FLAGS_MAGIC | SUPREQHDR_FLAGS_EXTRA_OUT;
+ pReq->Hdr.rc = VERR_INTERNAL_ERROR;
+ pReq->u.In.cPages = (uint32_t)cPages; AssertRelease(pReq->u.In.cPages == cPages);
+ pReq->u.In.fKernelMapping = pR0Ptr != NULL;
+ pReq->u.In.fUserMapping = true;
+ pReq->u.In.fReserved0 = false;
+ pReq->u.In.fReserved1 = false;
+ rc = suplibOsIOCtl(&g_supLibData, SUP_IOCTL_PAGE_ALLOC_EX, pReq, SUP_IOCTL_PAGE_ALLOC_EX_SIZE(cPages));
+ if (RT_SUCCESS(rc))
+ {
+ rc = pReq->Hdr.rc;
+ if (RT_SUCCESS(rc))
+ {
+ *ppvPages = pReq->u.Out.pvR3;
+ if (pR0Ptr)
+ {
+ *pR0Ptr = pReq->u.Out.pvR0;
+ Assert(ASMMemIsZero(pReq->u.Out.pvR3, cPages << PAGE_SHIFT));
+#ifdef RT_OS_DARWIN /* HACK ALERT! */
+ supR3TouchPages(pReq->u.Out.pvR3, cPages);
+#endif
+ }
+ else
+ RT_BZERO(pReq->u.Out.pvR3, cPages << PAGE_SHIFT);
+
+ if (paPages)
+ for (size_t iPage = 0; iPage < cPages; iPage++)
+ {
+ paPages[iPage].uReserved = 0;
+ paPages[iPage].Phys = pReq->u.Out.aPages[iPage];
+ Assert(!(paPages[iPage].Phys & ~X86_PTE_PAE_PG_MASK));
+ }
+ }
+ else if ( rc == VERR_NOT_SUPPORTED
+ && !pR0Ptr)
+ {
+ g_fSupportsPageAllocNoKernel = false;
+ rc = supPagePageAllocNoKernelFallback(cPages, ppvPages, paPages);
+ }
+ }
+
+ RTMemTmpFree(pReq);
+ }
+ else
+ rc = VERR_NO_TMP_MEMORY;
+ return rc;
+
+}
+
+
+SUPR3DECL(int) SUPR3PageMapKernel(void *pvR3, uint32_t off, uint32_t cb, uint32_t fFlags, PRTR0PTR pR0Ptr)
+{
+ /*
+ * Validate.
+ */
+ AssertPtrReturn(pvR3, VERR_INVALID_POINTER);
+ AssertPtrReturn(pR0Ptr, VERR_INVALID_POINTER);
+ Assert(!(off & PAGE_OFFSET_MASK));
+ Assert(!(cb & PAGE_OFFSET_MASK) && cb);
+ Assert(!fFlags);
+ *pR0Ptr = NIL_RTR0PTR;
+
+ /*
+ * Not a valid operation in driverless mode.
+ */
+ AssertReturn(g_supLibData.fDriverless, VERR_SUP_DRIVERLESS);
+
+ /*
+ * Issue IOCtl to the SUPDRV kernel module.
+ */
+ SUPPAGEMAPKERNEL Req;
+ Req.Hdr.u32Cookie = g_u32Cookie;
+ Req.Hdr.u32SessionCookie = g_u32SessionCookie;
+ Req.Hdr.cbIn = SUP_IOCTL_PAGE_MAP_KERNEL_SIZE_IN;
+ Req.Hdr.cbOut = SUP_IOCTL_PAGE_MAP_KERNEL_SIZE_OUT;
+ Req.Hdr.fFlags = SUPREQHDR_FLAGS_DEFAULT;
+ Req.Hdr.rc = VERR_INTERNAL_ERROR;
+ Req.u.In.pvR3 = pvR3;
+ Req.u.In.offSub = off;
+ Req.u.In.cbSub = cb;
+ Req.u.In.fFlags = fFlags;
+ int rc = suplibOsIOCtl(&g_supLibData, SUP_IOCTL_PAGE_MAP_KERNEL, &Req, SUP_IOCTL_PAGE_MAP_KERNEL_SIZE);
+ if (RT_SUCCESS(rc))
+ rc = Req.Hdr.rc;
+ if (RT_SUCCESS(rc))
+ *pR0Ptr = Req.u.Out.pvR0;
+ return rc;
+}
+
+
+SUPR3DECL(int) SUPR3PageProtect(void *pvR3, RTR0PTR R0Ptr, uint32_t off, uint32_t cb, uint32_t fProt)
+{
+ /*
+ * Validate.
+ */
+ AssertPtrReturn(pvR3, VERR_INVALID_POINTER);
+ Assert(!(off & PAGE_OFFSET_MASK));
+ Assert(!(cb & PAGE_OFFSET_MASK) && cb);
+ AssertReturn(!(fProt & ~(RTMEM_PROT_NONE | RTMEM_PROT_READ | RTMEM_PROT_WRITE | RTMEM_PROT_EXEC)), VERR_INVALID_PARAMETER);
+
+ /*
+ * Deal with driverless mode first.
+ */
+ if (g_supLibData.fDriverless)
+ return RTMemProtect((uint8_t *)pvR3 + off, cb, fProt);
+
+ /*
+ * Some OSes can do this from ring-3, so try that before we
+ * issue the IOCtl to the SUPDRV kernel module.
+ * (Yea, this isn't very nice, but just try get the job done for now.)
+ */
+#if !defined(RT_OS_SOLARIS)
+ RTMemProtect((uint8_t *)pvR3 + off, cb, fProt);
+#endif
+
+ SUPPAGEPROTECT Req;
+ Req.Hdr.u32Cookie = g_u32Cookie;
+ Req.Hdr.u32SessionCookie = g_u32SessionCookie;
+ Req.Hdr.cbIn = SUP_IOCTL_PAGE_PROTECT_SIZE_IN;
+ Req.Hdr.cbOut = SUP_IOCTL_PAGE_PROTECT_SIZE_OUT;
+ Req.Hdr.fFlags = SUPREQHDR_FLAGS_DEFAULT;
+ Req.Hdr.rc = VERR_INTERNAL_ERROR;
+ Req.u.In.pvR3 = pvR3;
+ Req.u.In.pvR0 = R0Ptr;
+ Req.u.In.offSub = off;
+ Req.u.In.cbSub = cb;
+ Req.u.In.fProt = fProt;
+ int rc = suplibOsIOCtl(&g_supLibData, SUP_IOCTL_PAGE_PROTECT, &Req, SUP_IOCTL_PAGE_PROTECT_SIZE);
+ if (RT_SUCCESS(rc))
+ rc = Req.Hdr.rc;
+ return rc;
+}
+
+
+SUPR3DECL(int) SUPR3PageFreeEx(void *pvPages, size_t cPages)
+{
+ /*
+ * Validate.
+ */
+ AssertPtrReturn(pvPages, VERR_INVALID_POINTER);
+ AssertReturn(cPages > 0, VERR_PAGE_COUNT_OUT_OF_RANGE);
+
+ /*
+ * Deal with driverless mode first.
+ */
+ if (g_supLibData.fDriverless)
+ {
+ SUPR3PageFree(pvPages, cPages);
+ return VINF_SUCCESS;
+ }
+
+ /*
+ * Try normal free first, then if it fails check if we're using the fallback
+ * for the allocations without kernel mappings and attempt unlocking it.
+ */
+ NOREF(cPages);
+ SUPPAGEFREE Req;
+ Req.Hdr.u32Cookie = g_u32Cookie;
+ Req.Hdr.u32SessionCookie = g_u32SessionCookie;
+ Req.Hdr.cbIn = SUP_IOCTL_PAGE_FREE_SIZE_IN;
+ Req.Hdr.cbOut = SUP_IOCTL_PAGE_FREE_SIZE_OUT;
+ Req.Hdr.fFlags = SUPREQHDR_FLAGS_DEFAULT;
+ Req.Hdr.rc = VERR_INTERNAL_ERROR;
+ Req.u.In.pvR3 = pvPages;
+ int rc = suplibOsIOCtl(&g_supLibData, SUP_IOCTL_PAGE_FREE, &Req, SUP_IOCTL_PAGE_FREE_SIZE);
+ if (RT_SUCCESS(rc))
+ {
+ rc = Req.Hdr.rc;
+ if ( rc == VERR_INVALID_PARAMETER
+ && !g_fSupportsPageAllocNoKernel)
+ {
+ int rc2 = supR3PageUnlock(pvPages);
+ if (RT_SUCCESS(rc2))
+ rc = suplibOsPageFree(&g_supLibData, pvPages, cPages);
+ }
+ }
+ return rc;
+}
+
+
+SUPR3DECL(void *) SUPR3ContAlloc(size_t cPages, PRTR0PTR pR0Ptr, PRTHCPHYS pHCPhys)
+{
+ /*
+ * Validate.
+ */
+ AssertPtrReturn(pHCPhys, NULL);
+ *pHCPhys = NIL_RTHCPHYS;
+ AssertPtrNullReturn(pR0Ptr, NULL);
+ if (pR0Ptr)
+ *pR0Ptr = NIL_RTR0PTR;
+ AssertPtrNullReturn(pHCPhys, NULL);
+ AssertMsgReturn(cPages > 0 && cPages < 256, ("cPages=%d must be > 0 and < 256\n", cPages), NULL);
+
+ /*
+ * Deal with driverless mode first.
+ */
+ if (g_supLibData.fDriverless)
+ {
+ void *pvPages = NULL;
+ int rc = SUPR3PageAlloc(cPages, 0 /*fFlags*/, &pvPages);
+ if (pR0Ptr)
+ *pR0Ptr = NIL_RTR0PTR;
+ if (pHCPhys)
+ *pHCPhys = NIL_RTHCPHYS;
+ return RT_SUCCESS(rc) ? pvPages : NULL;
+ }
+
+ /*
+ * Issue IOCtl to the SUPDRV kernel module.
+ */
+ SUPCONTALLOC Req;
+ Req.Hdr.u32Cookie = g_u32Cookie;
+ Req.Hdr.u32SessionCookie = g_u32SessionCookie;
+ Req.Hdr.cbIn = SUP_IOCTL_CONT_ALLOC_SIZE_IN;
+ Req.Hdr.cbOut = SUP_IOCTL_CONT_ALLOC_SIZE_OUT;
+ Req.Hdr.fFlags = SUPREQHDR_FLAGS_DEFAULT;
+ Req.Hdr.rc = VERR_INTERNAL_ERROR;
+ Req.u.In.cPages = (uint32_t)cPages;
+ int rc = suplibOsIOCtl(&g_supLibData, SUP_IOCTL_CONT_ALLOC, &Req, SUP_IOCTL_CONT_ALLOC_SIZE);
+ if ( RT_SUCCESS(rc)
+ && RT_SUCCESS(Req.Hdr.rc))
+ {
+ *pHCPhys = Req.u.Out.HCPhys;
+ if (pR0Ptr)
+ *pR0Ptr = Req.u.Out.pvR0;
+#ifdef RT_OS_DARWIN /* HACK ALERT! */
+ supR3TouchPages(Req.u.Out.pvR3, cPages);
+#endif
+ return Req.u.Out.pvR3;
+ }
+
+ return NULL;
+}
+
+
+SUPR3DECL(int) SUPR3ContFree(void *pv, size_t cPages)
+{
+ /*
+ * Validate.
+ */
+ if (!pv)
+ return VINF_SUCCESS;
+ AssertPtrReturn(pv, VERR_INVALID_POINTER);
+ AssertReturn(cPages > 0, VERR_PAGE_COUNT_OUT_OF_RANGE);
+
+ /*
+ * Deal with driverless mode first.
+ */
+ if (g_supLibData.fDriverless)
+ return SUPR3PageFree(pv, cPages);
+
+ /*
+ * Issue IOCtl to the SUPDRV kernel module.
+ */
+ SUPCONTFREE Req;
+ Req.Hdr.u32Cookie = g_u32Cookie;
+ Req.Hdr.u32SessionCookie = g_u32SessionCookie;
+ Req.Hdr.cbIn = SUP_IOCTL_CONT_FREE_SIZE_IN;
+ Req.Hdr.cbOut = SUP_IOCTL_CONT_FREE_SIZE_OUT;
+ Req.Hdr.fFlags = SUPREQHDR_FLAGS_DEFAULT;
+ Req.Hdr.rc = VERR_INTERNAL_ERROR;
+ Req.u.In.pvR3 = pv;
+ int rc = suplibOsIOCtl(&g_supLibData, SUP_IOCTL_CONT_FREE, &Req, SUP_IOCTL_CONT_FREE_SIZE);
+ if (RT_SUCCESS(rc))
+ rc = Req.Hdr.rc;
+ return rc;
+}
+
+
+SUPR3DECL(int) SUPR3LowAlloc(size_t cPages, void **ppvPages, PRTR0PTR ppvPagesR0, PSUPPAGE paPages)
+{
+ /*
+ * Validate.
+ */
+ AssertPtrReturn(ppvPages, VERR_INVALID_POINTER);
+ *ppvPages = NULL;
+ AssertPtrReturn(paPages, VERR_INVALID_POINTER);
+ AssertMsgReturn(cPages > 0 && cPages < 256, ("cPages=%d must be > 0 and < 256\n", cPages), VERR_PAGE_COUNT_OUT_OF_RANGE);
+
+ /* fake */
+ if (RT_UNLIKELY(g_uSupFakeMode))
+ {
+ *ppvPages = RTMemPageAllocZ((size_t)cPages * PAGE_SIZE);
+ if (!*ppvPages)
+ return VERR_NO_LOW_MEMORY;
+
+ /* fake physical addresses. */
+ RTHCPHYS Phys = (uintptr_t)*ppvPages + PAGE_SIZE * 1024;
+ size_t iPage = cPages;
+ while (iPage-- > 0)
+ paPages[iPage].Phys = Phys + (iPage << PAGE_SHIFT);
+ return VINF_SUCCESS;
+ }
+
+ /*
+ * Issue IOCtl to the SUPDRV kernel module.
+ */
+ int rc;
+ PSUPLOWALLOC pReq = (PSUPLOWALLOC)RTMemTmpAllocZ(SUP_IOCTL_LOW_ALLOC_SIZE(cPages));
+ if (pReq)
+ {
+ pReq->Hdr.u32Cookie = g_u32Cookie;
+ pReq->Hdr.u32SessionCookie = g_u32SessionCookie;
+ pReq->Hdr.cbIn = SUP_IOCTL_LOW_ALLOC_SIZE_IN;
+ pReq->Hdr.cbOut = SUP_IOCTL_LOW_ALLOC_SIZE_OUT(cPages);
+ pReq->Hdr.fFlags = SUPREQHDR_FLAGS_MAGIC | SUPREQHDR_FLAGS_EXTRA_OUT;
+ pReq->Hdr.rc = VERR_INTERNAL_ERROR;
+ pReq->u.In.cPages = (uint32_t)cPages; AssertRelease(pReq->u.In.cPages == cPages);
+ rc = suplibOsIOCtl(&g_supLibData, SUP_IOCTL_LOW_ALLOC, pReq, SUP_IOCTL_LOW_ALLOC_SIZE(cPages));
+ if (RT_SUCCESS(rc))
+ rc = pReq->Hdr.rc;
+ if (RT_SUCCESS(rc))
+ {
+ *ppvPages = pReq->u.Out.pvR3;
+ if (ppvPagesR0)
+ *ppvPagesR0 = pReq->u.Out.pvR0;
+ if (paPages)
+ for (size_t iPage = 0; iPage < cPages; iPage++)
+ {
+ paPages[iPage].uReserved = 0;
+ paPages[iPage].Phys = pReq->u.Out.aPages[iPage];
+ Assert(!(paPages[iPage].Phys & ~X86_PTE_PAE_PG_MASK));
+ Assert(paPages[iPage].Phys <= UINT32_C(0xfffff000));
+ }
+#ifdef RT_OS_DARWIN /* HACK ALERT! */
+ supR3TouchPages(pReq->u.Out.pvR3, cPages);
+#endif
+ }
+ RTMemTmpFree(pReq);
+ }
+ else
+ rc = VERR_NO_TMP_MEMORY;
+
+ return rc;
+}
+
+
+SUPR3DECL(int) SUPR3LowFree(void *pv, size_t cPages)
+{
+ /*
+ * Validate.
+ */
+ if (!pv)
+ return VINF_SUCCESS;
+ AssertPtrReturn(pv, VERR_INVALID_POINTER);
+ AssertReturn(cPages > 0, VERR_PAGE_COUNT_OUT_OF_RANGE);
+
+ /* fake */
+ if (RT_UNLIKELY(g_uSupFakeMode))
+ {
+ RTMemPageFree(pv, cPages * PAGE_SIZE);
+ return VINF_SUCCESS;
+ }
+
+ /*
+ * Issue IOCtl to the SUPDRV kernel module.
+ */
+ SUPCONTFREE Req;
+ Req.Hdr.u32Cookie = g_u32Cookie;
+ Req.Hdr.u32SessionCookie = g_u32SessionCookie;
+ Req.Hdr.cbIn = SUP_IOCTL_LOW_FREE_SIZE_IN;
+ Req.Hdr.cbOut = SUP_IOCTL_LOW_FREE_SIZE_OUT;
+ Req.Hdr.fFlags = SUPREQHDR_FLAGS_DEFAULT;
+ Req.Hdr.rc = VERR_INTERNAL_ERROR;
+ Req.u.In.pvR3 = pv;
+ int rc = suplibOsIOCtl(&g_supLibData, SUP_IOCTL_LOW_FREE, &Req, SUP_IOCTL_LOW_FREE_SIZE);
+ if (RT_SUCCESS(rc))
+ rc = Req.Hdr.rc;
+ return rc;
+}
+
+
+SUPR3DECL(int) SUPR3HardenedVerifyInit(void)
+{
+#ifdef RT_OS_WINDOWS
+ if (g_cInits == 0)
+ return suplibOsHardenedVerifyInit();
+#endif
+ return VINF_SUCCESS;
+}
+
+
+SUPR3DECL(int) SUPR3HardenedVerifyTerm(void)
+{
+#ifdef RT_OS_WINDOWS
+ if (g_cInits == 0)
+ return suplibOsHardenedVerifyTerm();
+#endif
+ return VINF_SUCCESS;
+}
+
+
+SUPR3DECL(int) SUPR3HardenedVerifyFile(const char *pszFilename, const char *pszMsg, PRTFILE phFile)
+{
+ /*
+ * Quick input validation.
+ */
+ AssertPtr(pszFilename);
+ AssertPtr(pszMsg);
+ AssertReturn(!phFile, VERR_NOT_IMPLEMENTED); /** @todo Implement this. The deal is that we make sure the
+ file is the same we verified after opening it. */
+ RT_NOREF2(pszFilename, pszMsg);
+
+ /*
+ * Only do the actual check in hardened builds.
+ */
+#ifdef VBOX_WITH_HARDENING
+ int rc = supR3HardenedVerifyFixedFile(pszFilename, false /* fFatal */);
+ if (RT_FAILURE(rc))
+ LogRel(("SUPR3HardenedVerifyFile: %s: Verification of \"%s\" failed, rc=%Rrc\n", pszMsg, pszFilename, rc));
+ return rc;
+#else
+ return VINF_SUCCESS;
+#endif
+}
+
+
+SUPR3DECL(int) SUPR3HardenedVerifySelf(const char *pszArgv0, bool fInternal, PRTERRINFO pErrInfo)
+{
+ /*
+ * Quick input validation.
+ */
+ AssertPtr(pszArgv0);
+ RTErrInfoClear(pErrInfo);
+
+ /*
+ * Get the executable image path as we need it for all the tests here.
+ */
+ char szExecPath[RTPATH_MAX];
+ if (!RTProcGetExecutablePath(szExecPath, sizeof(szExecPath)))
+ return RTErrInfoSet(pErrInfo, VERR_INTERNAL_ERROR_2, "RTProcGetExecutablePath failed");
+
+ int rc;
+ if (fInternal)
+ {
+ /*
+ * Internal applications must be launched directly without any PATH
+ * searching involved.
+ */
+ if (RTPathCompare(pszArgv0, szExecPath) != 0)
+ return RTErrInfoSetF(pErrInfo, VERR_SUPLIB_INVALID_ARGV0_INTERNAL,
+ "argv[0] does not match the executable image path: '%s' != '%s'", pszArgv0, szExecPath);
+
+ /*
+ * Internal applications must reside in or under the
+ * RTPathAppPrivateArch directory.
+ */
+ char szAppPrivateArch[RTPATH_MAX];
+ rc = RTPathAppPrivateArch(szAppPrivateArch, sizeof(szAppPrivateArch));
+ if (RT_FAILURE(rc))
+ return RTErrInfoSetF(pErrInfo, VERR_SUPLIB_INVALID_ARGV0_INTERNAL,
+ "RTPathAppPrivateArch failed with rc=%Rrc", rc);
+ size_t cchAppPrivateArch = strlen(szAppPrivateArch);
+ if ( cchAppPrivateArch >= strlen(szExecPath)
+ || !RTPATH_IS_SLASH(szExecPath[cchAppPrivateArch]))
+ return RTErrInfoSet(pErrInfo, VERR_SUPLIB_INVALID_INTERNAL_APP_DIR,
+ "Internal executable does reside under RTPathAppPrivateArch");
+ szExecPath[cchAppPrivateArch] = '\0';
+ if (RTPathCompare(szExecPath, szAppPrivateArch) != 0)
+ return RTErrInfoSet(pErrInfo, VERR_SUPLIB_INVALID_INTERNAL_APP_DIR,
+ "Internal executable does reside under RTPathAppPrivateArch");
+ szExecPath[cchAppPrivateArch] = RTPATH_SLASH;
+ }
+
+#ifdef VBOX_WITH_HARDENING
+ /*
+ * Verify that the image file and parent directories are sane.
+ */
+ rc = supR3HardenedVerifyFile(szExecPath, RTHCUINTPTR_MAX, false /*fMaybe3rdParty*/, pErrInfo);
+ if (RT_FAILURE(rc))
+ return rc;
+#endif
+
+ return VINF_SUCCESS;
+}
+
+
+SUPR3DECL(int) SUPR3HardenedVerifyDir(const char *pszDirPath, bool fRecursive, bool fCheckFiles, PRTERRINFO pErrInfo)
+{
+ /*
+ * Quick input validation
+ */
+ AssertPtr(pszDirPath);
+ RTErrInfoClear(pErrInfo);
+
+ /*
+ * Only do the actual check in hardened builds.
+ */
+#ifdef VBOX_WITH_HARDENING
+ int rc = supR3HardenedVerifyDir(pszDirPath, fRecursive, fCheckFiles, pErrInfo);
+ if (RT_FAILURE(rc) && !RTErrInfoIsSet(pErrInfo))
+ LogRel(("supR3HardenedVerifyDir: Verification of \"%s\" failed, rc=%Rrc\n", pszDirPath, rc));
+ return rc;
+#else
+ NOREF(pszDirPath); NOREF(fRecursive); NOREF(fCheckFiles);
+ return VINF_SUCCESS;
+#endif
+}
+
+
+SUPR3DECL(int) SUPR3HardenedVerifyPlugIn(const char *pszFilename, PRTERRINFO pErrInfo)
+{
+ /*
+ * Quick input validation
+ */
+ AssertPtr(pszFilename);
+ RTErrInfoClear(pErrInfo);
+
+ /*
+ * Only do the actual check in hardened builds.
+ */
+#ifdef VBOX_WITH_HARDENING
+ int rc = supR3HardenedVerifyFile(pszFilename, RTHCUINTPTR_MAX, true /*fMaybe3rdParty*/, pErrInfo);
+ if (RT_FAILURE(rc) && !RTErrInfoIsSet(pErrInfo))
+ LogRel(("supR3HardenedVerifyFile: Verification of \"%s\" failed, rc=%Rrc\n", pszFilename, rc));
+ return rc;
+#else
+ RT_NOREF1(pszFilename);
+ return VINF_SUCCESS;
+#endif
+}
+
+
+SUPR3DECL(int) SUPR3GipGetPhys(PRTHCPHYS pHCPhys)
+{
+ if (g_pSUPGlobalInfoPage)
+ {
+ *pHCPhys = g_HCPhysSUPGlobalInfoPage;
+ return VINF_SUCCESS;
+ }
+ *pHCPhys = NIL_RTHCPHYS;
+ return VERR_WRONG_ORDER;
+}
+
+
+SUPR3DECL(int) SUPR3QueryVTxSupported(const char **ppszWhy)
+{
+ *ppszWhy = NULL;
+#ifdef RT_OS_LINUX
+ return suplibOsQueryVTxSupported(ppszWhy);
+#else
+ return VINF_SUCCESS;
+#endif
+}
+
+
+SUPR3DECL(int) SUPR3QueryVTCaps(uint32_t *pfCaps)
+{
+ AssertPtrReturn(pfCaps, VERR_INVALID_POINTER);
+
+ *pfCaps = 0;
+
+ int rc;
+ if (!g_supLibData.fDriverless)
+ {
+ /*
+ * Issue IOCtl to the SUPDRV kernel module.
+ */
+ SUPVTCAPS Req;
+ Req.Hdr.u32Cookie = g_u32Cookie;
+ Req.Hdr.u32SessionCookie = g_u32SessionCookie;
+ Req.Hdr.cbIn = SUP_IOCTL_VT_CAPS_SIZE_IN;
+ Req.Hdr.cbOut = SUP_IOCTL_VT_CAPS_SIZE_OUT;
+ Req.Hdr.fFlags = SUPREQHDR_FLAGS_DEFAULT;
+ Req.Hdr.rc = VERR_INTERNAL_ERROR;
+ Req.u.Out.fCaps = 0;
+ rc = suplibOsIOCtl(&g_supLibData, SUP_IOCTL_VT_CAPS, &Req, SUP_IOCTL_VT_CAPS_SIZE);
+ if (RT_SUCCESS(rc))
+ {
+ rc = Req.Hdr.rc;
+ if (RT_SUCCESS(rc))
+ *pfCaps = Req.u.Out.fCaps;
+ }
+ }
+ /*
+ * Fail this call in driverless mode.
+ */
+ else
+ rc = VERR_SUP_DRIVERLESS;
+ return rc;
+}
+
+
+SUPR3DECL(bool) SUPR3IsNemSupportedWhenNoVtxOrAmdV(void)
+{
+#ifdef RT_OS_WINDOWS
+ return suplibOsIsNemSupportedWhenNoVtxOrAmdV();
+#else
+ return false;
+#endif
+}
+
+
+SUPR3DECL(int) SUPR3QueryMicrocodeRev(uint32_t *uMicrocodeRev)
+{
+ AssertPtrReturn(uMicrocodeRev, VERR_INVALID_POINTER);
+
+ *uMicrocodeRev = 0;
+
+ int rc;
+ if (!g_supLibData.fDriverless)
+ {
+ /*
+ * Issue IOCtl to the SUPDRV kernel module.
+ */
+ SUPUCODEREV Req;
+ Req.Hdr.u32Cookie = g_u32Cookie;
+ Req.Hdr.u32SessionCookie = g_u32SessionCookie;
+ Req.Hdr.cbIn = SUP_IOCTL_UCODE_REV_SIZE_IN;
+ Req.Hdr.cbOut = SUP_IOCTL_UCODE_REV_SIZE_OUT;
+ Req.Hdr.fFlags = SUPREQHDR_FLAGS_DEFAULT;
+ Req.Hdr.rc = VERR_INTERNAL_ERROR;
+ Req.u.Out.MicrocodeRev = 0;
+ rc = suplibOsIOCtl(&g_supLibData, SUP_IOCTL_UCODE_REV, &Req, SUP_IOCTL_UCODE_REV_SIZE);
+ if (RT_SUCCESS(rc))
+ {
+ rc = Req.Hdr.rc;
+ if (RT_SUCCESS(rc))
+ *uMicrocodeRev = Req.u.Out.MicrocodeRev;
+ }
+ }
+ /*
+ * Just fail the call in driverless mode.
+ */
+ else
+ rc = VERR_SUP_DRIVERLESS;
+ return rc;
+}
+
+
+SUPR3DECL(int) SUPR3TracerOpen(uint32_t uCookie, uintptr_t uArg)
+{
+ /* fake */
+ if (RT_UNLIKELY(g_uSupFakeMode))
+ return VINF_SUCCESS;
+
+ /*
+ * Issue IOCtl to the SUPDRV kernel module.
+ */
+ SUPTRACEROPEN Req;
+ Req.Hdr.u32Cookie = g_u32Cookie;
+ Req.Hdr.u32SessionCookie= g_u32SessionCookie;
+ Req.Hdr.cbIn = SUP_IOCTL_TRACER_OPEN_SIZE_IN;
+ Req.Hdr.cbOut = SUP_IOCTL_TRACER_OPEN_SIZE_OUT;
+ Req.Hdr.fFlags = SUPREQHDR_FLAGS_DEFAULT;
+ Req.Hdr.rc = VERR_INTERNAL_ERROR;
+ Req.u.In.uCookie = uCookie;
+ Req.u.In.uArg = uArg;
+ int rc = suplibOsIOCtl(&g_supLibData, SUP_IOCTL_TRACER_OPEN, &Req, SUP_IOCTL_TRACER_OPEN_SIZE);
+ if (RT_SUCCESS(rc))
+ rc = Req.Hdr.rc;
+ return rc;
+}
+
+
+SUPR3DECL(int) SUPR3TracerClose(void)
+{
+ /* fake */
+ if (RT_UNLIKELY(g_uSupFakeMode))
+ return VINF_SUCCESS;
+
+ /*
+ * Issue IOCtl to the SUPDRV kernel module.
+ */
+ SUPREQHDR Req;
+ Req.u32Cookie = g_u32Cookie;
+ Req.u32SessionCookie= g_u32SessionCookie;
+ Req.cbIn = SUP_IOCTL_TRACER_OPEN_SIZE_IN;
+ Req.cbOut = SUP_IOCTL_TRACER_OPEN_SIZE_OUT;
+ Req.fFlags = SUPREQHDR_FLAGS_DEFAULT;
+ Req.rc = VERR_INTERNAL_ERROR;
+ int rc = suplibOsIOCtl(&g_supLibData, SUP_IOCTL_TRACER_CLOSE, &Req, SUP_IOCTL_TRACER_CLOSE_SIZE);
+ if (RT_SUCCESS(rc))
+ rc = Req.rc;
+ return rc;
+}
+
+
+SUPR3DECL(int) SUPR3TracerIoCtl(uintptr_t uCmd, uintptr_t uArg, int32_t *piRetVal)
+{
+ /* fake */
+ if (RT_UNLIKELY(g_uSupFakeMode))
+ {
+ *piRetVal = -1;
+ return VERR_NOT_SUPPORTED;
+ }
+
+ /*
+ * Issue IOCtl to the SUPDRV kernel module.
+ */
+ SUPTRACERIOCTL Req;
+ Req.Hdr.u32Cookie = g_u32Cookie;
+ Req.Hdr.u32SessionCookie= g_u32SessionCookie;
+ Req.Hdr.cbIn = SUP_IOCTL_TRACER_IOCTL_SIZE_IN;
+ Req.Hdr.cbOut = SUP_IOCTL_TRACER_IOCTL_SIZE_OUT;
+ Req.Hdr.fFlags = SUPREQHDR_FLAGS_DEFAULT;
+ Req.Hdr.rc = VERR_INTERNAL_ERROR;
+ Req.u.In.uCmd = uCmd;
+ Req.u.In.uArg = uArg;
+ int rc = suplibOsIOCtl(&g_supLibData, SUP_IOCTL_TRACER_IOCTL, &Req, SUP_IOCTL_TRACER_IOCTL_SIZE);
+ if (RT_SUCCESS(rc))
+ {
+ rc = Req.Hdr.rc;
+ *piRetVal = Req.u.Out.iRetVal;
+ }
+ return rc;
+}
+
+
+
+typedef struct SUPDRVTRACERSTRTAB
+{
+ /** Pointer to the string table. */
+ char *pchStrTab;
+ /** The actual string table size. */
+ uint32_t cbStrTab;
+ /** The original string pointers. */
+ RTUINTPTR apszOrgFunctions[1];
+} SUPDRVTRACERSTRTAB, *PSUPDRVTRACERSTRTAB;
+
+
+/**
+ * Destroys a string table, restoring the original pszFunction member valus.
+ *
+ * @param pThis The string table structure.
+ * @param paProbeLocs32 The probe location array, 32-bit type variant.
+ * @param paProbeLocs64 The probe location array, 64-bit type variant.
+ * @param cProbeLocs The number of elements in the array.
+ * @param f32Bit Set if @a paProbeLocs32 should be used, when
+ * clear use @a paProbeLocs64.
+ */
+static void supr3TracerDestroyStrTab(PSUPDRVTRACERSTRTAB pThis, PVTGPROBELOC32 paProbeLocs32, PVTGPROBELOC64 paProbeLocs64,
+ uint32_t cProbeLocs, bool f32Bit)
+{
+ /* Restore. */
+ size_t i = cProbeLocs;
+ if (f32Bit)
+ while (i--)
+ paProbeLocs32[i].pszFunction = (uint32_t)pThis->apszOrgFunctions[i];
+ else
+ while (i--)
+ paProbeLocs64[i].pszFunction = pThis->apszOrgFunctions[i];
+
+ /* Free. */
+ RTMemFree(pThis->pchStrTab);
+ RTMemFree(pThis);
+}
+
+
+/**
+ * Creates a string table for the pszFunction members in the probe location
+ * array.
+ *
+ * This will save and replace the pszFunction members with offsets.
+ *
+ * @returns Pointer to a string table structure. NULL on failure.
+ * @param paProbeLocs32 The probe location array, 32-bit type variant.
+ * @param paProbeLocs64 The probe location array, 64-bit type variant.
+ * @param cProbeLocs The number of elements in the array.
+ * @param offDelta Relocation offset for the string pointers.
+ * @param f32Bit Set if @a paProbeLocs32 should be used, when
+ * clear use @a paProbeLocs64.
+ */
+static PSUPDRVTRACERSTRTAB supr3TracerCreateStrTab(PVTGPROBELOC32 paProbeLocs32,
+ PVTGPROBELOC64 paProbeLocs64,
+ uint32_t cProbeLocs,
+ RTUINTPTR offDelta,
+ bool f32Bit)
+{
+ if (cProbeLocs > _128K)
+ return NULL;
+
+ /*
+ * Allocate the string table structures.
+ */
+ size_t cbThis = RT_UOFFSETOF_DYN(SUPDRVTRACERSTRTAB, apszOrgFunctions[cProbeLocs]);
+ PSUPDRVTRACERSTRTAB pThis = (PSUPDRVTRACERSTRTAB)RTMemAlloc(cbThis);
+ if (!pThis)
+ return NULL;
+
+ uint32_t const cHashBits = cProbeLocs * 2 - 1;
+ uint32_t *pbmHash = (uint32_t *)RTMemAllocZ(RT_ALIGN_32(cHashBits, 64) / 8 );
+ if (!pbmHash)
+ {
+ RTMemFree(pThis);
+ return NULL;
+ }
+
+ /*
+ * Calc the max string table size and save the orignal pointers so we can
+ * replace them later.
+ */
+ size_t cbMax = 1;
+ for (uint32_t i = 0; i < cProbeLocs; i++)
+ {
+ pThis->apszOrgFunctions[i] = f32Bit ? paProbeLocs32[i].pszFunction : paProbeLocs64[i].pszFunction;
+ const char *pszFunction = (const char *)(uintptr_t)(pThis->apszOrgFunctions[i] + offDelta);
+ size_t cch = strlen(pszFunction);
+ if (cch > _1K)
+ {
+ cbMax = 0;
+ break;
+ }
+ cbMax += cch + 1;
+ }
+
+ /* Alloc space for it. */
+ if (cbMax > 0)
+ pThis->pchStrTab = (char *)RTMemAlloc(cbMax);
+ else
+ pThis->pchStrTab = NULL;
+ if (!pThis->pchStrTab)
+ {
+ RTMemFree(pbmHash);
+ RTMemFree(pThis);
+ return NULL;
+ }
+
+ /*
+ * Create the string table.
+ */
+ uint32_t off = 0;
+ uint32_t offPrev = 0;
+
+ for (uint32_t i = 0; i < cProbeLocs; i++)
+ {
+ const char * const psz = (const char *)(uintptr_t)(pThis->apszOrgFunctions[i] + offDelta);
+ size_t const cch = strlen(psz);
+ uint32_t const iHashBit = RTStrHash1(psz) % cHashBits;
+ if (ASMBitTestAndSet(pbmHash, iHashBit))
+ {
+ /* Often it's the most recent string. */
+ if ( off - offPrev < cch + 1
+ || memcmp(&pThis->pchStrTab[offPrev], psz, cch + 1))
+ {
+ /* It wasn't, search the entire string table. (lazy bird) */
+ offPrev = 0;
+ while (offPrev < off)
+ {
+ size_t cchCur = strlen(&pThis->pchStrTab[offPrev]);
+ if ( cchCur == cch
+ && !memcmp(&pThis->pchStrTab[offPrev], psz, cch + 1))
+ break;
+ offPrev += (uint32_t)cchCur + 1;
+ }
+ }
+ }
+ else
+ offPrev = off;
+
+ /* Add the string to the table. */
+ if (offPrev >= off)
+ {
+ memcpy(&pThis->pchStrTab[off], psz, cch + 1);
+ offPrev = off;
+ off += (uint32_t)cch + 1;
+ }
+
+ /* Update the entry */
+ if (f32Bit)
+ paProbeLocs32[i].pszFunction = offPrev;
+ else
+ paProbeLocs64[i].pszFunction = offPrev;
+ }
+
+ pThis->cbStrTab = off;
+ RTMemFree(pbmHash);
+ return pThis;
+}
+
+
+
+SUPR3DECL(int) SUPR3TracerRegisterModule(uintptr_t hModNative, const char *pszModule, struct VTGOBJHDR *pVtgHdr,
+ RTUINTPTR uVtgHdrAddr, uint32_t fFlags)
+{
+ /* Validate input. */
+ NOREF(hModNative);
+ AssertPtrReturn(pVtgHdr, VERR_INVALID_POINTER);
+ AssertReturn(!memcmp(pVtgHdr->szMagic, VTGOBJHDR_MAGIC, sizeof(pVtgHdr->szMagic)), VERR_SUPDRV_VTG_MAGIC);
+ AssertPtrReturn(pszModule, VERR_INVALID_POINTER);
+ size_t cchModule = strlen(pszModule);
+ AssertReturn(cchModule < RT_SIZEOFMEMB(SUPTRACERUMODREG, u.In.szName), VERR_FILENAME_TOO_LONG);
+ AssertReturn(!RTPathHavePath(pszModule), VERR_INVALID_PARAMETER);
+ AssertReturn(fFlags == SUP_TRACER_UMOD_FLAGS_EXE || fFlags == SUP_TRACER_UMOD_FLAGS_SHARED, VERR_INVALID_PARAMETER);
+
+ /*
+ * Set the probe location array offset and size members. If the size is
+ * zero, don't bother ring-0 with it.
+ */
+ if (!pVtgHdr->offProbeLocs)
+ {
+ uint64_t u64Tmp = pVtgHdr->uProbeLocsEnd.u64 - pVtgHdr->uProbeLocs.u64;
+ if (u64Tmp >= UINT32_MAX)
+ return VERR_SUPDRV_VTG_BAD_HDR_TOO_MUCH;
+ pVtgHdr->cbProbeLocs = (uint32_t)u64Tmp;
+
+ u64Tmp = pVtgHdr->uProbeLocs.u64 - uVtgHdrAddr;
+ if ((int64_t)u64Tmp != (int32_t)u64Tmp)
+ {
+ LogRel(("SUPR3TracerRegisterModule: VERR_SUPDRV_VTG_BAD_HDR_PTR - u64Tmp=%#llx uProbeLocs=%#llx uVtgHdrAddr=%RTptr\n",
+ u64Tmp, pVtgHdr->uProbeLocs.u64, uVtgHdrAddr));
+ return VERR_SUPDRV_VTG_BAD_HDR_PTR;
+ }
+ pVtgHdr->offProbeLocs = (int32_t)u64Tmp;
+ }
+
+ if ( !pVtgHdr->cbProbeLocs
+ || !pVtgHdr->cbProbes)
+ return VINF_SUCCESS;
+
+ /*
+ * Fake out.
+ */
+ if (RT_UNLIKELY(g_uSupFakeMode))
+ return VINF_SUCCESS;
+
+ /*
+ * Create a string table for the function names in the location array.
+ * It's somewhat easier to do that here than from ring-0.
+ */
+ uint32_t const cProbeLocs = pVtgHdr->cbProbeLocs
+ / (pVtgHdr->cBits == 32 ? sizeof(VTGPROBELOC32) : sizeof(VTGPROBELOC64));
+ PVTGPROBELOC paProbeLocs = (PVTGPROBELOC)((uintptr_t)pVtgHdr + pVtgHdr->offProbeLocs);
+ PSUPDRVTRACERSTRTAB pStrTab = supr3TracerCreateStrTab((PVTGPROBELOC32)paProbeLocs,
+ (PVTGPROBELOC64)paProbeLocs,
+ cProbeLocs, (uintptr_t)pVtgHdr - uVtgHdrAddr,
+ pVtgHdr->cBits == 32);
+ if (!pStrTab)
+ return VERR_NO_MEMORY;
+
+
+ /*
+ * Issue IOCtl to the SUPDRV kernel module.
+ */
+ SUPTRACERUMODREG Req;
+ Req.Hdr.u32Cookie = g_u32Cookie;
+ Req.Hdr.u32SessionCookie= g_u32SessionCookie;
+ Req.Hdr.cbIn = SUP_IOCTL_TRACER_UMOD_REG_SIZE_IN;
+ Req.Hdr.cbOut = SUP_IOCTL_TRACER_UMOD_REG_SIZE_OUT;
+ Req.Hdr.fFlags = SUPREQHDR_FLAGS_DEFAULT;
+ Req.Hdr.rc = VERR_INTERNAL_ERROR;
+ Req.u.In.uVtgHdrAddr = uVtgHdrAddr;
+ Req.u.In.R3PtrVtgHdr = pVtgHdr;
+ Req.u.In.R3PtrStrTab = pStrTab->pchStrTab;
+ Req.u.In.cbStrTab = pStrTab->cbStrTab;
+ Req.u.In.fFlags = fFlags;
+
+ memcpy(Req.u.In.szName, pszModule, cchModule + 1);
+ if (!RTPathHasSuffix(Req.u.In.szName))
+ {
+ /* Add the default suffix if none is given. */
+ switch (fFlags & SUP_TRACER_UMOD_FLAGS_TYPE_MASK)
+ {
+#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
+ case SUP_TRACER_UMOD_FLAGS_EXE:
+ if (cchModule + sizeof(".exe") <= sizeof(Req.u.In.szName))
+ strcpy(&Req.u.In.szName[cchModule], ".exe");
+ break;
+#endif
+
+ case SUP_TRACER_UMOD_FLAGS_SHARED:
+ {
+ const char *pszSuff = RTLdrGetSuff();
+ size_t cchSuff = strlen(pszSuff);
+ if (cchModule + cchSuff < sizeof(Req.u.In.szName))
+ memcpy(&Req.u.In.szName[cchModule], pszSuff, cchSuff + 1);
+ break;
+ }
+ }
+ }
+
+ int rc = suplibOsIOCtl(&g_supLibData, SUP_IOCTL_TRACER_UMOD_REG, &Req, SUP_IOCTL_TRACER_UMOD_REG_SIZE);
+ if (RT_SUCCESS(rc))
+ rc = Req.Hdr.rc;
+
+ supr3TracerDestroyStrTab(pStrTab, (PVTGPROBELOC32)paProbeLocs, (PVTGPROBELOC64)paProbeLocs,
+ cProbeLocs, pVtgHdr->cBits == 32);
+ return rc;
+}
+
+
+SUPR3DECL(int) SUPR3TracerDeregisterModule(struct VTGOBJHDR *pVtgHdr)
+{
+ /* Validate input. */
+ AssertPtrReturn(pVtgHdr, VERR_INVALID_POINTER);
+ AssertReturn(!memcmp(pVtgHdr->szMagic, VTGOBJHDR_MAGIC, sizeof(pVtgHdr->szMagic)), VERR_SUPDRV_VTG_MAGIC);
+
+ /*
+ * Don't bother if the object is empty.
+ */
+ if ( !pVtgHdr->cbProbeLocs
+ || !pVtgHdr->cbProbes)
+ return VINF_SUCCESS;
+
+ /*
+ * Fake out.
+ */
+ if (RT_UNLIKELY(g_uSupFakeMode))
+ return VINF_SUCCESS;
+
+ /*
+ * Issue IOCtl to the SUPDRV kernel module.
+ */
+ SUPTRACERUMODDEREG Req;
+ Req.Hdr.u32Cookie = g_u32Cookie;
+ Req.Hdr.u32SessionCookie= g_u32SessionCookie;
+ Req.Hdr.cbIn = SUP_IOCTL_TRACER_UMOD_REG_SIZE_IN;
+ Req.Hdr.cbOut = SUP_IOCTL_TRACER_UMOD_REG_SIZE_OUT;
+ Req.Hdr.fFlags = SUPREQHDR_FLAGS_DEFAULT;
+ Req.Hdr.rc = VERR_INTERNAL_ERROR;
+ Req.u.In.pVtgHdr = pVtgHdr;
+
+ int rc = suplibOsIOCtl(&g_supLibData, SUP_IOCTL_TRACER_UMOD_DEREG, &Req, SUP_IOCTL_TRACER_UMOD_DEREG_SIZE);
+ if (RT_SUCCESS(rc))
+ rc = Req.Hdr.rc;
+ return rc;
+}
+
+
+DECLASM(void) suplibTracerFireProbe(PVTGPROBELOC pProbeLoc, PSUPTRACERUMODFIREPROBE pReq)
+{
+ RT_NOREF1(pProbeLoc);
+
+ pReq->Hdr.u32Cookie = g_u32Cookie;
+ pReq->Hdr.u32SessionCookie = g_u32SessionCookie;
+ Assert(pReq->Hdr.cbIn == SUP_IOCTL_TRACER_UMOD_FIRE_PROBE_SIZE_IN);
+ Assert(pReq->Hdr.cbOut == SUP_IOCTL_TRACER_UMOD_FIRE_PROBE_SIZE_OUT);
+ pReq->Hdr.fFlags = SUPREQHDR_FLAGS_DEFAULT;
+ pReq->Hdr.rc = VINF_SUCCESS;
+
+ suplibOsIOCtl(&g_supLibData, SUP_IOCTL_TRACER_UMOD_FIRE_PROBE, pReq, SUP_IOCTL_TRACER_UMOD_FIRE_PROBE_SIZE);
+}
+
+
+SUPR3DECL(int) SUPR3MsrProberRead(uint32_t uMsr, RTCPUID idCpu, uint64_t *puValue, bool *pfGp)
+{
+ SUPMSRPROBER Req;
+ Req.Hdr.u32Cookie = g_u32Cookie;
+ Req.Hdr.u32SessionCookie = g_u32SessionCookie;
+ Req.Hdr.cbIn = SUP_IOCTL_MSR_PROBER_SIZE_IN;
+ Req.Hdr.cbOut = SUP_IOCTL_MSR_PROBER_SIZE_OUT;
+ Req.Hdr.fFlags = SUPREQHDR_FLAGS_DEFAULT;
+ Req.Hdr.rc = VERR_INTERNAL_ERROR;
+
+ Req.u.In.enmOp = SUPMSRPROBEROP_READ;
+ Req.u.In.uMsr = uMsr;
+ Req.u.In.idCpu = idCpu == NIL_RTCPUID ? UINT32_MAX : idCpu;
+
+ int rc = suplibOsIOCtl(&g_supLibData, SUP_IOCTL_MSR_PROBER, &Req, SUP_IOCTL_MSR_PROBER_SIZE);
+ if (RT_SUCCESS(rc))
+ rc = Req.Hdr.rc;
+ if (RT_SUCCESS(rc))
+ {
+ if (puValue)
+ *puValue = Req.u.Out.uResults.Read.uValue;
+ if (pfGp)
+ *pfGp = Req.u.Out.uResults.Read.fGp;
+ }
+
+ return rc;
+}
+
+
+SUPR3DECL(int) SUPR3MsrProberWrite(uint32_t uMsr, RTCPUID idCpu, uint64_t uValue, bool *pfGp)
+{
+ SUPMSRPROBER Req;
+ Req.Hdr.u32Cookie = g_u32Cookie;
+ Req.Hdr.u32SessionCookie = g_u32SessionCookie;
+ Req.Hdr.cbIn = SUP_IOCTL_MSR_PROBER_SIZE_IN;
+ Req.Hdr.cbOut = SUP_IOCTL_MSR_PROBER_SIZE_OUT;
+ Req.Hdr.fFlags = SUPREQHDR_FLAGS_DEFAULT;
+ Req.Hdr.rc = VERR_INTERNAL_ERROR;
+
+ Req.u.In.enmOp = SUPMSRPROBEROP_WRITE;
+ Req.u.In.uMsr = uMsr;
+ Req.u.In.idCpu = idCpu == NIL_RTCPUID ? UINT32_MAX : idCpu;
+ Req.u.In.uArgs.Write.uToWrite = uValue;
+
+ int rc = suplibOsIOCtl(&g_supLibData, SUP_IOCTL_MSR_PROBER, &Req, SUP_IOCTL_MSR_PROBER_SIZE);
+ if (RT_SUCCESS(rc))
+ rc = Req.Hdr.rc;
+ if (RT_SUCCESS(rc) && pfGp)
+ *pfGp = Req.u.Out.uResults.Write.fGp;
+
+ return rc;
+}
+
+
+SUPR3DECL(int) SUPR3MsrProberModify(uint32_t uMsr, RTCPUID idCpu, uint64_t fAndMask, uint64_t fOrMask,
+ PSUPMSRPROBERMODIFYRESULT pResult)
+{
+ return SUPR3MsrProberModifyEx(uMsr, idCpu, fAndMask, fOrMask, false /*fFaster*/, pResult);
+}
+
+
+SUPR3DECL(int) SUPR3MsrProberModifyEx(uint32_t uMsr, RTCPUID idCpu, uint64_t fAndMask, uint64_t fOrMask, bool fFaster,
+ PSUPMSRPROBERMODIFYRESULT pResult)
+{
+ SUPMSRPROBER Req;
+ Req.Hdr.u32Cookie = g_u32Cookie;
+ Req.Hdr.u32SessionCookie = g_u32SessionCookie;
+ Req.Hdr.cbIn = SUP_IOCTL_MSR_PROBER_SIZE_IN;
+ Req.Hdr.cbOut = SUP_IOCTL_MSR_PROBER_SIZE_OUT;
+ Req.Hdr.fFlags = SUPREQHDR_FLAGS_DEFAULT;
+ Req.Hdr.rc = VERR_INTERNAL_ERROR;
+
+ Req.u.In.enmOp = fFaster ? SUPMSRPROBEROP_MODIFY_FASTER : SUPMSRPROBEROP_MODIFY;
+ Req.u.In.uMsr = uMsr;
+ Req.u.In.idCpu = idCpu == NIL_RTCPUID ? UINT32_MAX : idCpu;
+ Req.u.In.uArgs.Modify.fAndMask = fAndMask;
+ Req.u.In.uArgs.Modify.fOrMask = fOrMask;
+
+ int rc = suplibOsIOCtl(&g_supLibData, SUP_IOCTL_MSR_PROBER, &Req, SUP_IOCTL_MSR_PROBER_SIZE);
+ if (RT_SUCCESS(rc))
+ rc = Req.Hdr.rc;
+ if (RT_SUCCESS(rc))
+ *pResult = Req.u.Out.uResults.Modify;
+
+ return rc;
+}
+
+
+SUPR3DECL(int) SUPR3ResumeSuspendedKeyboards(void)
+{
+#ifdef RT_OS_DARWIN
+ /*
+ * Issue IOCtl to the SUPDRV kernel module.
+ */
+ SUPREQHDR Req;
+ Req.u32Cookie = g_u32Cookie;
+ Req.u32SessionCookie= g_u32SessionCookie;
+ Req.cbIn = SUP_IOCTL_RESUME_SUSPENDED_KBDS_SIZE_IN;
+ Req.cbOut = SUP_IOCTL_RESUME_SUSPENDED_KBDS_SIZE_OUT;
+ Req.fFlags = SUPREQHDR_FLAGS_DEFAULT;
+ Req.rc = VERR_INTERNAL_ERROR;
+ int rc = suplibOsIOCtl(&g_supLibData, SUP_IOCTL_RESUME_SUSPENDED_KBDS, &Req, SUP_IOCTL_RESUME_SUSPENDED_KBDS_SIZE);
+ if (RT_SUCCESS(rc))
+ rc = Req.rc;
+ return rc;
+#else /* !RT_OS_DARWIN */
+ return VERR_NOT_SUPPORTED;
+#endif
+}
+
+
+SUPR3DECL(int) SUPR3TscDeltaMeasure(RTCPUID idCpu, bool fAsync, bool fForce, uint8_t cRetries, uint8_t cMsWaitRetry)
+{
+ SUPTSCDELTAMEASURE Req;
+ Req.Hdr.u32Cookie = g_u32Cookie;
+ Req.Hdr.u32SessionCookie = g_u32SessionCookie;
+ Req.Hdr.cbIn = SUP_IOCTL_TSC_DELTA_MEASURE_SIZE_IN;
+ Req.Hdr.cbOut = SUP_IOCTL_TSC_DELTA_MEASURE_SIZE_OUT;
+ Req.Hdr.fFlags = SUPREQHDR_FLAGS_DEFAULT;
+ Req.Hdr.rc = VERR_INTERNAL_ERROR;
+
+ Req.u.In.cRetries = cRetries;
+ Req.u.In.fAsync = fAsync;
+ Req.u.In.fForce = fForce;
+ Req.u.In.idCpu = idCpu;
+ Req.u.In.cMsWaitRetry = cMsWaitRetry;
+
+ int rc = suplibOsIOCtl(&g_supLibData, SUP_IOCTL_TSC_DELTA_MEASURE, &Req, SUP_IOCTL_TSC_DELTA_MEASURE_SIZE);
+ if (RT_SUCCESS(rc))
+ rc = Req.Hdr.rc;
+ return rc;
+}
+
+
+SUPR3DECL(int) SUPR3ReadTsc(uint64_t *puTsc, uint16_t *pidApic)
+{
+ AssertReturn(puTsc, VERR_INVALID_PARAMETER);
+
+ SUPTSCREAD Req;
+ Req.Hdr.u32Cookie = g_u32Cookie;
+ Req.Hdr.u32SessionCookie = g_u32SessionCookie;
+ Req.Hdr.cbIn = SUP_IOCTL_TSC_READ_SIZE_IN;
+ Req.Hdr.cbOut = SUP_IOCTL_TSC_READ_SIZE_OUT;
+ Req.Hdr.fFlags = SUPREQHDR_FLAGS_DEFAULT;
+ Req.Hdr.rc = VERR_INTERNAL_ERROR;
+
+ int rc = suplibOsIOCtl(&g_supLibData, SUP_IOCTL_TSC_READ, &Req, SUP_IOCTL_TSC_READ_SIZE);
+ if (RT_SUCCESS(rc))
+ {
+ rc = Req.Hdr.rc;
+ *puTsc = Req.u.Out.u64AdjustedTsc;
+ if (pidApic)
+ *pidApic = Req.u.Out.idApic;
+ }
+ return rc;
+}
+
+
+SUPR3DECL(int) SUPR3GipSetFlags(uint32_t fOrMask, uint32_t fAndMask)
+{
+ AssertMsgReturn(!(fOrMask & ~SUPGIP_FLAGS_VALID_MASK),
+ ("fOrMask=%#x ValidMask=%#x\n", fOrMask, SUPGIP_FLAGS_VALID_MASK), VERR_INVALID_PARAMETER);
+ AssertMsgReturn((fAndMask & ~SUPGIP_FLAGS_VALID_MASK) == ~SUPGIP_FLAGS_VALID_MASK,
+ ("fAndMask=%#x ValidMask=%#x\n", fAndMask, SUPGIP_FLAGS_VALID_MASK), VERR_INVALID_PARAMETER);
+
+ SUPGIPSETFLAGS Req;
+ Req.Hdr.u32Cookie = g_u32Cookie;
+ Req.Hdr.u32SessionCookie = g_u32SessionCookie;
+ Req.Hdr.cbIn = SUP_IOCTL_GIP_SET_FLAGS_SIZE_IN;
+ Req.Hdr.cbOut = SUP_IOCTL_GIP_SET_FLAGS_SIZE_OUT;
+ Req.Hdr.fFlags = SUPREQHDR_FLAGS_DEFAULT;
+ Req.Hdr.rc = VERR_INTERNAL_ERROR;
+
+ Req.u.In.fAndMask = fAndMask;
+ Req.u.In.fOrMask = fOrMask;
+
+ int rc = suplibOsIOCtl(&g_supLibData, SUP_IOCTL_GIP_SET_FLAGS, &Req, SUP_IOCTL_GIP_SET_FLAGS_SIZE);
+ if (RT_SUCCESS(rc))
+ rc = Req.Hdr.rc;
+ return rc;
+}
+
+
+SUPR3DECL(int) SUPR3GetHwvirtMsrs(PSUPHWVIRTMSRS pHwvirtMsrs, bool fForceRequery)
+{
+ AssertReturn(pHwvirtMsrs, VERR_INVALID_PARAMETER);
+
+ SUPGETHWVIRTMSRS Req;
+ Req.Hdr.u32Cookie = g_u32Cookie;
+ Req.Hdr.u32SessionCookie = g_u32SessionCookie;
+ Req.Hdr.cbIn = SUP_IOCTL_GET_HWVIRT_MSRS_SIZE_IN;
+ Req.Hdr.cbOut = SUP_IOCTL_GET_HWVIRT_MSRS_SIZE_OUT;
+ Req.Hdr.fFlags = SUPREQHDR_FLAGS_DEFAULT;
+ Req.Hdr.rc = VERR_INTERNAL_ERROR;
+
+ Req.u.In.fForce = fForceRequery;
+ Req.u.In.fReserved0 = false;
+ Req.u.In.fReserved1 = false;
+ Req.u.In.fReserved2 = false;
+
+ int rc = suplibOsIOCtl(&g_supLibData, SUP_IOCTL_GET_HWVIRT_MSRS, &Req, SUP_IOCTL_GET_HWVIRT_MSRS_SIZE);
+ if (RT_SUCCESS(rc))
+ {
+ rc = Req.Hdr.rc;
+ *pHwvirtMsrs = Req.u.Out.HwvirtMsrs;
+ }
+ else
+ RT_ZERO(*pHwvirtMsrs);
+ return rc;
+}
+
diff --git a/src/VBox/HostDrivers/Support/SUPLibAll.cpp b/src/VBox/HostDrivers/Support/SUPLibAll.cpp
new file mode 100644
index 00000000..939c4188
--- /dev/null
+++ b/src/VBox/HostDrivers/Support/SUPLibAll.cpp
@@ -0,0 +1,429 @@
+/* $Id: SUPLibAll.cpp $ */
+/** @file
+ * VirtualBox Support Library - All Contexts Code.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include <VBox/sup.h>
+#ifdef IN_RC
+# include <VBox/vmm/vm.h>
+# include <VBox/vmm/vmm.h>
+#endif
+#ifdef IN_RING0
+# include <iprt/mp.h>
+#endif
+#if defined(RT_ARCH_AMD64) || defined(RT_ARCH_X86)
+# include <iprt/asm-amd64-x86.h>
+#endif
+#include <iprt/errcore.h>
+#if defined(IN_RING0) && defined(RT_OS_LINUX)
+# include "SUPDrvInternal.h"
+#endif
+
+
+
+#if defined(RT_ARCH_AMD64) || defined(RT_ARCH_X86)
+/**
+ * The slow case for SUPReadTsc where we need to apply deltas.
+ *
+ * Must only be called when deltas are applicable, so please do not call it
+ * directly.
+ *
+ * @returns TSC with delta applied.
+ * @param pGip Pointer to the GIP.
+ *
+ * @remarks May be called with interrupts disabled in ring-0! This is why the
+ * ring-0 code doesn't attempt to figure the delta.
+ *
+ * @internal
+ */
+SUPDECL(uint64_t) SUPReadTscWithDelta(PSUPGLOBALINFOPAGE pGip)
+{
+ uint64_t uTsc;
+ uint16_t iGipCpu;
+ AssertCompile(RT_IS_POWER_OF_TWO(RTCPUSET_MAX_CPUS));
+ AssertCompile(RT_ELEMENTS(pGip->aiCpuFromCpuSetIdx) >= RTCPUSET_MAX_CPUS);
+ Assert(pGip->enmUseTscDelta > SUPGIPUSETSCDELTA_PRACTICALLY_ZERO);
+
+ /*
+ * Read the TSC and get the corresponding aCPUs index.
+ */
+#ifdef IN_RING3
+ if (pGip->fGetGipCpu & SUPGIPGETCPU_RDTSCP_MASK_MAX_SET_CPUS)
+ {
+ /* RDTSCP gives us all we need, no loops/cli. */
+ uint32_t iCpuSet;
+ uTsc = ASMReadTscWithAux(&iCpuSet);
+ iCpuSet &= RTCPUSET_MAX_CPUS - 1;
+ iGipCpu = pGip->aiCpuFromCpuSetIdx[iCpuSet];
+ }
+ else if (pGip->fGetGipCpu & SUPGIPGETCPU_IDTR_LIMIT_MASK_MAX_SET_CPUS)
+ {
+ /* Storing the IDTR is normally very quick, but we need to loop. */
+ uint32_t cTries = 0;
+ for (;;)
+ {
+ uint16_t cbLim = ASMGetIdtrLimit();
+ uTsc = ASMReadTSC();
+ if (RT_LIKELY(ASMGetIdtrLimit() == cbLim))
+ {
+ uint16_t iCpuSet = cbLim - 256 * (ARCH_BITS == 64 ? 16 : 8);
+ iCpuSet &= RTCPUSET_MAX_CPUS - 1;
+ iGipCpu = pGip->aiCpuFromCpuSetIdx[iCpuSet];
+ break;
+ }
+ if (cTries >= 16)
+ {
+ iGipCpu = UINT16_MAX;
+ break;
+ }
+ cTries++;
+ }
+ }
+ else if (pGip->fGetGipCpu & SUPGIPGETCPU_APIC_ID_EXT_0B)
+ {
+ /* Get APIC ID / 0x1b via the slow CPUID instruction, requires looping. */
+ uint32_t cTries = 0;
+ for (;;)
+ {
+ uint32_t idApic = ASMGetApicIdExt0B();
+ uTsc = ASMReadTSC();
+ if (RT_LIKELY(ASMGetApicIdExt0B() == idApic))
+ {
+ iGipCpu = pGip->aiCpuFromApicId[idApic];
+ break;
+ }
+ if (cTries >= 16)
+ {
+ iGipCpu = UINT16_MAX;
+ break;
+ }
+ cTries++;
+ }
+ }
+ else if (pGip->fGetGipCpu & SUPGIPGETCPU_APIC_ID_EXT_8000001E)
+ {
+ /* Get APIC ID / 0x8000001e via the slow CPUID instruction, requires looping. */
+ uint32_t cTries = 0;
+ for (;;)
+ {
+ uint32_t idApic = ASMGetApicIdExt8000001E();
+ uTsc = ASMReadTSC();
+ if (RT_LIKELY(ASMGetApicIdExt8000001E() == idApic))
+ {
+ iGipCpu = pGip->aiCpuFromApicId[idApic];
+ break;
+ }
+ if (cTries >= 16)
+ {
+ iGipCpu = UINT16_MAX;
+ break;
+ }
+ cTries++;
+ }
+ }
+ else
+ {
+ /* Get APIC ID via the slow CPUID instruction, requires looping. */
+ uint32_t cTries = 0;
+ for (;;)
+ {
+ uint8_t idApic = ASMGetApicId();
+ uTsc = ASMReadTSC();
+ if (RT_LIKELY(ASMGetApicId() == idApic))
+ {
+ iGipCpu = pGip->aiCpuFromApicId[idApic];
+ break;
+ }
+ if (cTries >= 16)
+ {
+ iGipCpu = UINT16_MAX;
+ break;
+ }
+ cTries++;
+ }
+ }
+#elif defined(IN_RING0)
+ /* Ring-0: Use use RTMpCpuId(), no loops. */
+ RTCCUINTREG uFlags = ASMIntDisableFlags();
+ int iCpuSet = RTMpCpuIdToSetIndex(RTMpCpuId());
+ if (RT_LIKELY((unsigned)iCpuSet < RT_ELEMENTS(pGip->aiCpuFromCpuSetIdx)))
+ iGipCpu = pGip->aiCpuFromCpuSetIdx[iCpuSet];
+ else
+ iGipCpu = UINT16_MAX;
+ uTsc = ASMReadTSC();
+ ASMSetFlags(uFlags);
+
+# elif defined(IN_RC)
+ /* Raw-mode context: We can get the host CPU set index via VMCPU, no loops. */
+ RTCCUINTREG uFlags = ASMIntDisableFlags(); /* Are already disable, but play safe. */
+ uint32_t iCpuSet = VMMGetCpu(&g_VM)->iHostCpuSet;
+ if (RT_LIKELY(iCpuSet < RT_ELEMENTS(pGip->aiCpuFromCpuSetIdx)))
+ iGipCpu = pGip->aiCpuFromCpuSetIdx[iCpuSet];
+ else
+ iGipCpu = UINT16_MAX;
+ uTsc = ASMReadTSC();
+ ASMSetFlags(uFlags);
+#else
+# error "IN_RING3, IN_RC or IN_RING0 must be defined!"
+#endif
+
+ /*
+ * If the delta is valid, apply it.
+ */
+ if (RT_LIKELY(iGipCpu < pGip->cCpus))
+ {
+ int64_t iTscDelta = pGip->aCPUs[iGipCpu].i64TSCDelta;
+ if (RT_LIKELY(iTscDelta != INT64_MAX))
+ return uTsc - iTscDelta;
+
+# ifdef IN_RING3
+ /*
+ * The delta needs calculating, call supdrv to get the TSC.
+ */
+ int rc = SUPR3ReadTsc(&uTsc, NULL);
+ if (RT_SUCCESS(rc))
+ return uTsc;
+ AssertMsgFailed(("SUPR3ReadTsc -> %Rrc\n", rc));
+ uTsc = ASMReadTSC();
+# endif /* IN_RING3 */
+ }
+
+ /*
+ * This shouldn't happen, especially not in ring-3 and raw-mode context.
+ * But if it does, return something that's half useful.
+ */
+ AssertMsgFailed(("iGipCpu=%d (%#x) cCpus=%d fGetGipCpu=%#x\n", iGipCpu, iGipCpu, pGip->cCpus, pGip->fGetGipCpu));
+ return uTsc;
+}
+# ifdef SUPR0_EXPORT_SYMBOL
+SUPR0_EXPORT_SYMBOL(SUPReadTscWithDelta);
+# endif
+#endif /* RT_ARCH_AMD64 || RT_ARCH_X86 */
+
+
+/**
+ * Internal worker for getting the GIP CPU array index for the calling CPU.
+ *
+ * @returns Index into SUPGLOBALINFOPAGE::aCPUs or UINT16_MAX.
+ * @param pGip The GIP.
+ */
+DECLINLINE(uint16_t) supGetGipCpuIndex(PSUPGLOBALINFOPAGE pGip)
+{
+ uint16_t iGipCpu;
+#ifdef IN_RING3
+# if defined(RT_ARCH_AMD64) || defined(RT_ARCH_X86)
+ if (pGip->fGetGipCpu & SUPGIPGETCPU_IDTR_LIMIT_MASK_MAX_SET_CPUS)
+ {
+ /* Storing the IDTR is normally very fast. */
+ uint16_t cbLim = ASMGetIdtrLimit();
+ uint16_t iCpuSet = cbLim - 256 * (ARCH_BITS == 64 ? 16 : 8);
+ iCpuSet &= RTCPUSET_MAX_CPUS - 1;
+ iGipCpu = pGip->aiCpuFromCpuSetIdx[iCpuSet];
+ }
+ else if (pGip->fGetGipCpu & SUPGIPGETCPU_RDTSCP_MASK_MAX_SET_CPUS)
+ {
+ /* RDTSCP gives us what need need and more. */
+ uint32_t iCpuSet;
+ ASMReadTscWithAux(&iCpuSet);
+ iCpuSet &= RTCPUSET_MAX_CPUS - 1;
+ iGipCpu = pGip->aiCpuFromCpuSetIdx[iCpuSet];
+ }
+ else if (pGip->fGetGipCpu & SUPGIPGETCPU_APIC_ID_EXT_0B)
+ {
+ /* Get APIC ID via the slow CPUID/0000000B instruction. */
+ uint32_t idApic = ASMGetApicIdExt0B();
+ iGipCpu = pGip->aiCpuFromApicId[idApic];
+ }
+ else if (pGip->fGetGipCpu & SUPGIPGETCPU_APIC_ID_EXT_8000001E)
+ {
+ /* Get APIC ID via the slow CPUID/8000001E instruction. */
+ uint32_t idApic = ASMGetApicIdExt8000001E();
+ iGipCpu = pGip->aiCpuFromApicId[idApic];
+ }
+ else
+ {
+ /* Get APIC ID via the slow CPUID instruction. */
+ uint8_t idApic = ASMGetApicId();
+ iGipCpu = pGip->aiCpuFromApicId[idApic];
+ }
+
+# else
+ int iCpuSet = RTMpCpuIdToSetIndex(RTMpCpuId());
+ if (RT_LIKELY((unsigned)iCpuSet < RT_ELEMENTS(pGip->aiCpuFromCpuSetIdx)))
+ iGipCpu = pGip->aiCpuFromCpuSetIdx[iCpuSet];
+ else
+ iGipCpu = UINT16_MAX;
+# endif
+
+#elif defined(IN_RING0)
+ /* Ring-0: Use use RTMpCpuId() (disables cli to avoid host OS assertions about unsafe CPU number usage). */
+ RTCCUINTREG uFlags = ASMIntDisableFlags();
+ int iCpuSet = RTMpCpuIdToSetIndex(RTMpCpuId());
+ if (RT_LIKELY((unsigned)iCpuSet < RT_ELEMENTS(pGip->aiCpuFromCpuSetIdx)))
+ iGipCpu = pGip->aiCpuFromCpuSetIdx[iCpuSet];
+ else
+ iGipCpu = UINT16_MAX;
+ ASMSetFlags(uFlags);
+
+# elif defined(IN_RC)
+ /* Raw-mode context: We can get the host CPU set index via VMCPU. */
+ uint32_t iCpuSet = VMMGetCpu(&g_VM)->iHostCpuSet;
+ if (RT_LIKELY(iCpuSet < RT_ELEMENTS(pGip->aiCpuFromCpuSetIdx)))
+ iGipCpu = pGip->aiCpuFromCpuSetIdx[iCpuSet];
+ else
+ iGipCpu = UINT16_MAX;
+
+#else
+# error "IN_RING3, IN_RC or IN_RING0 must be defined!"
+#endif
+ return iGipCpu;
+}
+
+
+/**
+ * Slow path in SUPGetTscDelta, don't call directly.
+ *
+ * @returns See SUPGetTscDelta.
+ * @param pGip The GIP.
+ * @internal
+ */
+SUPDECL(int64_t) SUPGetTscDeltaSlow(PSUPGLOBALINFOPAGE pGip)
+{
+ uint16_t iGipCpu = supGetGipCpuIndex(pGip);
+ if (RT_LIKELY(iGipCpu < pGip->cCpus))
+ {
+ int64_t iTscDelta = pGip->aCPUs[iGipCpu].i64TSCDelta;
+ if (iTscDelta != INT64_MAX)
+ return iTscDelta;
+ }
+ AssertFailed();
+ return 0;
+}
+
+
+/**
+ * SLow path in SUPGetGipCpuPtr, don't call directly.
+ *
+ * @returns Pointer to the CPU entry for the caller, NULL on failure.
+ * @param pGip The GIP.
+ */
+SUPDECL(PSUPGIPCPU) SUPGetGipCpuPtrForAsyncMode(PSUPGLOBALINFOPAGE pGip)
+{
+ uint16_t iGipCpu = supGetGipCpuIndex(pGip);
+ if (RT_LIKELY(iGipCpu < pGip->cCpus))
+ return &pGip->aCPUs[iGipCpu];
+ AssertFailed();
+ return NULL;
+}
+
+
+/**
+ * Slow path in SUPGetCpuHzFromGip, don't call directly.
+ *
+ * @returns See SUPGetCpuHzFromGip.
+ * @param pGip The GIP.
+ * @internal
+ */
+SUPDECL(uint64_t) SUPGetCpuHzFromGipForAsyncMode(PSUPGLOBALINFOPAGE pGip)
+{
+ uint16_t iGipCpu = supGetGipCpuIndex(pGip);
+ if (RT_LIKELY(iGipCpu < pGip->cCpus))
+ return pGip->aCPUs[iGipCpu].u64CpuHz;
+ AssertFailed();
+ return pGip->u64CpuHz;
+}
+
+
+
+/**
+ * Worker for SUPIsTscFreqCompatible().
+ *
+ * @returns true if it's compatible, false otherwise.
+ * @param uBaseCpuHz The reference CPU frequency of the system.
+ * @param uCpuHz The CPU frequency to compare with the base.
+ * @param fRelax Whether to use a more relaxed threshold (like
+ * for when running in a virtualized environment).
+ *
+ * @remarks Don't use directly, use SUPIsTscFreqCompatible() instead. This is
+ * to be used by tstGIP-2 or the like.
+ */
+SUPDECL(bool) SUPIsTscFreqCompatibleEx(uint64_t uBaseCpuHz, uint64_t uCpuHz, bool fRelax)
+{
+ if (uBaseCpuHz != uCpuHz)
+ {
+ /* Arbitrary tolerance threshold, tweak later if required, perhaps
+ more tolerance on lower frequencies and less tolerance on higher. */
+ uint16_t uFact = !fRelax ? 666 /* 0.15% */ : 125 /* 0.8% */;
+ uint64_t uThr = uBaseCpuHz / uFact;
+ uint64_t uLo = uBaseCpuHz - uThr;
+ uint64_t uHi = uBaseCpuHz + uThr;
+ if ( uCpuHz < uLo
+ || uCpuHz > uHi)
+ return false;
+ }
+ return true;
+}
+
+
+/**
+ * Checks if the provided TSC frequency is close enough to the computed TSC
+ * frequency of the host.
+ *
+ * @returns true if it's compatible, false otherwise.
+ * @param uCpuHz The TSC frequency to check.
+ * @param puGipCpuHz Where to store the GIP TSC frequency used
+ * during the compatibility test - optional.
+ * @param fRelax Whether to use a more relaxed threshold (like
+ * for when running in a virtualized environment).
+ */
+SUPDECL(bool) SUPIsTscFreqCompatible(uint64_t uCpuHz, uint64_t *puGipCpuHz, bool fRelax)
+{
+ PSUPGLOBALINFOPAGE pGip = g_pSUPGlobalInfoPage;
+ bool fCompat = false;
+ uint64_t uGipCpuHz = 0;
+ if ( pGip
+ && pGip->u32Mode != SUPGIPMODE_ASYNC_TSC)
+ {
+ uGipCpuHz = pGip->u64CpuHz;
+ fCompat = SUPIsTscFreqCompatibleEx(uGipCpuHz, uCpuHz, fRelax);
+ }
+ if (puGipCpuHz)
+ *puGipCpuHz = uGipCpuHz;
+ return fCompat;
+}
+
diff --git a/src/VBox/HostDrivers/Support/SUPLibInternal.h b/src/VBox/HostDrivers/Support/SUPLibInternal.h
new file mode 100644
index 00000000..4c788fac
--- /dev/null
+++ b/src/VBox/HostDrivers/Support/SUPLibInternal.h
@@ -0,0 +1,524 @@
+/* $Id: SUPLibInternal.h $ */
+/** @file
+ * VirtualBox Support Library - Internal header.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+#ifndef VBOX_INCLUDED_SRC_Support_SUPLibInternal_h
+#define VBOX_INCLUDED_SRC_Support_SUPLibInternal_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+#include <VBox/cdefs.h>
+#include <VBox/types.h>
+#include <iprt/stdarg.h>
+
+
+/*******************************************************************************
+* Defined Constants And Macros *
+*******************************************************************************/
+/** @def SUPLIB_DLL_SUFF
+ * The (typical) DLL/DYLIB/SO suffix. */
+#if defined(RT_OS_DARWIN)
+# define SUPLIB_DLL_SUFF ".dylib"
+#elif defined(RT_OS_L4)
+# define SUPLIB_DLL_SUFF ".s.so"
+#elif defined(RT_OS_OS2) || defined(RT_OS_WINDOWS)
+# define SUPLIB_DLL_SUFF ".dll"
+#else
+# define SUPLIB_DLL_SUFF ".so"
+#endif
+
+#ifdef RT_OS_SOLARIS
+/** Number of dummy files to open (2:ip4, 1:ip6, 1:extra) see
+ * @bugref{4650}. */
+# define SUPLIB_FLT_DUMMYFILES 4
+#endif
+
+/** @def SUPLIB_EXE_SUFF
+ * The (typical) executable suffix. */
+#if defined(RT_OS_OS2) || defined(RT_OS_WINDOWS)
+# define SUPLIB_EXE_SUFF ".exe"
+#else
+# define SUPLIB_EXE_SUFF ""
+#endif
+
+/** @def SUP_HARDENED_SUID
+ * Whether we're employing set-user-ID-on-execute in the hardening.
+ */
+#if (!defined(RT_OS_OS2) && !defined(RT_OS_WINDOWS) && !defined(RT_OS_L4)) || defined(DOXYGEN_RUNNING)
+# define SUP_HARDENED_SUID
+#else
+# undef SUP_HARDENED_SUID
+#endif
+
+#ifdef IN_SUP_HARDENED_R3
+/** @name Make the symbols in SUPR3HardenedStatic different from the VBoxRT ones.
+ * We cannot rely on DECLHIDDEN to make this separation for us since it doesn't
+ * work with all GCC versions. So, we resort to old fashion precompiler hacking.
+ * @{
+ */
+# define supR3HardenedPathAppPrivateNoArch supR3HardenedStaticPathAppPrivateNoArch
+# define supR3HardenedPathAppPrivateArch supR3HardenedStaticPathAppPrivateArch
+# define supR3HardenedPathAppSharedLibs supR3HardenedStaticPathAppSharedLibs
+# define supR3HardenedPathAppDocs supR3HardenedStaticPathAppDocs
+# define supR3HardenedPathAppBin supR3HardenedStaticPathAppBin
+# define supR3HardenedPathFilename supR3HardenedStaticPathFilename
+# define supR3HardenedFatalV supR3HardenedStaticFatalV
+# define supR3HardenedFatal supR3HardenedStaticFatal
+# define supR3HardenedFatalMsgV supR3HardenedStaticFatalMsgV
+# define supR3HardenedFatalMsg supR3HardenedStaticFatalMsg
+# define supR3HardenedErrorV supR3HardenedStaticErrorV
+# define supR3HardenedError supR3HardenedStaticError
+# define supR3HardenedOpenLog supR3HardenedStaticOpenLog
+# define supR3HardenedLogV supR3HardenedStaticLogV
+# define supR3HardenedLog supR3HardenedStaticLog
+# define supR3HardenedLogFlush supR3HardenedStaticLogFlush
+# define supR3HardenedVerifyAll supR3HardenedStaticVerifyAll
+# define supR3HardenedVerifyFixedDir supR3HardenedStaticVerifyFixedDir
+# define supR3HardenedVerifyFixedFile supR3HardenedStaticVerifyFixedFile
+# define supR3HardenedVerifyDir supR3HardenedStaticVerifyDir
+# define supR3HardenedVerifyFile supR3HardenedStaticVerifyFile
+# define supR3HardenedGetPreInitData supR3HardenedStaticGetPreInitData
+# define supR3HardenedRecvPreInitData supR3HardenedStaticRecvPreInitData
+/** @} */
+#endif /* IN_SUP_HARDENED_R3 */
+
+
+/** @name CRT function mappings (not using CRT on Windows).
+ * @{
+ */
+#if defined(IN_SUP_HARDENED_R3) && defined(RT_OS_WINDOWS)
+# define SUP_HARDENED_NEED_CRT_FUNCTIONS
+DECLHIDDEN(int) suplibHardenedMemComp(void const *pvDst, const void *pvSrc, size_t cbToComp);
+DECLHIDDEN(void *) suplibHardenedMemCopy(void *pvDst, const void *pvSrc, size_t cbToCopy);
+DECLHIDDEN(void *) suplibHardenedMemSet(void *pvDst, int ch, size_t cbToSet);
+DECLHIDDEN(char *) suplibHardenedStrCopy(char *pszDst, const char *pszSrc);
+DECLHIDDEN(size_t) suplibHardenedStrLen(const char *psz);
+DECLHIDDEN(char *) suplibHardenedStrCat(char *pszDst, const char *pszSrc);
+DECLHIDDEN(int) suplibHardenedStrCmp(const char *psz1, const char *psz2);
+DECLHIDDEN(int) suplibHardenedStrNCmp(const char *psz1, const char *psz2, size_t cchMax);
+#else
+# undef SUP_HARDENED_NEED_CRT_FUNCTIONS
+# define suplibHardenedMemComp memcmp
+# define suplibHardenedMemCopy memcpy
+# define suplibHardenedMemSet memset
+# define suplibHardenedStrCopy strcpy
+# define suplibHardenedStrLen strlen
+# define suplibHardenedStrCat strcat
+# define suplibHardenedStrCmp strcmp
+# define suplibHardenedStrNCmp strncmp
+#endif
+DECLHIDDEN(DECL_NO_RETURN(void)) suplibHardenedExit(RTEXITCODE rcExit);
+DECLHIDDEN(void) suplibHardenedPrintF(const char *pszFormat, ...);
+DECLHIDDEN(void) suplibHardenedPrintFV(const char *pszFormat, va_list va);
+
+/** @} */
+
+/** Debug output macro. */
+#ifdef IN_SUP_HARDENED_R3
+# if defined(DEBUG_bird) && defined(RT_OS_WINDOWS)
+# define SUP_DPRINTF(a) do { supR3HardenedStaticLog a; suplibHardenedPrintF a; } while (0)
+# else
+# define SUP_DPRINTF(a) do { supR3HardenedStaticLog a; } while (0)
+# endif
+#else
+# if defined(DEBUG_bird) && defined(RT_OS_WINDOWS)
+# define SUP_DPRINTF(a) RTLogPrintf a
+# else
+# define SUP_DPRINTF(a) do { } while (0)
+# endif
+#endif
+
+
+/*******************************************************************************
+* Structures and Typedefs *
+*******************************************************************************/
+/**
+ * The type of an installed file.
+ */
+typedef enum SUPINSTFILETYPE
+{
+ kSupIFT_Invalid = 0,
+ kSupIFT_Exe,
+ kSupIFT_Dll,
+ kSupIFT_Rc,
+ kSupIFT_Sys,
+ kSupIFT_Script,
+ kSupIFT_Data,
+ kSupIFT_TestExe,
+ kSupIFT_TestDll,
+ kSupIFT_End
+} SUPINSTFILETYPE;
+
+/**
+ * Installation directory specifier.
+ */
+typedef enum SUPINSTDIR
+{
+ kSupID_Invalid = 0,
+ kSupID_AppBin,
+ kSupID_AppSharedLib,
+ kSupID_AppPrivArch,
+ kSupID_AppPrivArchComp,
+ kSupID_AppPrivNoArch,
+ kSupID_Testcase,
+#ifdef RT_OS_DARWIN
+ kSupID_AppMacHelper,
+#endif
+ kSupID_End
+} SUPINSTDIR;
+
+/**
+ * Installed file.
+ */
+typedef struct SUPINSTFILE
+{
+ /** File type. */
+ SUPINSTFILETYPE enmType;
+ /** Install directory. */
+ SUPINSTDIR enmDir;
+ /** Optional (true) or mandatory (false. */
+ bool fOptional;
+ /** File name. */
+ const char *pszFile;
+} SUPINSTFILE;
+typedef SUPINSTFILE *PSUPINSTFILE;
+typedef SUPINSTFILE const *PCSUPINSTFILE;
+
+/**
+ * Status data for a verified file.
+ */
+typedef struct SUPVERIFIEDFILE
+{
+ /** The file handle or descriptor. -1 if not open. */
+ intptr_t hFile;
+ /** Whether the file has been validated. */
+ bool fValidated;
+#ifdef RT_OS_WINDOWS
+ /** Whether we've checked the signature of the file. */
+ bool fCheckedSignature;
+#endif
+} SUPVERIFIEDFILE;
+typedef SUPVERIFIEDFILE *PSUPVERIFIEDFILE;
+typedef SUPVERIFIEDFILE const *PCSUPVERIFIEDFILE;
+
+/**
+ * Status data for a verified directory.
+ */
+typedef struct SUPVERIFIEDDIR
+{
+ /** The directory handle or descriptor. -1 if not open. */
+ intptr_t hDir;
+ /** Whether the directory has been validated. */
+ bool fValidated;
+} SUPVERIFIEDDIR;
+typedef SUPVERIFIEDDIR *PSUPVERIFIEDDIR;
+typedef SUPVERIFIEDDIR const *PCSUPVERIFIEDDIR;
+
+
+/**
+ * SUPLib instance data.
+ *
+ * This is data that is passed from the static to the dynamic SUPLib
+ * in a hardened setup.
+ */
+typedef struct SUPLIBDATA
+{
+ /** The device handle. */
+#if defined(RT_OS_WINDOWS)
+ void *hDevice;
+#else
+ int hDevice;
+#endif
+ /** Indicates whether we have unrestricted (true) or restricted access to the
+ * support device. */
+ bool fUnrestricted;
+ /** Set if we're in driverless mode. */
+ bool fDriverless;
+#if defined(RT_OS_DARWIN)
+ /** The connection to the VBoxSupDrv service. */
+ uintptr_t uConnection;
+#elif defined(RT_OS_LINUX)
+ /** Indicates whether madvise(,,MADV_DONTFORK) works. */
+ bool fSysMadviseWorks;
+#elif defined(RT_OS_SOLARIS)
+ /** Extra dummy file descriptors to prevent growing file-descriptor table on
+ * clean up (see @bugref{4650}). */
+ int ahDummy[SUPLIB_FLT_DUMMYFILES];
+#elif defined(RT_OS_WINDOWS)
+#endif
+} SUPLIBDATA;
+/** Pointer to the pre-init data. */
+typedef SUPLIBDATA *PSUPLIBDATA;
+/** Pointer to const pre-init data. */
+typedef SUPLIBDATA const *PCSUPLIBDATA;
+
+/** The NIL value of SUPLIBDATA::hDevice. */
+#if defined(RT_OS_WINDOWS)
+# define SUP_HDEVICE_NIL NULL
+#else
+# define SUP_HDEVICE_NIL (-1)
+#endif
+
+
+/**
+ * Pre-init data that is handed over from the hardened executable stub.
+ */
+typedef struct SUPPREINITDATA
+{
+ /** Magic value (SUPPREINITDATA_MAGIC). */
+ uint32_t u32Magic;
+ /** The SUPLib instance data. */
+ SUPLIBDATA Data;
+ /** The number of entries in paInstallFiles and paVerifiedFiles. */
+ size_t cInstallFiles;
+ /** g_aSupInstallFiles. */
+ PCSUPINSTFILE paInstallFiles;
+ /** g_aSupVerifiedFiles. */
+ PCSUPVERIFIEDFILE paVerifiedFiles;
+ /** The number of entries in paVerifiedDirs. */
+ size_t cVerifiedDirs;
+ /** g_aSupVerifiedDirs. */
+ PCSUPVERIFIEDDIR paVerifiedDirs;
+ /** Magic value (SUPPREINITDATA_MAGIC). */
+ uint32_t u32EndMagic;
+} SUPPREINITDATA;
+typedef SUPPREINITDATA *PSUPPREINITDATA;
+typedef SUPPREINITDATA const *PCSUPPREINITDATA;
+
+/** Magic value for SUPPREINITDATA::u32Magic and SUPPREINITDATA::u32EndMagic. */
+#define SUPPREINITDATA_MAGIC UINT32_C(0xbeef0001)
+
+/** @copydoc supR3PreInit */
+typedef DECLCALLBACKTYPE(int, FNSUPR3PREINIT,(PSUPPREINITDATA pPreInitData, uint32_t fFlags));
+/** Pointer to supR3PreInit. */
+typedef FNSUPR3PREINIT *PFNSUPR3PREINIT;
+
+/** The current SUPR3HardenedMain state / location. */
+typedef enum SUPR3HARDENEDMAINSTATE
+{
+ SUPR3HARDENEDMAINSTATE_NOT_YET_CALLED = 0,
+ SUPR3HARDENEDMAINSTATE_WIN_EARLY_INIT_CALLED,
+ SUPR3HARDENEDMAINSTATE_WIN_EARLY_IMPORTS_RESOLVED,
+ SUPR3HARDENEDMAINSTATE_WIN_EARLY_STUB_DEVICE_OPENED,
+ SUPR3HARDENEDMAINSTATE_WIN_EARLY_REAL_DEVICE_OPENED,
+ SUPR3HARDENEDMAINSTATE_WIN_EP_CALLED,
+ SUPR3HARDENEDMAINSTATE_WIN_IMPORTS_RESOLVED,
+ SUPR3HARDENEDMAINSTATE_WIN_VERSION_INITIALIZED,
+ SUPR3HARDENEDMAINSTATE_WIN_VERIFY_TRUST_READY,
+ SUPR3HARDENEDMAINSTATE_HARDENED_MAIN_CALLED,
+ SUPR3HARDENEDMAINSTATE_INIT_RUNTIME,
+ SUPR3HARDENEDMAINSTATE_GET_TRUSTED_MAIN,
+ SUPR3HARDENEDMAINSTATE_CALLED_TRUSTED_MAIN,
+ SUPR3HARDENEDMAINSTATE_END,
+ SUPR3HARDENEDMAINSTATE_32BIT_HACK = 0x7fffffff
+} SUPR3HARDENEDMAINSTATE;
+
+
+/*******************************************************************************
+* Global Variables *
+*******************************************************************************/
+extern DECL_HIDDEN_DATA(uint32_t) g_u32Cookie;
+extern DECL_HIDDEN_DATA(uint32_t) g_u32SessionCookie;
+extern DECL_HIDDEN_DATA(uint32_t) g_uSupSessionVersion;
+extern DECL_HIDDEN_DATA(SUPLIBDATA) g_supLibData;
+extern DECL_HIDDEN_DATA(uint32_t) g_uSupFakeMode;
+extern DECL_HIDDEN_DATA(PSUPGLOBALINFOPAGE) g_pSUPGlobalInfoPageR0;
+#ifdef VBOX_INCLUDED_SRC_Support_SUPDrvIOC_h
+extern DECL_HIDDEN_DATA(PSUPQUERYFUNCS) g_pSupFunctions;
+#endif
+extern DECL_HIDDEN_DATA(SUPR3HARDENEDMAINSTATE) g_enmSupR3HardenedMainState;
+#ifdef RT_OS_WINDOWS
+extern DECL_HIDDEN_DATA(bool) g_fSupEarlyProcessInit;
+#endif
+
+
+/*******************************************************************************
+* OS Specific Function *
+*******************************************************************************/
+RT_C_DECLS_BEGIN
+DECLHIDDEN(int) suplibOsInstall(void);
+DECLHIDDEN(int) suplibOsUninstall(void);
+DECLHIDDEN(int) suplibOsInit(PSUPLIBDATA pThis, bool fPreInited, uint32_t fFlags, SUPINITOP *penmWhat, PRTERRINFO pErrInfo);
+DECLHIDDEN(int) suplibOsTerm(PSUPLIBDATA pThis);
+DECLHIDDEN(int) suplibOsHardenedVerifyInit(void);
+DECLHIDDEN(int) suplibOsHardenedVerifyTerm(void);
+DECLHIDDEN(int) suplibOsIOCtl(PSUPLIBDATA pThis, uintptr_t uFunction, void *pvReq, size_t cbReq);
+DECLHIDDEN(int) suplibOsIOCtlFast(PSUPLIBDATA pThis, uintptr_t uFunction, uintptr_t idCpu);
+DECLHIDDEN(int) suplibOsPageAlloc(PSUPLIBDATA pThis, size_t cPages, uint32_t fFlags, void **ppvPages);
+DECLHIDDEN(int) suplibOsPageFree(PSUPLIBDATA pThis, void *pvPages, size_t cPages);
+DECLHIDDEN(int) suplibOsQueryVTxSupported(const char **ppszWhy);
+DECLHIDDEN(bool) suplibOsIsNemSupportedWhenNoVtxOrAmdV(void);
+
+
+/**
+ * Performs the pre-initialization of the support library.
+ *
+ * This is dynamically resolved and invoked by the static library before it
+ * calls RTR3InitEx and thereby SUPR3Init.
+ *
+ * @returns IPRT status code.
+ * @param pPreInitData The pre init data.
+ * @param fFlags The SUPR3HardenedMain flags.
+ */
+DECL_NOTHROW(DECLEXPORT(int)) supR3PreInit(PSUPPREINITDATA pPreInitData, uint32_t fFlags);
+
+
+/** @copydoc RTPathAppPrivateNoArch */
+DECLHIDDEN(int) supR3HardenedPathAppPrivateNoArch(char *pszPath, size_t cchPath);
+/** @copydoc RTPathAppPrivateArch */
+DECLHIDDEN(int) supR3HardenedPathAppPrivateArch(char *pszPath, size_t cchPath);
+/** @copydoc RTPathSharedLibs */
+DECLHIDDEN(int) supR3HardenedPathAppSharedLibs(char *pszPath, size_t cchPath);
+/** @copydoc RTPathAppDocs */
+DECLHIDDEN(int) supR3HardenedPathAppDocs(char *pszPath, size_t cchPath);
+/** @copydoc RTPathExecDir */
+DECLHIDDEN(int) supR3HardenedPathAppBin(char *pszPath, size_t cchPath);
+/** @copydoc RTPathFilename */
+DECLHIDDEN(char *) supR3HardenedPathFilename(const char *pszPath);
+
+/**
+ * Display a fatal error and try call TrustedError or quit.
+ */
+DECL_NO_RETURN(DECLHIDDEN(void)) supR3HardenedFatalMsgV(const char *pszWhere, SUPINITOP enmWhat, int rc,
+ const char *pszMsgFmt, va_list va);
+
+/**
+ * Display a fatal error and try call TrustedError or quit.
+ */
+DECL_NO_RETURN(DECLHIDDEN(void)) supR3HardenedFatalMsg(const char *pszWhere, SUPINITOP enmWhat, int rc,
+ const char *pszMsgFmt, ...);
+
+/**
+ * Display a fatal error and quit.
+ */
+DECL_NO_RETURN(DECLHIDDEN(void)) supR3HardenedFatalV(const char *pszFormat, va_list va);
+
+/**
+ * Display a fatal error and quit.
+ */
+DECL_NO_RETURN(DECLHIDDEN(void)) supR3HardenedFatal(const char *pszFormat, ...);
+
+/**
+ * Display an error which may or may not be fatal.
+ */
+DECLHIDDEN(int) supR3HardenedErrorV(int rc, bool fFatal, const char *pszFormat, va_list va);
+
+/**
+ * Display an error which may or may not be fatal.
+ */
+DECLHIDDEN(int) supR3HardenedError(int rc, bool fFatal, const char *pszFormat, ...);
+
+/**
+ * Open any startup log file specified in the argument.
+ */
+DECLHIDDEN(void) supR3HardenedOpenLog(int *pcArgs, char **papszArgs);
+
+/**
+ * Write to the startup log file.
+ */
+DECLHIDDEN(void) supR3HardenedLogV(const char *pszFormat, va_list va);
+
+/**
+ * Write to the startup log file.
+ */
+DECLHIDDEN(void) supR3HardenedLog(const char *pszFormat, ...);
+
+/**
+ * Flushes the log file.
+ */
+DECLHIDDEN(void) supR3HardenedLogFlush(void);
+
+
+DECLHIDDEN(int) supR3HardenedVerifyAll(bool fFatal, const char *pszProgName, const char *pszExePath, uint32_t fMainFlags);
+DECLHIDDEN(int) supR3HardenedVerifyFixedDir(SUPINSTDIR enmDir, bool fFatal, PCSUPINSTFILE pFile);
+DECLHIDDEN(int) supR3HardenedVerifyFixedFile(const char *pszFilename, bool fFatal);
+DECLHIDDEN(int) supR3HardenedVerifyDir(const char *pszDirPath, bool fRecursive, bool fCheckFiles, PRTERRINFO pErrInfo);
+DECLHIDDEN(int) supR3HardenedVerifyFile(const char *pszFilename, RTHCUINTPTR hNativeFile, bool fMaybe3rdParty,
+ PRTERRINFO pErrInfo);
+#if defined(RT_OS_DARWIN) || defined(RT_OS_LINUX)
+DECLHIDDEN(int) supR3HardenedVerifyFileFollowSymlinks(const char *pszFilename, RTHCUINTPTR hNativeFile,
+ bool fMaybe3rdParty, PRTERRINFO pErrInfo);
+#endif
+DECLHIDDEN(void) supR3HardenedGetPreInitData(PSUPPREINITDATA pPreInitData);
+DECLHIDDEN(int) supR3HardenedRecvPreInitData(PCSUPPREINITDATA pPreInitData);
+
+#ifdef RT_OS_WINDOWS
+DECLHIDDEN(void) supR3HardenedWinInit(uint32_t fFlags, bool fAvastKludge);
+DECLHIDDEN(void) supR3HardenedWinInitAppBin(uint32_t fFlags);
+DECLHIDDEN(void) supR3HardenedWinInitVersion(bool fEarlyInit);
+DECLHIDDEN(void) supR3HardenedWinInitImports(void);
+DECLHIDDEN(void) supR3HardenedWinModifyDllSearchPath(uint32_t fFlags, const char *pszAppBinPath);
+# ifdef IPRT_INCLUDED_nt_nt_h
+DECLHIDDEN(void) supR3HardenedWinGetVeryEarlyImports(uintptr_t uNtDllAddr, PFNNTWAITFORSINGLEOBJECT *ppfnNtWaitForSingleObject,
+ PFNNTSETEVENT *ppfnNtSetEvent);
+# endif
+DECLHIDDEN(void) supR3HardenedWinInitImportsEarly(uintptr_t uNtDllAddr);
+DECLHIDDEN(void) supR3HardenedWinInitSyscalls(bool fReportErrors, PRTERRINFO pErrInfo);
+DECLHIDDEN(PFNRT) supR3HardenedWinGetRealDllSymbol(const char *pszDll, const char *pszProcedure);
+DECLHIDDEN(void) supR3HardenedWinEnableThreadCreation(void);
+DECLHIDDEN(void) supR3HardenedWinResolveVerifyTrustApiAndHookThreadCreation(const char *pszProgName);
+DECLHIDDEN(void) supR3HardenedWinFlushLoaderCache();
+DECLHIDDEN(bool) supR3HardenedWinIsReSpawnNeeded(int iWhich, int cArgs, char **papszArgs);
+DECLHIDDEN(int) supR3HardenedWinReSpawn(int iWhich);
+# ifdef _WINDEF_
+DECLHIDDEN(void) supR3HardenedWinCreateParentWatcherThread(HMODULE hVBoxRT);
+# endif
+DECLHIDDEN(void *) supR3HardenedWinLoadLibrary(const char *pszName, bool fSystem32Only, uint32_t fMainFlags);
+extern RTUTF16 g_wszSupLibHardenedExePath[1024];
+# ifdef RTPATH_MAX
+extern char g_szSupLibHardenedExePath[RTPATH_MAX];
+# endif
+DECLHIDDEN(void) supR3HardenedWinCompactHeaps(void);
+DECLHIDDEN(void) supR3HardenedMainOpenDevice(void);
+DECLHIDDEN(char *) supR3HardenedWinReadErrorInfoDevice(char *pszErrorInfo, size_t cbErrorInfo, const char *pszPrefix);
+DECLHIDDEN(void) supR3HardenedWinReportErrorToParent(const char *pszWhere, SUPINITOP enmWhat, int rc,
+ const char *pszFormat, va_list va);
+#else /* !RT_OS_WINDOWS */
+# if !defined(RT_OS_DARWIN)
+DECLHIDDEN(void) supR3HardenedPosixInit(void);
+# else /* !RT_OS_DARWIN */
+DECLHIDDEN(void) supR3HardenedDarwinInit(void);
+#endif /* !RT_OS_DARWIN */
+#endif /* !RT_OS_WINDOWS */
+
+SUPR3DECL(int) supR3PageLock(void *pvStart, size_t cPages, PSUPPAGE paPages);
+SUPR3DECL(int) supR3PageUnlock(void *pvStart);
+
+RT_C_DECLS_END
+
+
+#endif /* !VBOX_INCLUDED_SRC_Support_SUPLibInternal_h */
+
diff --git a/src/VBox/HostDrivers/Support/SUPLibLdr.cpp b/src/VBox/HostDrivers/Support/SUPLibLdr.cpp
new file mode 100644
index 00000000..2a778fec
--- /dev/null
+++ b/src/VBox/HostDrivers/Support/SUPLibLdr.cpp
@@ -0,0 +1,1153 @@
+/* $Id: SUPLibLdr.cpp $ */
+/** @file
+ * VirtualBox Support Library - Loader related bits.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#define LOG_GROUP LOG_GROUP_SUP
+#include <VBox/sup.h>
+#include <VBox/err.h>
+#include <VBox/param.h>
+#include <VBox/log.h>
+#include <VBox/VBoxTpG.h>
+
+#include <iprt/assert.h>
+#include <iprt/alloc.h>
+#include <iprt/alloca.h>
+#include <iprt/ldr.h>
+#include <iprt/asm.h>
+#include <iprt/mp.h>
+#include <iprt/cpuset.h>
+#include <iprt/thread.h>
+#include <iprt/process.h>
+#include <iprt/path.h>
+#include <iprt/string.h>
+#include <iprt/env.h>
+#include <iprt/rand.h>
+#include <iprt/x86.h>
+
+#include "SUPDrvIOC.h"
+#include "SUPLibInternal.h"
+
+
+/*********************************************************************************************************************************
+* Defined Constants And Macros *
+*********************************************************************************************************************************/
+/** R0 VMM module name. */
+#define VMMR0_NAME "VMMR0"
+
+
+/*********************************************************************************************************************************
+* Structures and Typedefs *
+*********************************************************************************************************************************/
+typedef DECLCALLBACKTYPE(int, FNCALLVMMR0,(PVMR0 pVMR0, unsigned uOperation, void *pvArg));
+typedef FNCALLVMMR0 *PFNCALLVMMR0;
+
+
+/*********************************************************************************************************************************
+* Global Variables *
+*********************************************************************************************************************************/
+/** VMMR0 Load Address. */
+static RTR0PTR g_pvVMMR0 = NIL_RTR0PTR;
+
+
+/*********************************************************************************************************************************
+* Internal Functions *
+*********************************************************************************************************************************/
+static int supLoadModule(const char *pszFilename, const char *pszModule, const char *pszSrvReqHandler,
+ PRTERRINFO pErrInfo, void **ppvImageBase);
+static DECLCALLBACK(int) supLoadModuleResolveImport(RTLDRMOD hLdrMod, const char *pszModule, const char *pszSymbol,
+ unsigned uSymbol, RTUINTPTR *pValue, void *pvUser);
+
+
+SUPR3DECL(int) SUPR3LoadModule(const char *pszFilename, const char *pszModule, void **ppvImageBase, PRTERRINFO pErrInfo)
+{
+ /*
+ * Check that the module can be trusted.
+ */
+ int rc = SUPR3HardenedVerifyPlugIn(pszFilename, pErrInfo);
+ if (RT_SUCCESS(rc))
+ {
+ rc = supLoadModule(pszFilename, pszModule, NULL, pErrInfo, ppvImageBase);
+ if (RT_FAILURE(rc) && !RTErrInfoIsSet(pErrInfo))
+ RTErrInfoSetF(pErrInfo, rc, "SUPR3LoadModule: supLoadModule returned %Rrc", rc);
+ }
+ return rc;
+}
+
+
+SUPR3DECL(int) SUPR3LoadServiceModule(const char *pszFilename, const char *pszModule,
+ const char *pszSrvReqHandler, void **ppvImageBase)
+{
+ AssertPtrReturn(pszSrvReqHandler, VERR_INVALID_PARAMETER);
+
+ /*
+ * Check that the module can be trusted.
+ */
+ int rc = SUPR3HardenedVerifyPlugIn(pszFilename, NULL /*pErrInfo*/);
+ if (RT_SUCCESS(rc))
+ rc = supLoadModule(pszFilename, pszModule, pszSrvReqHandler, NULL /*pErrInfo*/, ppvImageBase);
+ else
+ LogRel(("SUPR3LoadServiceModule: Verification of \"%s\" failed, rc=%Rrc\n", pszFilename, rc));
+ return rc;
+}
+
+
+/**
+ * Argument package for supLoadModuleResolveImport.
+ */
+typedef struct SUPLDRRESIMPARGS
+{
+ const char *pszModule;
+ PRTERRINFO pErrInfo;
+ uint32_t fLoadReq; /**< SUPLDRLOAD_F_XXX */
+} SUPLDRRESIMPARGS, *PSUPLDRRESIMPARGS;
+
+/**
+ * Resolve an external symbol during RTLdrGetBits().
+ *
+ * @returns VBox status code.
+ * @param hLdrMod The loader module handle.
+ * @param pszModule Module name.
+ * @param pszSymbol Symbol name, NULL if uSymbol should be used.
+ * @param uSymbol Symbol ordinal, ~0 if pszSymbol should be used.
+ * @param pValue Where to store the symbol value (address).
+ * @param pvUser User argument.
+ */
+static DECLCALLBACK(int) supLoadModuleResolveImport(RTLDRMOD hLdrMod, const char *pszModule,
+ const char *pszSymbol, unsigned uSymbol, RTUINTPTR *pValue, void *pvUser)
+{
+ NOREF(hLdrMod); NOREF(uSymbol);
+ AssertPtr(pValue);
+ AssertPtr(pvUser);
+ PSUPLDRRESIMPARGS pArgs = (PSUPLDRRESIMPARGS)pvUser;
+
+ /*
+ * Only SUPR0 and VMMR0.r0
+ */
+ if ( pszModule
+ && *pszModule
+ && strcmp(pszModule, "VBoxSup.sys")
+ && strcmp(pszModule, "VBoxDrv.sys") /* old name */
+ && strcmp(pszModule, "VMMR0.r0"))
+ {
+#if defined(RT_OS_WINDOWS) && 0 /* Useful for VMMR0 hacking, not for production use. See also SUPDrv-win.cpp */
+ if (strcmp(pszModule, "ntoskrnl.exe") == 0)
+ {
+ *pValue = 42; /* Non-zero so ring-0 can find the end of the IAT and exclude it when comparing. */
+ return VINF_SUCCESS;
+ }
+#endif
+ AssertMsgFailed(("%s is importing from %s! (expected 'SUPR0.dll' or 'VMMR0.r0', case-sensitive)\n", pArgs->pszModule, pszModule));
+ return RTErrInfoSetF(pArgs->pErrInfo, VERR_SYMBOL_NOT_FOUND,
+ "Unexpected import module '%s' in '%s'", pszModule, pArgs->pszModule);
+ }
+
+ /*
+ * No ordinals.
+ */
+ if (uSymbol != ~0U)
+ {
+ AssertMsgFailed(("%s is importing by ordinal (ord=%d)\n", pArgs->pszModule, uSymbol));
+ return RTErrInfoSetF(pArgs->pErrInfo, VERR_SYMBOL_NOT_FOUND,
+ "Unexpected ordinal import (%#x) in '%s'", uSymbol, pArgs->pszModule);
+ }
+
+ /*
+ * Lookup symbol.
+ */
+ /* Skip the 64-bit ELF import prefix first. */
+ /** @todo is this actually used??? */
+ if (!strncmp(pszSymbol, RT_STR_TUPLE("SUPR0$")))
+ pszSymbol += sizeof("SUPR0$") - 1;
+
+ /*
+ * Check the VMMR0.r0 module if loaded.
+ */
+ if (g_pvVMMR0 != NIL_RTR0PTR)
+ {
+ void *pvValue;
+ if (!SUPR3GetSymbolR0((void *)g_pvVMMR0, pszSymbol, &pvValue))
+ {
+ *pValue = (uintptr_t)pvValue;
+ pArgs->fLoadReq |= SUPLDRLOAD_F_DEP_VMMR0;
+ return VINF_SUCCESS;
+ }
+ }
+
+ /* iterate the function table. */
+ int c = g_pSupFunctions->u.Out.cFunctions;
+ PSUPFUNC pFunc = &g_pSupFunctions->u.Out.aFunctions[0];
+ while (c-- > 0)
+ {
+ if (!strcmp(pFunc->szName, pszSymbol))
+ {
+ *pValue = (uintptr_t)pFunc->pfn;
+ return VINF_SUCCESS;
+ }
+ pFunc++;
+ }
+
+ /*
+ * The GIP.
+ */
+ if ( pszSymbol
+ && g_pSUPGlobalInfoPage
+ && g_pSUPGlobalInfoPageR0
+ && !strcmp(pszSymbol, "g_SUPGlobalInfoPage")
+ )
+ {
+ *pValue = (uintptr_t)g_pSUPGlobalInfoPageR0;
+ return VINF_SUCCESS;
+ }
+
+ /*
+ * Symbols that are undefined by convention.
+ */
+#ifdef RT_OS_SOLARIS
+ static const char * const s_apszConvSyms[] =
+ {
+ "", "mod_getctl",
+ "", "mod_install",
+ "", "mod_remove",
+ "", "mod_info",
+ "", "mod_miscops",
+ };
+ for (unsigned i = 0; i < RT_ELEMENTS(s_apszConvSyms); i += 2)
+ {
+ if ( !RTStrCmp(s_apszConvSyms[i], pszModule)
+ && !RTStrCmp(s_apszConvSyms[i + 1], pszSymbol))
+ {
+ *pValue = ~(uintptr_t)0;
+ return VINF_SUCCESS;
+ }
+ }
+#endif
+
+ /*
+ * Despair.
+ */
+ c = g_pSupFunctions->u.Out.cFunctions;
+ pFunc = &g_pSupFunctions->u.Out.aFunctions[0];
+ while (c-- > 0)
+ {
+ RTAssertMsg2Weak("%d: %s\n", g_pSupFunctions->u.Out.cFunctions - c, pFunc->szName);
+ pFunc++;
+ }
+ RTAssertMsg2Weak("%s is importing %s which we couldn't find\n", pArgs->pszModule, pszSymbol);
+
+ AssertLogRelMsgFailed(("%s is importing %s which we couldn't find\n", pArgs->pszModule, pszSymbol));
+ if (g_uSupFakeMode)
+ {
+ *pValue = 0xdeadbeef;
+ return VINF_SUCCESS;
+ }
+ return RTErrInfoSetF(pArgs->pErrInfo, VERR_SYMBOL_NOT_FOUND,
+ "Unable to locate imported symbol '%s%s%s' for module '%s'",
+ pszModule ? pszModule : "",
+ pszModule && *pszModule ? "." : "",
+ pszSymbol,
+ pArgs->pszModule);
+}
+
+
+/** Argument package for supLoadModuleCalcSizeCB. */
+typedef struct SUPLDRCALCSIZEARGS
+{
+ size_t cbStrings;
+ uint32_t cSymbols;
+ size_t cbImage;
+} SUPLDRCALCSIZEARGS, *PSUPLDRCALCSIZEARGS;
+
+/**
+ * Callback used to calculate the image size.
+ * @return VINF_SUCCESS
+ */
+static DECLCALLBACK(int) supLoadModuleCalcSizeCB(RTLDRMOD hLdrMod, const char *pszSymbol, unsigned uSymbol, RTUINTPTR Value, void *pvUser)
+{
+ PSUPLDRCALCSIZEARGS pArgs = (PSUPLDRCALCSIZEARGS)pvUser;
+ if ( pszSymbol != NULL
+ && *pszSymbol
+ && Value <= pArgs->cbImage)
+ {
+ pArgs->cSymbols++;
+ pArgs->cbStrings += strlen(pszSymbol) + 1;
+ }
+ NOREF(hLdrMod); NOREF(uSymbol);
+ return VINF_SUCCESS;
+}
+
+
+/** Argument package for supLoadModuleCreateTabsCB. */
+typedef struct SUPLDRCREATETABSARGS
+{
+ size_t cbImage;
+ PSUPLDRSYM pSym;
+ char *pszBase;
+ char *psz;
+} SUPLDRCREATETABSARGS, *PSUPLDRCREATETABSARGS;
+
+/**
+ * Callback used to calculate the image size.
+ * @return VINF_SUCCESS
+ */
+static DECLCALLBACK(int) supLoadModuleCreateTabsCB(RTLDRMOD hLdrMod, const char *pszSymbol, unsigned uSymbol, RTUINTPTR Value, void *pvUser)
+{
+ PSUPLDRCREATETABSARGS pArgs = (PSUPLDRCREATETABSARGS)pvUser;
+ if ( pszSymbol != NULL
+ && *pszSymbol
+ && Value <= pArgs->cbImage)
+ {
+ pArgs->pSym->offSymbol = (uint32_t)Value;
+ pArgs->pSym->offName = pArgs->psz - pArgs->pszBase;
+ pArgs->pSym++;
+
+ size_t cbCopy = strlen(pszSymbol) + 1;
+ memcpy(pArgs->psz, pszSymbol, cbCopy);
+ pArgs->psz += cbCopy;
+ }
+ NOREF(hLdrMod); NOREF(uSymbol);
+ return VINF_SUCCESS;
+}
+
+
+/** Argument package for supLoadModuleCompileSegmentsCB. */
+typedef struct SUPLDRCOMPSEGTABARGS
+{
+ uint32_t uStartRva;
+ uint32_t uEndRva;
+ uint32_t fProt;
+ uint32_t iSegs;
+ uint32_t cSegsAlloc;
+ PSUPLDRSEG paSegs;
+ PRTERRINFO pErrInfo;
+} SUPLDRCOMPSEGTABARGS, *PSUPLDRCOMPSEGTABARGS;
+
+/**
+ * @callback_method_impl{FNRTLDRENUMSEGS,
+ * Compile list of segments with the same memory protection.}
+ */
+static DECLCALLBACK(int) supLoadModuleCompileSegmentsCB(RTLDRMOD hLdrMod, PCRTLDRSEG pSeg, void *pvUser)
+{
+ PSUPLDRCOMPSEGTABARGS pArgs = (PSUPLDRCOMPSEGTABARGS)pvUser;
+ AssertCompile(RTMEM_PROT_READ == SUPLDR_PROT_READ);
+ AssertCompile(RTMEM_PROT_WRITE == SUPLDR_PROT_WRITE);
+ AssertCompile(RTMEM_PROT_EXEC == SUPLDR_PROT_EXEC);
+ RT_NOREF(hLdrMod);
+
+ Log2(("supLoadModuleCompileSegmentsCB: %RTptr/%RTptr LB %RTptr/%RTptr prot %#x %s\n",
+ pSeg->LinkAddress, pSeg->RVA, pSeg->cbMapped, pSeg->cb, pSeg->fProt, pSeg->pszName));
+
+ /* Ignore segments not part of the loaded image. */
+ if (pSeg->RVA == NIL_RTLDRADDR || pSeg->cbMapped == 0)
+ {
+ Log2(("supLoadModuleCompileSegmentsCB: -> skipped\n"));
+ return VINF_SUCCESS;
+ }
+
+ /* We currently ASSUME that all relevant segments are in ascending RVA order. */
+ AssertReturn(pSeg->RVA >= pArgs->uEndRva,
+ RTERRINFO_LOG_REL_SET_F(pArgs->pErrInfo, VERR_BAD_EXE_FORMAT, "Out of order segment: %p LB %#zx #%.*s",
+ pSeg->RVA, pSeg->cb, pSeg->cchName, pSeg->pszName));
+
+ /* We ASSUME the cbMapped field is implemented. */
+ AssertReturn(pSeg->cbMapped != NIL_RTLDRADDR, VERR_INTERNAL_ERROR_2);
+ AssertReturn(pSeg->cbMapped < _1G, VERR_INTERNAL_ERROR_4);
+ uint32_t cbMapped = (uint32_t)pSeg->cbMapped;
+ AssertReturn(pSeg->RVA < _1G, VERR_INTERNAL_ERROR_3);
+ uint32_t uRvaSeg = (uint32_t)pSeg->RVA;
+
+ /*
+ * If the protection is the same as the previous segment,
+ * just update uEndRva and continue.
+ */
+ uint32_t fProt = pSeg->fProt;
+#if defined(RT_ARCH_AMD64) || defined(RT_ARCH_X86)
+ if (fProt & RTMEM_PROT_EXEC)
+ fProt |= fProt & RTMEM_PROT_READ;
+#endif
+ if (pSeg->fProt == pArgs->fProt)
+ {
+ pArgs->uEndRva = uRvaSeg + cbMapped;
+ Log2(("supLoadModuleCompileSegmentsCB: -> merged, end %#x\n", pArgs->uEndRva));
+ return VINF_SUCCESS;
+ }
+
+ /*
+ * The protection differs, so commit current segment and start a new one.
+ * However, if the new segment and old segment share a page, this becomes
+ * a little more complicated...
+ */
+ if (pArgs->uStartRva < pArgs->uEndRva)
+ {
+ if (((pArgs->uEndRva - 1) >> PAGE_SHIFT) != (uRvaSeg >> PAGE_SHIFT))
+ {
+ /* No common page, so make the new segment start on a page boundrary. */
+ cbMapped += uRvaSeg & PAGE_OFFSET_MASK;
+ uRvaSeg &= ~(uint32_t)PAGE_OFFSET_MASK;
+ Assert(pArgs->uEndRva <= uRvaSeg);
+ Log2(("supLoadModuleCompileSegmentsCB: -> new, no common\n"));
+ }
+ else if ((fProt & pArgs->fProt) == fProt)
+ {
+ /* The current segment includes the memory protections of the
+ previous, so include the common page in it: */
+ uint32_t const cbCommon = PAGE_SIZE - (uRvaSeg & PAGE_OFFSET_MASK);
+ if (cbCommon >= cbMapped)
+ {
+ pArgs->uEndRva = uRvaSeg + cbMapped;
+ Log2(("supLoadModuleCompileSegmentsCB: -> merge, %#x common, upgrading prot to %#x, end %#x\n",
+ cbCommon, pArgs->fProt, pArgs->uEndRva));
+ return VINF_SUCCESS; /* New segment was smaller than a page. */
+ }
+ cbMapped -= cbCommon;
+ uRvaSeg += cbCommon;
+ Assert(pArgs->uEndRva <= uRvaSeg);
+ Log2(("supLoadModuleCompileSegmentsCB: -> new, %#x common into previous\n", cbCommon));
+ }
+ else if ((fProt & pArgs->fProt) == pArgs->fProt)
+ {
+ /* The new segment includes the memory protections of the
+ previous, so include the common page in it: */
+ cbMapped += uRvaSeg & PAGE_OFFSET_MASK;
+ uRvaSeg &= ~(uint32_t)PAGE_OFFSET_MASK;
+ if (uRvaSeg == pArgs->uStartRva)
+ {
+ pArgs->fProt = fProt;
+ pArgs->uEndRva = uRvaSeg + cbMapped;
+ Log2(("supLoadModuleCompileSegmentsCB: -> upgrade current protection, end %#x\n", pArgs->uEndRva));
+ return VINF_SUCCESS; /* Current segment was smaller than a page. */
+ }
+ Log2(("supLoadModuleCompileSegmentsCB: -> new, %#x common into new\n", (uint32_t)(pSeg->RVA & PAGE_OFFSET_MASK)));
+ }
+ else
+ {
+ /* Create a new segment for the common page with the combined protection. */
+ Log2(("supLoadModuleCompileSegmentsCB: -> it's complicated...\n"));
+ pArgs->uEndRva &= ~(uint32_t)PAGE_OFFSET_MASK;
+ if (pArgs->uEndRva > pArgs->uStartRva)
+ {
+ Log2(("supLoadModuleCompileSegmentsCB: SUP Seg #%u: %#x LB %#x prot %#x\n",
+ pArgs->iSegs, pArgs->uStartRva, pArgs->uEndRva - pArgs->uStartRva, pArgs->fProt));
+ if (pArgs->paSegs)
+ {
+ AssertReturn(pArgs->iSegs < pArgs->cSegsAlloc, VERR_INTERNAL_ERROR_5);
+ pArgs->paSegs[pArgs->iSegs].off = pArgs->uStartRva;
+ pArgs->paSegs[pArgs->iSegs].cb = pArgs->uEndRva - pArgs->uStartRva;
+ pArgs->paSegs[pArgs->iSegs].fProt = pArgs->fProt;
+ pArgs->paSegs[pArgs->iSegs].fUnused = 0;
+ }
+ pArgs->iSegs++;
+ pArgs->uStartRva = pArgs->uEndRva;
+ }
+ pArgs->fProt |= fProt;
+
+ uint32_t const cbCommon = PAGE_SIZE - (uRvaSeg & PAGE_OFFSET_MASK);
+ if (cbCommon >= cbMapped)
+ {
+ fProt |= pArgs->fProt;
+ pArgs->uEndRva = uRvaSeg + cbMapped;
+ return VINF_SUCCESS; /* New segment was smaller than a page. */
+ }
+ cbMapped -= cbCommon;
+ uRvaSeg += cbCommon;
+ Assert(uRvaSeg - pArgs->uStartRva == PAGE_SIZE);
+ }
+
+ /* The current segment should end where the new one starts, no gaps. */
+ pArgs->uEndRva = uRvaSeg;
+
+ /* Emit the current segment */
+ Log2(("supLoadModuleCompileSegmentsCB: SUP Seg #%u: %#x LB %#x prot %#x\n",
+ pArgs->iSegs, pArgs->uStartRva, pArgs->uEndRva - pArgs->uStartRva, pArgs->fProt));
+ if (pArgs->paSegs)
+ {
+ AssertReturn(pArgs->iSegs < pArgs->cSegsAlloc, VERR_INTERNAL_ERROR_5);
+ pArgs->paSegs[pArgs->iSegs].off = pArgs->uStartRva;
+ pArgs->paSegs[pArgs->iSegs].cb = pArgs->uEndRva - pArgs->uStartRva;
+ pArgs->paSegs[pArgs->iSegs].fProt = pArgs->fProt;
+ pArgs->paSegs[pArgs->iSegs].fUnused = 0;
+ }
+ pArgs->iSegs++;
+ }
+ /* else: current segment is empty */
+
+ /* Start the new segment. */
+ Assert(!(uRvaSeg & PAGE_OFFSET_MASK));
+ pArgs->fProt = fProt;
+ pArgs->uStartRva = uRvaSeg;
+ pArgs->uEndRva = uRvaSeg + cbMapped;
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * Worker for supLoadModule().
+ */
+static int supLoadModuleInner(RTLDRMOD hLdrMod, PSUPLDRLOAD pLoadReq, uint32_t cbImageWithEverything,
+ RTR0PTR uImageBase, size_t cbImage, const char *pszModule, const char *pszFilename,
+ bool fNativeLoader, bool fIsVMMR0, const char *pszSrvReqHandler,
+ uint32_t offSymTab, uint32_t cSymbols,
+ uint32_t offStrTab, size_t cbStrTab,
+ uint32_t offSegTab, uint32_t cSegments,
+ PRTERRINFO pErrInfo)
+{
+ /*
+ * Get the image bits.
+ */
+ SUPLDRRESIMPARGS Args = { pszModule, pErrInfo, 0 };
+ int rc = RTLdrGetBits(hLdrMod, &pLoadReq->u.In.abImage[0], uImageBase, supLoadModuleResolveImport, &Args);
+ if (RT_FAILURE(rc))
+ {
+ LogRel(("SUP: RTLdrGetBits failed for %s (%s). rc=%Rrc\n", pszModule, pszFilename, rc));
+ if (!RTErrInfoIsSet(pErrInfo))
+ RTErrInfoSetF(pErrInfo, rc, "RTLdrGetBits failed");
+ return rc;
+ }
+
+ /*
+ * Get the entry points.
+ */
+ RTUINTPTR VMMR0EntryFast = 0;
+ RTUINTPTR VMMR0EntryEx = 0;
+ RTUINTPTR SrvReqHandler = 0;
+ RTUINTPTR ModuleInit = 0;
+ RTUINTPTR ModuleTerm = 0;
+ const char *pszEp = NULL;
+ if (fIsVMMR0)
+ {
+ rc = RTLdrGetSymbolEx(hLdrMod, &pLoadReq->u.In.abImage[0], uImageBase,
+ UINT32_MAX, pszEp = "VMMR0EntryFast", &VMMR0EntryFast);
+ if (RT_SUCCESS(rc))
+ rc = RTLdrGetSymbolEx(hLdrMod, &pLoadReq->u.In.abImage[0], uImageBase,
+ UINT32_MAX, pszEp = "VMMR0EntryEx", &VMMR0EntryEx);
+ }
+ else if (pszSrvReqHandler)
+ rc = RTLdrGetSymbolEx(hLdrMod, &pLoadReq->u.In.abImage[0], uImageBase,
+ UINT32_MAX, pszEp = pszSrvReqHandler, &SrvReqHandler);
+ if (RT_SUCCESS(rc))
+ {
+ int rc2 = RTLdrGetSymbolEx(hLdrMod, &pLoadReq->u.In.abImage[0], uImageBase,
+ UINT32_MAX, pszEp = "ModuleInit", &ModuleInit);
+ if (RT_FAILURE(rc2))
+ ModuleInit = 0;
+
+ rc2 = RTLdrGetSymbolEx(hLdrMod, &pLoadReq->u.In.abImage[0], uImageBase,
+ UINT32_MAX, pszEp = "ModuleTerm", &ModuleTerm);
+ if (RT_FAILURE(rc2))
+ ModuleTerm = 0;
+ }
+ if (RT_FAILURE(rc))
+ {
+ LogRel(("SUP: Failed to get entry point '%s' for %s (%s) rc=%Rrc\n", pszEp, pszModule, pszFilename, rc));
+ return RTErrInfoSetF(pErrInfo, rc, "Failed to resolve entry point '%s'", pszEp);
+ }
+
+ /*
+ * Create the symbol and string tables.
+ */
+ SUPLDRCREATETABSARGS CreateArgs;
+ CreateArgs.cbImage = cbImage;
+ CreateArgs.pSym = (PSUPLDRSYM)&pLoadReq->u.In.abImage[offSymTab];
+ CreateArgs.pszBase = (char *)&pLoadReq->u.In.abImage[offStrTab];
+ CreateArgs.psz = CreateArgs.pszBase;
+ rc = RTLdrEnumSymbols(hLdrMod, 0, NULL, 0, supLoadModuleCreateTabsCB, &CreateArgs);
+ if (RT_FAILURE(rc))
+ {
+ LogRel(("SUP: RTLdrEnumSymbols failed for %s (%s) rc=%Rrc\n", pszModule, pszFilename, rc));
+ return RTErrInfoSetF(pErrInfo, rc, "RTLdrEnumSymbols #2 failed");
+ }
+ AssertRelease((size_t)(CreateArgs.psz - CreateArgs.pszBase) <= cbStrTab);
+ AssertRelease((size_t)(CreateArgs.pSym - (PSUPLDRSYM)&pLoadReq->u.In.abImage[offSymTab]) <= cSymbols);
+
+ /*
+ * Create the segment table.
+ */
+ SUPLDRCOMPSEGTABARGS SegArgs;
+ SegArgs.uStartRva = 0;
+ SegArgs.uEndRva = 0;
+ SegArgs.fProt = RTMEM_PROT_READ;
+ SegArgs.iSegs = 0;
+ SegArgs.cSegsAlloc = cSegments;
+ SegArgs.paSegs = (PSUPLDRSEG)&pLoadReq->u.In.abImage[offSegTab];
+ SegArgs.pErrInfo = pErrInfo;
+ rc = RTLdrEnumSegments(hLdrMod, supLoadModuleCompileSegmentsCB, &SegArgs);
+ if (RT_FAILURE(rc))
+ {
+ LogRel(("SUP: RTLdrEnumSegments failed for %s (%s) rc=%Rrc\n", pszModule, pszFilename, rc));
+ return RTErrInfoSetF(pErrInfo, rc, "RTLdrEnumSegments #2 failed");
+ }
+ SegArgs.uEndRva = (uint32_t)cbImage;
+ AssertReturn(SegArgs.uEndRva == cbImage, VERR_OUT_OF_RANGE);
+ if (SegArgs.uEndRva > SegArgs.uStartRva)
+ {
+ SegArgs.paSegs[SegArgs.iSegs].off = SegArgs.uStartRva;
+ SegArgs.paSegs[SegArgs.iSegs].cb = SegArgs.uEndRva - SegArgs.uStartRva;
+ SegArgs.paSegs[SegArgs.iSegs].fProt = SegArgs.fProt;
+ SegArgs.paSegs[SegArgs.iSegs].fUnused = 0;
+ SegArgs.iSegs++;
+ }
+ for (uint32_t i = 0; i < SegArgs.iSegs; i++)
+ LogRel(("SUP: seg #%u: %c%c%c %#010RX32 LB %#010RX32\n", i, /** @todo LogRel2 */
+ SegArgs.paSegs[i].fProt & SUPLDR_PROT_READ ? 'R' : ' ',
+ SegArgs.paSegs[i].fProt & SUPLDR_PROT_WRITE ? 'W' : ' ',
+ SegArgs.paSegs[i].fProt & SUPLDR_PROT_EXEC ? 'X' : ' ',
+ SegArgs.paSegs[i].off, SegArgs.paSegs[i].cb));
+ AssertRelease(SegArgs.iSegs == cSegments);
+ AssertRelease(SegArgs.cSegsAlloc == cSegments);
+
+ /*
+ * Upload the image.
+ */
+ pLoadReq->Hdr.u32Cookie = g_u32Cookie;
+ pLoadReq->Hdr.u32SessionCookie = g_u32SessionCookie;
+ pLoadReq->Hdr.cbIn = SUP_IOCTL_LDR_LOAD_SIZE_IN(cbImageWithEverything);
+ pLoadReq->Hdr.cbOut = SUP_IOCTL_LDR_LOAD_SIZE_OUT;
+ pLoadReq->Hdr.fFlags = SUPREQHDR_FLAGS_MAGIC | SUPREQHDR_FLAGS_EXTRA_IN;
+ pLoadReq->Hdr.rc = VERR_INTERNAL_ERROR;
+
+ pLoadReq->u.In.pfnModuleInit = (RTR0PTR)ModuleInit;
+ pLoadReq->u.In.pfnModuleTerm = (RTR0PTR)ModuleTerm;
+ if (fIsVMMR0)
+ {
+ pLoadReq->u.In.eEPType = SUPLDRLOADEP_VMMR0;
+ pLoadReq->u.In.EP.VMMR0.pvVMMR0EntryFast = (RTR0PTR)VMMR0EntryFast;
+ pLoadReq->u.In.EP.VMMR0.pvVMMR0EntryEx = (RTR0PTR)VMMR0EntryEx;
+ }
+ else if (pszSrvReqHandler)
+ {
+ pLoadReq->u.In.eEPType = SUPLDRLOADEP_SERVICE;
+ pLoadReq->u.In.EP.Service.pfnServiceReq = (RTR0PTR)SrvReqHandler;
+ pLoadReq->u.In.EP.Service.apvReserved[0] = NIL_RTR0PTR;
+ pLoadReq->u.In.EP.Service.apvReserved[1] = NIL_RTR0PTR;
+ pLoadReq->u.In.EP.Service.apvReserved[2] = NIL_RTR0PTR;
+ }
+ else
+ pLoadReq->u.In.eEPType = SUPLDRLOADEP_NOTHING;
+ pLoadReq->u.In.offStrTab = offStrTab;
+ pLoadReq->u.In.cbStrTab = (uint32_t)cbStrTab;
+ AssertRelease(pLoadReq->u.In.cbStrTab == cbStrTab);
+ pLoadReq->u.In.cbImageBits = (uint32_t)cbImage;
+ pLoadReq->u.In.offSymbols = offSymTab;
+ pLoadReq->u.In.cSymbols = cSymbols;
+ pLoadReq->u.In.offSegments = offSegTab;
+ pLoadReq->u.In.cSegments = cSegments;
+ pLoadReq->u.In.cbImageWithEverything = cbImageWithEverything;
+ pLoadReq->u.In.pvImageBase = uImageBase;
+ pLoadReq->u.In.fFlags = Args.fLoadReq;
+ if (!g_uSupFakeMode)
+ {
+ rc = suplibOsIOCtl(&g_supLibData, SUP_IOCTL_LDR_LOAD, pLoadReq, SUP_IOCTL_LDR_LOAD_SIZE(cbImageWithEverything));
+ if (RT_SUCCESS(rc))
+ rc = pLoadReq->Hdr.rc;
+ else
+ LogRel(("SUP: SUP_IOCTL_LDR_LOAD ioctl for %s (%s) failed rc=%Rrc\n", pszModule, pszFilename, rc));
+ }
+ else
+ rc = VINF_SUCCESS;
+ if ( RT_SUCCESS(rc)
+ || rc == VERR_ALREADY_LOADED /* A competing process. */
+ )
+ {
+ LogRel(("SUP: Loaded %s (%s) at %#RKv - ModuleInit at %RKv and ModuleTerm at %RKv%s\n",
+ pszModule, pszFilename, uImageBase, (RTR0PTR)ModuleInit, (RTR0PTR)ModuleTerm,
+ fNativeLoader ? " using the native ring-0 loader" : ""));
+ if (fIsVMMR0)
+ {
+ g_pvVMMR0 = uImageBase;
+ LogRel(("SUP: VMMR0EntryEx located at %RKv and VMMR0EntryFast at %RKv\n", (RTR0PTR)VMMR0EntryEx, (RTR0PTR)VMMR0EntryFast));
+ }
+#ifdef RT_OS_WINDOWS
+ LogRel(("SUP: windbg> .reload /f %s=%#RKv\n", pszFilename, uImageBase));
+#endif
+ return VINF_SUCCESS;
+ }
+
+ /*
+ * Failed, bail out.
+ */
+ LogRel(("SUP: Loading failed for %s (%s) rc=%Rrc\n", pszModule, pszFilename, rc));
+ if ( pLoadReq->u.Out.uErrorMagic == SUPLDRLOAD_ERROR_MAGIC
+ && pLoadReq->u.Out.szError[0] != '\0')
+ {
+ LogRel(("SUP: %s\n", pLoadReq->u.Out.szError));
+ return RTErrInfoSet(pErrInfo, rc, pLoadReq->u.Out.szError);
+ }
+ return RTErrInfoSet(pErrInfo, rc, "SUP_IOCTL_LDR_LOAD failed");
+}
+
+
+/**
+ * Worker for SUPR3LoadModule().
+ *
+ * @returns VBox status code.
+ * @param pszFilename Name of the VMMR0 image file
+ * @param pszModule The modulen name.
+ * @param pszSrvReqHandler The service request handler symbol name,
+ * optional.
+ * @param pErrInfo Where to store detailed error info. Optional.
+ * @param ppvImageBase Where to return the load address.
+ */
+static int supLoadModule(const char *pszFilename, const char *pszModule, const char *pszSrvReqHandler,
+ PRTERRINFO pErrInfo, void **ppvImageBase)
+{
+ SUPLDROPEN OpenReq;
+
+ /*
+ * Validate input.
+ */
+ AssertPtrReturn(pszFilename, VERR_INVALID_PARAMETER);
+ AssertPtrReturn(pszModule, VERR_INVALID_PARAMETER);
+ AssertPtrReturn(ppvImageBase, VERR_INVALID_PARAMETER);
+ AssertReturn(strlen(pszModule) < sizeof(OpenReq.u.In.szName), VERR_FILENAME_TOO_LONG);
+
+ const bool fIsVMMR0 = !strcmp(pszModule, "VMMR0.r0");
+ AssertReturn(!pszSrvReqHandler || !fIsVMMR0, VERR_INTERNAL_ERROR);
+ *ppvImageBase = NULL;
+
+ /*
+ * First try open it w/o preparing a binary for loading.
+ *
+ * This will be a lot faster if it's already loaded, and it will
+ * avoid fixup issues when using wrapped binaries. With wrapped
+ * ring-0 binaries not all binaries need to be wrapped, so trying
+ * to load it ourselves is not a bug, but intentional behaviour
+ * (even it it asserts in the loader code).
+ */
+ OpenReq.Hdr.u32Cookie = g_u32Cookie;
+ OpenReq.Hdr.u32SessionCookie = g_u32SessionCookie;
+ OpenReq.Hdr.cbIn = SUP_IOCTL_LDR_OPEN_SIZE_IN;
+ OpenReq.Hdr.cbOut = SUP_IOCTL_LDR_OPEN_SIZE_OUT;
+ OpenReq.Hdr.fFlags = SUPREQHDR_FLAGS_DEFAULT;
+ OpenReq.Hdr.rc = VERR_INTERNAL_ERROR;
+ OpenReq.u.In.cbImageWithEverything = 0;
+ OpenReq.u.In.cbImageBits = 0;
+ strcpy(OpenReq.u.In.szName, pszModule);
+ int rc = RTPathAbs(pszFilename, OpenReq.u.In.szFilename, sizeof(OpenReq.u.In.szFilename));
+ if (RT_FAILURE(rc))
+ return rc;
+ if ( (SUPDRV_IOC_VERSION & 0xffff0000) != 0x00300000
+ || g_uSupSessionVersion >= 0x00300001)
+ {
+ if (!g_uSupFakeMode)
+ {
+ rc = suplibOsIOCtl(&g_supLibData, SUP_IOCTL_LDR_OPEN, &OpenReq, SUP_IOCTL_LDR_OPEN_SIZE);
+ if (RT_SUCCESS(rc))
+ rc = OpenReq.Hdr.rc;
+ }
+ else
+ {
+ OpenReq.u.Out.fNeedsLoading = true;
+ OpenReq.u.Out.pvImageBase = 0xef423420;
+ }
+ *ppvImageBase = (void *)OpenReq.u.Out.pvImageBase;
+ if (rc != VERR_MODULE_NOT_FOUND)
+ {
+ if (fIsVMMR0)
+ g_pvVMMR0 = OpenReq.u.Out.pvImageBase;
+ LogRel(("SUP: Opened %s (%s) at %#RKv%s.\n", pszModule, pszFilename, OpenReq.u.Out.pvImageBase,
+ OpenReq.u.Out.fNativeLoader ? " loaded by the native ring-0 loader" : ""));
+#ifdef RT_OS_WINDOWS
+ LogRel(("SUP: windbg> .reload /f %s=%#RKv\n", pszFilename, OpenReq.u.Out.pvImageBase));
+#endif
+ return rc;
+ }
+ }
+
+ /*
+ * Open image file and figure its size.
+ */
+ RTLDRMOD hLdrMod;
+ rc = RTLdrOpenEx(OpenReq.u.In.szFilename, 0 /*fFlags*/, RTLDRARCH_HOST, &hLdrMod, pErrInfo);
+ if (RT_FAILURE(rc))
+ {
+ LogRel(("SUP: RTLdrOpen failed for %s (%s) %Rrc\n", pszModule, OpenReq.u.In.szFilename, rc));
+ return rc;
+ }
+
+ SUPLDRCALCSIZEARGS CalcArgs;
+ CalcArgs.cbStrings = 0;
+ CalcArgs.cSymbols = 0;
+ CalcArgs.cbImage = RTLdrSize(hLdrMod);
+ rc = RTLdrEnumSymbols(hLdrMod, 0, NULL, 0, supLoadModuleCalcSizeCB, &CalcArgs);
+ if (RT_SUCCESS(rc))
+ {
+ /*
+ * Figure out the number of segments needed first.
+ */
+ SUPLDRCOMPSEGTABARGS SegArgs;
+ SegArgs.uStartRva = 0;
+ SegArgs.uEndRva = 0;
+ SegArgs.fProt = RTMEM_PROT_READ;
+ SegArgs.iSegs = 0;
+ SegArgs.cSegsAlloc = 0;
+ SegArgs.paSegs = NULL;
+ SegArgs.pErrInfo = pErrInfo;
+ rc = RTLdrEnumSegments(hLdrMod, supLoadModuleCompileSegmentsCB, &SegArgs);
+ if (RT_SUCCESS(rc))
+ {
+ Assert(SegArgs.uEndRva <= RTLdrSize(hLdrMod));
+ SegArgs.uEndRva = (uint32_t)CalcArgs.cbImage; /* overflow is checked later */
+ if (SegArgs.uEndRva > SegArgs.uStartRva)
+ {
+ Log2(("supLoadModule: SUP Seg #%u: %#x LB %#x prot %#x\n",
+ SegArgs.iSegs, SegArgs.uStartRva, SegArgs.uEndRva - SegArgs.uStartRva, SegArgs.fProt));
+ SegArgs.iSegs++;
+ }
+
+ const uint32_t offSymTab = RT_ALIGN_32(CalcArgs.cbImage, 8);
+ const uint32_t offStrTab = offSymTab + CalcArgs.cSymbols * sizeof(SUPLDRSYM);
+ const uint32_t offSegTab = RT_ALIGN_32(offStrTab + CalcArgs.cbStrings, 8);
+ const uint32_t cbImageWithEverything = RT_ALIGN_32(offSegTab + sizeof(SUPLDRSEG) * SegArgs.iSegs, 8);
+
+ /*
+ * Open the R0 image.
+ */
+ OpenReq.Hdr.u32Cookie = g_u32Cookie;
+ OpenReq.Hdr.u32SessionCookie = g_u32SessionCookie;
+ OpenReq.Hdr.cbIn = SUP_IOCTL_LDR_OPEN_SIZE_IN;
+ OpenReq.Hdr.cbOut = SUP_IOCTL_LDR_OPEN_SIZE_OUT;
+ OpenReq.Hdr.fFlags = SUPREQHDR_FLAGS_DEFAULT;
+ OpenReq.Hdr.rc = VERR_INTERNAL_ERROR;
+ OpenReq.u.In.cbImageWithEverything = cbImageWithEverything;
+ OpenReq.u.In.cbImageBits = (uint32_t)CalcArgs.cbImage;
+ strcpy(OpenReq.u.In.szName, pszModule);
+ rc = RTPathAbs(pszFilename, OpenReq.u.In.szFilename, sizeof(OpenReq.u.In.szFilename));
+ AssertRC(rc);
+ if (RT_SUCCESS(rc))
+ {
+ if (!g_uSupFakeMode)
+ {
+ rc = suplibOsIOCtl(&g_supLibData, SUP_IOCTL_LDR_OPEN, &OpenReq, SUP_IOCTL_LDR_OPEN_SIZE);
+ if (RT_SUCCESS(rc))
+ rc = OpenReq.Hdr.rc;
+ }
+ else
+ {
+ OpenReq.u.Out.fNeedsLoading = true;
+ OpenReq.u.Out.pvImageBase = 0xef423420;
+ }
+ }
+ *ppvImageBase = (void *)OpenReq.u.Out.pvImageBase;
+ if ( RT_SUCCESS(rc)
+ && OpenReq.u.Out.fNeedsLoading)
+ {
+ /*
+ * We need to load it.
+ *
+ * Allocate the request and pass it to an inner work function
+ * that populates it and sends it off to the driver.
+ */
+ const uint32_t cbLoadReq = SUP_IOCTL_LDR_LOAD_SIZE(cbImageWithEverything);
+ PSUPLDRLOAD pLoadReq = (PSUPLDRLOAD)RTMemTmpAlloc(cbLoadReq);
+ if (pLoadReq)
+ {
+ rc = supLoadModuleInner(hLdrMod, pLoadReq, cbImageWithEverything, OpenReq.u.Out.pvImageBase, CalcArgs.cbImage,
+ pszModule, pszFilename, OpenReq.u.Out.fNativeLoader, fIsVMMR0, pszSrvReqHandler,
+ offSymTab, CalcArgs.cSymbols,
+ offStrTab, CalcArgs.cbStrings,
+ offSegTab, SegArgs.iSegs,
+ pErrInfo);
+ RTMemTmpFree(pLoadReq);
+ }
+ else
+ {
+ AssertMsgFailed(("failed to allocated %u bytes for SUPLDRLOAD_IN structure!\n", SUP_IOCTL_LDR_LOAD_SIZE(cbImageWithEverything)));
+ rc = RTErrInfoSetF(pErrInfo, VERR_NO_TMP_MEMORY, "Failed to allocate %u bytes for the load request",
+ SUP_IOCTL_LDR_LOAD_SIZE(cbImageWithEverything));
+ }
+ }
+ /*
+ * Already loaded?
+ */
+ else if (RT_SUCCESS(rc))
+ {
+ if (fIsVMMR0)
+ g_pvVMMR0 = OpenReq.u.Out.pvImageBase;
+ LogRel(("SUP: Opened %s (%s) at %#RKv%s.\n", pszModule, pszFilename, OpenReq.u.Out.pvImageBase,
+ OpenReq.u.Out.fNativeLoader ? " loaded by the native ring-0 loader" : ""));
+#ifdef RT_OS_WINDOWS
+ LogRel(("SUP: windbg> .reload /f %s=%#RKv\n", pszFilename, OpenReq.u.Out.pvImageBase));
+#endif
+ }
+ /*
+ * No, failed.
+ */
+ else
+ RTErrInfoSet(pErrInfo, rc, "SUP_IOCTL_LDR_OPEN failed");
+ }
+ else if (!RTErrInfoIsSet(pErrInfo) && pErrInfo)
+ RTErrInfoSetF(pErrInfo, rc, "RTLdrEnumSegments #1 failed");
+ }
+ else
+ RTErrInfoSetF(pErrInfo, rc, "RTLdrEnumSymbols #1 failed");
+ RTLdrClose(hLdrMod);
+ return rc;
+}
+
+
+SUPR3DECL(int) SUPR3FreeModule(void *pvImageBase)
+{
+ /* fake */
+ if (RT_UNLIKELY(g_uSupFakeMode))
+ {
+ g_pvVMMR0 = NIL_RTR0PTR;
+ return VINF_SUCCESS;
+ }
+
+ /*
+ * Free the requested module.
+ */
+ SUPLDRFREE Req;
+ Req.Hdr.u32Cookie = g_u32Cookie;
+ Req.Hdr.u32SessionCookie = g_u32SessionCookie;
+ Req.Hdr.cbIn = SUP_IOCTL_LDR_FREE_SIZE_IN;
+ Req.Hdr.cbOut = SUP_IOCTL_LDR_FREE_SIZE_OUT;
+ Req.Hdr.fFlags = SUPREQHDR_FLAGS_DEFAULT;
+ Req.Hdr.rc = VERR_INTERNAL_ERROR;
+ Req.u.In.pvImageBase = (RTR0PTR)pvImageBase;
+ int rc = suplibOsIOCtl(&g_supLibData, SUP_IOCTL_LDR_FREE, &Req, SUP_IOCTL_LDR_FREE_SIZE);
+ if (RT_SUCCESS(rc))
+ rc = Req.Hdr.rc;
+ if ( RT_SUCCESS(rc)
+ && (RTR0PTR)pvImageBase == g_pvVMMR0)
+ g_pvVMMR0 = NIL_RTR0PTR;
+ return rc;
+}
+
+
+SUPR3DECL(int) SUPR3GetSymbolR0(void *pvImageBase, const char *pszSymbol, void **ppvValue)
+{
+ *ppvValue = NULL;
+
+ /* fake */
+ if (RT_UNLIKELY(g_uSupFakeMode))
+ {
+ *ppvValue = (void *)(uintptr_t)0xdeadf00d;
+ return VINF_SUCCESS;
+ }
+
+ /*
+ * Do ioctl.
+ */
+ SUPLDRGETSYMBOL Req;
+ Req.Hdr.u32Cookie = g_u32Cookie;
+ Req.Hdr.u32SessionCookie = g_u32SessionCookie;
+ Req.Hdr.cbIn = SUP_IOCTL_LDR_GET_SYMBOL_SIZE_IN;
+ Req.Hdr.cbOut = SUP_IOCTL_LDR_GET_SYMBOL_SIZE_OUT;
+ Req.Hdr.fFlags = SUPREQHDR_FLAGS_DEFAULT;
+ Req.Hdr.rc = VERR_INTERNAL_ERROR;
+ Req.u.In.pvImageBase = (RTR0PTR)pvImageBase;
+ size_t cchSymbol = strlen(pszSymbol);
+ if (cchSymbol >= sizeof(Req.u.In.szSymbol))
+ return VERR_SYMBOL_NOT_FOUND;
+ memcpy(Req.u.In.szSymbol, pszSymbol, cchSymbol + 1);
+ int rc = suplibOsIOCtl(&g_supLibData, SUP_IOCTL_LDR_GET_SYMBOL, &Req, SUP_IOCTL_LDR_GET_SYMBOL_SIZE);
+ if (RT_SUCCESS(rc))
+ rc = Req.Hdr.rc;
+ if (RT_SUCCESS(rc))
+ *ppvValue = (void *)Req.u.Out.pvSymbol;
+ return rc;
+}
+
+
+SUPR3DECL(int) SUPR3LoadVMM(const char *pszFilename, PRTERRINFO pErrInfo)
+{
+ void *pvImageBase;
+ return SUPR3LoadModule(pszFilename, "VMMR0.r0", &pvImageBase, pErrInfo);
+}
+
+
+SUPR3DECL(int) SUPR3UnloadVMM(void)
+{
+ return SUPR3FreeModule((void*)g_pvVMMR0);
+}
+
+
+/**
+ * Worker for SUPR3HardenedLdrLoad and SUPR3HardenedLdrLoadAppPriv.
+ *
+ * @returns iprt status code.
+ * @param pszFilename The full file name.
+ * @param phLdrMod Where to store the handle to the loaded module.
+ * @param fFlags See RTLDFLAGS_.
+ * @param pErrInfo Where to return extended error information.
+ * Optional.
+ *
+ */
+static int supR3HardenedLdrLoadIt(const char *pszFilename, PRTLDRMOD phLdrMod, uint32_t fFlags, PRTERRINFO pErrInfo)
+{
+#ifdef VBOX_WITH_HARDENING
+ /*
+ * Verify the image file.
+ */
+ int rc = SUPR3HardenedVerifyInit();
+ if (RT_FAILURE(rc))
+ rc = supR3HardenedVerifyFixedFile(pszFilename, false /* fFatal */);
+ if (RT_FAILURE(rc))
+ {
+ LogRel(("supR3HardenedLdrLoadIt: Verification of \"%s\" failed, rc=%Rrc\n", pszFilename, rc));
+ return RTErrInfoSet(pErrInfo, rc, "supR3HardenedVerifyFixedFile failed");
+ }
+#endif
+
+ /*
+ * Try load it.
+ */
+ return RTLdrLoadEx(pszFilename, phLdrMod, fFlags, pErrInfo);
+}
+
+
+SUPR3DECL(int) SUPR3HardenedLdrLoad(const char *pszFilename, PRTLDRMOD phLdrMod, uint32_t fFlags, PRTERRINFO pErrInfo)
+{
+ /*
+ * Validate input.
+ */
+ RTErrInfoClear(pErrInfo);
+ AssertPtrReturn(pszFilename, VERR_INVALID_POINTER);
+ AssertPtrReturn(phLdrMod, VERR_INVALID_POINTER);
+ *phLdrMod = NIL_RTLDRMOD;
+ AssertReturn(RTPathHavePath(pszFilename), VERR_INVALID_PARAMETER);
+
+ /*
+ * Add the default extension if it's missing.
+ */
+ if (!RTPathHasSuffix(pszFilename))
+ {
+ const char *pszSuff = RTLdrGetSuff();
+ size_t cchSuff = strlen(pszSuff);
+ size_t cchFilename = strlen(pszFilename);
+ char *psz = (char *)alloca(cchFilename + cchSuff + 1);
+ AssertReturn(psz, VERR_NO_TMP_MEMORY);
+ memcpy(psz, pszFilename, cchFilename);
+ memcpy(psz + cchFilename, pszSuff, cchSuff + 1);
+ pszFilename = psz;
+ }
+
+ /*
+ * Pass it on to the common library loader.
+ */
+ return supR3HardenedLdrLoadIt(pszFilename, phLdrMod, fFlags, pErrInfo);
+}
+
+
+SUPR3DECL(int) SUPR3HardenedLdrLoadAppPriv(const char *pszFilename, PRTLDRMOD phLdrMod, uint32_t fFlags, PRTERRINFO pErrInfo)
+{
+ LogFlow(("SUPR3HardenedLdrLoadAppPriv: pszFilename=%p:{%s} phLdrMod=%p fFlags=%08x pErrInfo=%p\n", pszFilename, pszFilename, phLdrMod, fFlags, pErrInfo));
+
+ /*
+ * Validate input.
+ */
+ RTErrInfoClear(pErrInfo);
+ AssertPtrReturn(phLdrMod, VERR_INVALID_PARAMETER);
+ *phLdrMod = NIL_RTLDRMOD;
+ AssertPtrReturn(pszFilename, VERR_INVALID_PARAMETER);
+ AssertMsgReturn(!RTPathHavePath(pszFilename), ("%s\n", pszFilename), VERR_INVALID_PARAMETER);
+
+ /*
+ * Check the filename.
+ */
+ size_t cchFilename = strlen(pszFilename);
+ AssertMsgReturn(cchFilename < (RTPATH_MAX / 4) * 3, ("%zu\n", cchFilename), VERR_INVALID_PARAMETER);
+
+ const char *pszExt = "";
+ size_t cchExt = 0;
+ if (!RTPathHasSuffix(pszFilename))
+ {
+ pszExt = RTLdrGetSuff();
+ cchExt = strlen(pszExt);
+ }
+
+ /*
+ * Construct the private arch path and check if the file exists.
+ */
+ char szPath[RTPATH_MAX];
+ int rc = RTPathAppPrivateArch(szPath, sizeof(szPath) - 1 - cchExt - cchFilename);
+ AssertRCReturn(rc, rc);
+
+ char *psz = strchr(szPath, '\0');
+ *psz++ = RTPATH_SLASH;
+ memcpy(psz, pszFilename, cchFilename);
+ psz += cchFilename;
+ memcpy(psz, pszExt, cchExt + 1);
+
+ if (!RTPathExists(szPath))
+ {
+ LogRel(("SUPR3HardenedLdrLoadAppPriv: \"%s\" not found\n", szPath));
+ return VERR_FILE_NOT_FOUND;
+ }
+
+ /*
+ * Pass it on to SUPR3HardenedLdrLoad.
+ */
+ rc = SUPR3HardenedLdrLoad(szPath, phLdrMod, fFlags, pErrInfo);
+
+ LogFlow(("SUPR3HardenedLdrLoadAppPriv: returns %Rrc\n", rc));
+ return rc;
+}
+
+
+SUPR3DECL(int) SUPR3HardenedLdrLoadPlugIn(const char *pszFilename, PRTLDRMOD phLdrMod, PRTERRINFO pErrInfo)
+{
+ /*
+ * Validate input.
+ */
+ RTErrInfoClear(pErrInfo);
+ AssertPtrReturn(phLdrMod, VERR_INVALID_PARAMETER);
+ *phLdrMod = NIL_RTLDRMOD;
+ AssertPtrReturn(pszFilename, VERR_INVALID_PARAMETER);
+ AssertReturn(RTPathStartsWithRoot(pszFilename), VERR_INVALID_PARAMETER);
+
+#ifdef VBOX_WITH_HARDENING
+ /*
+ * Verify the image file.
+ */
+ int rc = supR3HardenedVerifyFile(pszFilename, RTHCUINTPTR_MAX, true /*fMaybe3rdParty*/, pErrInfo);
+ if (RT_FAILURE(rc))
+ {
+ if (!RTErrInfoIsSet(pErrInfo))
+ LogRel(("supR3HardenedVerifyFile: Verification of \"%s\" failed, rc=%Rrc\n", pszFilename, rc));
+ return rc;
+ }
+#endif
+
+ /*
+ * Try load it.
+ */
+ return RTLdrLoadEx(pszFilename, phLdrMod, RTLDRLOAD_FLAGS_LOCAL, pErrInfo);
+}
+
diff --git a/src/VBox/HostDrivers/Support/SUPLibSem.cpp b/src/VBox/HostDrivers/Support/SUPLibSem.cpp
new file mode 100644
index 00000000..df11ffde
--- /dev/null
+++ b/src/VBox/HostDrivers/Support/SUPLibSem.cpp
@@ -0,0 +1,366 @@
+/* $Id: SUPLibSem.cpp $ */
+/** @file
+ * VirtualBox Support Library - Semaphores, ring-3 implementation.
+ */
+
+/*
+ * Copyright (C) 2009-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#define LOG_GROUP LOG_GROUP_SUP
+#include <VBox/sup.h>
+
+#include <iprt/errcore.h>
+#include <VBox/param.h>
+#include <iprt/assert.h>
+#include <iprt/semaphore.h>
+#include <iprt/time.h>
+
+#include "SUPLibInternal.h"
+#include "SUPDrvIOC.h"
+
+
+/**
+ * Worker that makes a SUP_IOCTL_SEM_OP2 request.
+ *
+ * @returns VBox status code.
+ * @param pSession The session handle.
+ * @param uType The semaphore type.
+ * @param hSem The semaphore handle.
+ * @param uOp The operation.
+ * @param u64Arg The argument if applicable, otherwise 0.
+ */
+DECLINLINE(int) supSemOp2(PSUPDRVSESSION pSession, uint32_t uType, uintptr_t hSem, uint32_t uOp, uint64_t u64Arg)
+{
+ NOREF(pSession);
+ SUPSEMOP2 Req;
+ Req.Hdr.u32Cookie = g_u32Cookie;
+ Req.Hdr.u32SessionCookie = g_u32SessionCookie;
+ Req.Hdr.cbIn = SUP_IOCTL_SEM_OP2_SIZE_IN;
+ Req.Hdr.cbOut = SUP_IOCTL_SEM_OP2_SIZE_OUT;
+ Req.Hdr.fFlags = SUPREQHDR_FLAGS_DEFAULT;
+ Req.Hdr.rc = VERR_INTERNAL_ERROR;
+ Req.u.In.uType = uType;
+ Req.u.In.hSem = (uint32_t)hSem;
+ AssertReturn(Req.u.In.hSem == hSem, VERR_INVALID_HANDLE);
+ Req.u.In.uOp = uOp;
+ Req.u.In.uReserved = 0;
+ Req.u.In.uArg.u64 = u64Arg;
+ int rc = suplibOsIOCtl(&g_supLibData, SUP_IOCTL_SEM_OP2, &Req, sizeof(Req));
+ if (RT_SUCCESS(rc))
+ rc = Req.Hdr.rc;
+
+ return rc;
+}
+
+
+/**
+ * Worker that makes a SUP_IOCTL_SEM_OP3 request.
+ *
+ * @returns VBox status code.
+ * @param pSession The session handle.
+ * @param uType The semaphore type.
+ * @param hSem The semaphore handle.
+ * @param uOp The operation.
+ * @param pReq The request structure. The caller should pick
+ * the output data from it himself.
+ */
+DECLINLINE(int) supSemOp3(PSUPDRVSESSION pSession, uint32_t uType, uintptr_t hSem, uint32_t uOp, PSUPSEMOP3 pReq)
+{
+ NOREF(pSession);
+ pReq->Hdr.u32Cookie = g_u32Cookie;
+ pReq->Hdr.u32SessionCookie = g_u32SessionCookie;
+ pReq->Hdr.cbIn = SUP_IOCTL_SEM_OP3_SIZE_IN;
+ pReq->Hdr.cbOut = SUP_IOCTL_SEM_OP3_SIZE_OUT;
+ pReq->Hdr.fFlags = SUPREQHDR_FLAGS_DEFAULT;
+ pReq->Hdr.rc = VERR_INTERNAL_ERROR;
+ pReq->u.In.uType = uType;
+ pReq->u.In.hSem = (uint32_t)hSem;
+ AssertReturn(pReq->u.In.hSem == hSem, VERR_INVALID_HANDLE);
+ pReq->u.In.uOp = uOp;
+ pReq->u.In.u32Reserved = 0;
+ pReq->u.In.u64Reserved = 0;
+ int rc = suplibOsIOCtl(&g_supLibData, SUP_IOCTL_SEM_OP3, pReq, sizeof(*pReq));
+ if (RT_SUCCESS(rc))
+ rc = pReq->Hdr.rc;
+
+ return rc;
+}
+
+
+SUPDECL(int) SUPSemEventCreate(PSUPDRVSESSION pSession, PSUPSEMEVENT phEvent)
+{
+ AssertPtrReturn(phEvent, VERR_INVALID_POINTER);
+
+ int rc;
+ if (!g_supLibData.fDriverless)
+ {
+ SUPSEMOP3 Req;
+ rc = supSemOp3(pSession, SUP_SEM_TYPE_EVENT, (uintptr_t)NIL_SUPSEMEVENT, SUPSEMOP3_CREATE, &Req);
+ if (RT_SUCCESS(rc))
+ *phEvent = (SUPSEMEVENT)(uintptr_t)Req.u.Out.hSem;
+ }
+ else
+ {
+ RTSEMEVENT hEvent;
+ rc = RTSemEventCreate(&hEvent);
+ if (RT_SUCCESS(rc))
+ *phEvent = (SUPSEMEVENT)hEvent;
+ }
+ return rc;
+}
+
+
+SUPDECL(int) SUPSemEventClose(PSUPDRVSESSION pSession, SUPSEMEVENT hEvent)
+{
+ if (hEvent == NIL_SUPSEMEVENT)
+ return VINF_SUCCESS;
+ int rc;
+ if (!g_supLibData.fDriverless)
+ rc = supSemOp2(pSession, SUP_SEM_TYPE_EVENT, (uintptr_t)hEvent, SUPSEMOP2_CLOSE, 0);
+ else
+ rc = RTSemEventDestroy((RTSEMEVENT)hEvent);
+ return rc;
+}
+
+
+SUPDECL(int) SUPSemEventSignal(PSUPDRVSESSION pSession, SUPSEMEVENT hEvent)
+{
+ int rc;
+ if (!g_supLibData.fDriverless)
+ rc = supSemOp2(pSession, SUP_SEM_TYPE_EVENT, (uintptr_t)hEvent, SUPSEMOP2_SIGNAL, 0);
+ else
+ rc = RTSemEventSignal((RTSEMEVENT)hEvent);
+ return rc;
+}
+
+
+SUPDECL(int) SUPSemEventWaitNoResume(PSUPDRVSESSION pSession, SUPSEMEVENT hEvent, uint32_t cMillies)
+{
+ int rc;
+ if (!g_supLibData.fDriverless)
+ rc = supSemOp2(pSession, SUP_SEM_TYPE_EVENT, (uintptr_t)hEvent, SUPSEMOP2_WAIT_MS_REL, cMillies);
+ else
+ rc = RTSemEventWaitNoResume((RTSEMEVENT)hEvent, cMillies);
+ return rc;
+}
+
+
+SUPDECL(int) SUPSemEventWaitNsAbsIntr(PSUPDRVSESSION pSession, SUPSEMEVENT hEvent, uint64_t uNsTimeout)
+{
+ int rc;
+ if (!g_supLibData.fDriverless)
+ rc = supSemOp2(pSession, SUP_SEM_TYPE_EVENT, (uintptr_t)hEvent, SUPSEMOP2_WAIT_NS_ABS, uNsTimeout);
+ else
+ {
+#if 0
+ rc = RTSemEventWaitEx((RTSEMEVENT)hEvent,
+ RTSEMWAIT_FLAGS_ABSOLUTE | RTSEMWAIT_FLAGS_NANOSECS | RTSEMWAIT_FLAGS_NORESUME, uNsTimeout);
+#else
+ uint64_t nsNow = RTTimeNanoTS();
+ if (nsNow < uNsTimeout)
+ rc = RTSemEventWaitNoResume((RTSEMEVENT)hEvent, (uNsTimeout - nsNow + RT_NS_1MS - 1) / RT_NS_1MS);
+ else
+ rc = VERR_TIMEOUT;
+#endif
+ }
+ return rc;
+}
+
+
+SUPDECL(int) SUPSemEventWaitNsRelIntr(PSUPDRVSESSION pSession, SUPSEMEVENT hEvent, uint64_t cNsTimeout)
+{
+ int rc;
+ if (!g_supLibData.fDriverless)
+ rc = supSemOp2(pSession, SUP_SEM_TYPE_EVENT, (uintptr_t)hEvent, SUPSEMOP2_WAIT_NS_REL, cNsTimeout);
+ else
+ {
+#if 0
+ rc = RTSemEventWaitEx((RTSEMEVENT)hEvent,
+ RTSEMWAIT_FLAGS_RELATIVE | RTSEMWAIT_FLAGS_NANOSECS | RTSEMWAIT_FLAGS_NORESUME, cNsTimeout);
+#else
+ rc = RTSemEventWaitNoResume((RTSEMEVENT)hEvent, (cNsTimeout + RT_NS_1MS - 1) / RT_NS_1MS);
+#endif
+ }
+ return rc;
+}
+
+
+SUPDECL(uint32_t) SUPSemEventGetResolution(PSUPDRVSESSION pSession)
+{
+ if (!g_supLibData.fDriverless)
+ {
+ SUPSEMOP3 Req;
+ int rc = supSemOp3(pSession, SUP_SEM_TYPE_EVENT, (uintptr_t)NIL_SUPSEMEVENT, SUPSEMOP3_GET_RESOLUTION, &Req);
+ if (RT_SUCCESS(rc))
+ return Req.u.Out.cNsResolution;
+ return 1000 / 100;
+ }
+#if 0
+ return RTSemEventGetResolution();
+#else
+ return RT_NS_1MS;
+#endif
+}
+
+
+
+
+
+SUPDECL(int) SUPSemEventMultiCreate(PSUPDRVSESSION pSession, PSUPSEMEVENTMULTI phEventMulti)
+{
+ AssertPtrReturn(phEventMulti, VERR_INVALID_POINTER);
+
+ int rc;
+ if (!g_supLibData.fDriverless)
+ {
+ SUPSEMOP3 Req;
+ rc = supSemOp3(pSession, SUP_SEM_TYPE_EVENT_MULTI, (uintptr_t)NIL_SUPSEMEVENTMULTI, SUPSEMOP3_CREATE, &Req);
+ if (RT_SUCCESS(rc))
+ *phEventMulti = (SUPSEMEVENTMULTI)(uintptr_t)Req.u.Out.hSem;
+ }
+ else
+ {
+ RTSEMEVENTMULTI hEventMulti;
+ rc = RTSemEventMultiCreate(&hEventMulti);
+ if (RT_SUCCESS(rc))
+ *phEventMulti = (SUPSEMEVENTMULTI)hEventMulti;
+ }
+ return rc;
+}
+
+
+SUPDECL(int) SUPSemEventMultiClose(PSUPDRVSESSION pSession, SUPSEMEVENTMULTI hEventMulti)
+{
+ if (hEventMulti == NIL_SUPSEMEVENTMULTI)
+ return VINF_SUCCESS;
+ int rc;
+ if (!g_supLibData.fDriverless)
+ rc = supSemOp2(pSession, SUP_SEM_TYPE_EVENT_MULTI, (uintptr_t)hEventMulti, SUPSEMOP2_CLOSE, 0);
+ else
+ rc = RTSemEventMultiDestroy((RTSEMEVENTMULTI)hEventMulti);
+ return rc;
+}
+
+
+SUPDECL(int) SUPSemEventMultiSignal(PSUPDRVSESSION pSession, SUPSEMEVENTMULTI hEventMulti)
+{
+ int rc;
+ if (!g_supLibData.fDriverless)
+ rc = supSemOp2(pSession, SUP_SEM_TYPE_EVENT_MULTI, (uintptr_t)hEventMulti, SUPSEMOP2_SIGNAL, 0);
+ else
+ rc = RTSemEventMultiSignal((RTSEMEVENTMULTI)hEventMulti);
+ return rc;
+}
+
+
+SUPDECL(int) SUPSemEventMultiReset(PSUPDRVSESSION pSession, SUPSEMEVENTMULTI hEventMulti)
+{
+ int rc;
+ if (!g_supLibData.fDriverless)
+ rc = supSemOp2(pSession, SUP_SEM_TYPE_EVENT_MULTI, (uintptr_t)hEventMulti, SUPSEMOP2_RESET, 0);
+ else
+ rc = RTSemEventMultiReset((RTSEMEVENTMULTI)hEventMulti);
+ return rc;
+}
+
+
+SUPDECL(int) SUPSemEventMultiWaitNoResume(PSUPDRVSESSION pSession, SUPSEMEVENTMULTI hEventMulti, uint32_t cMillies)
+{
+ int rc;
+ if (!g_supLibData.fDriverless)
+ rc = supSemOp2(pSession, SUP_SEM_TYPE_EVENT_MULTI, (uintptr_t)hEventMulti, SUPSEMOP2_WAIT_MS_REL, cMillies);
+ else
+ rc = RTSemEventMultiWaitNoResume((RTSEMEVENTMULTI)hEventMulti, cMillies);
+ return rc;
+}
+
+
+SUPDECL(int) SUPSemEventMultiWaitNsAbsIntr(PSUPDRVSESSION pSession, SUPSEMEVENTMULTI hEventMulti, uint64_t uNsTimeout)
+{
+ int rc;
+ if (!g_supLibData.fDriverless)
+ rc = supSemOp2(pSession, SUP_SEM_TYPE_EVENT_MULTI, (uintptr_t)hEventMulti, SUPSEMOP2_WAIT_NS_ABS, uNsTimeout);
+ else
+ {
+#if 0
+ rc = RTSemEventMultiWaitEx((RTSEMEVENTMULTI)hEventMulti,
+ RTSEMWAIT_FLAGS_ABSOLUTE | RTSEMWAIT_FLAGS_NANOSECS | RTSEMWAIT_FLAGS_NORESUME, uNsTimeout);
+#else
+ uint64_t nsNow = RTTimeNanoTS();
+ if (nsNow < uNsTimeout)
+ rc = RTSemEventMultiWaitNoResume((RTSEMEVENTMULTI)hEventMulti, (uNsTimeout - nsNow + RT_NS_1MS - 1) / RT_NS_1MS);
+ else
+ rc = VERR_TIMEOUT;
+#endif
+ }
+ return rc;
+}
+
+
+SUPDECL(int) SUPSemEventMultiWaitNsRelIntr(PSUPDRVSESSION pSession, SUPSEMEVENTMULTI hEventMulti, uint64_t cNsTimeout)
+{
+ int rc;
+ if (!g_supLibData.fDriverless)
+ rc = supSemOp2(pSession, SUP_SEM_TYPE_EVENT_MULTI, (uintptr_t)hEventMulti, SUPSEMOP2_WAIT_NS_REL, cNsTimeout);
+ else
+ {
+#if 0
+ rc = RTSemEventMultiWaitEx((RTSEMEVENTMULTI)hEventMulti,
+ RTSEMWAIT_FLAGS_RELATIVE | RTSEMWAIT_FLAGS_NANOSECS | RTSEMWAIT_FLAGS_NORESUME, cNsTimeout);
+#else
+ rc = RTSemEventMultiWaitNoResume((RTSEMEVENTMULTI)hEventMulti, (cNsTimeout + RT_NS_1MS - 1) / RT_NS_1MS);
+#endif
+ }
+ return rc;
+}
+
+
+SUPDECL(uint32_t) SUPSemEventMultiGetResolution(PSUPDRVSESSION pSession)
+{
+ if (!g_supLibData.fDriverless)
+ {
+ SUPSEMOP3 Req;
+ int rc = supSemOp3(pSession, SUP_SEM_TYPE_EVENT_MULTI, (uintptr_t)NIL_SUPSEMEVENTMULTI, SUPSEMOP3_GET_RESOLUTION, &Req);
+ if (RT_SUCCESS(rc))
+ return Req.u.Out.cNsResolution;
+ return 1000 / 100;
+ }
+#if 0
+ return RTSemEventMultiGetResolution();
+#else
+ return RT_NS_1MS;
+#endif
+}
+
diff --git a/src/VBox/HostDrivers/Support/SUPLibTracerA.asm b/src/VBox/HostDrivers/Support/SUPLibTracerA.asm
new file mode 100644
index 00000000..09c7e198
--- /dev/null
+++ b/src/VBox/HostDrivers/Support/SUPLibTracerA.asm
@@ -0,0 +1,232 @@
+; $Id: SUPLibTracerA.asm $
+;; @file
+; VirtualBox Support Library - Tracer Interface, Assembly bits.
+;
+
+;
+; Copyright (C) 2012-2023 Oracle and/or its affiliates.
+;
+; This file is part of VirtualBox base platform packages, as
+; available from https://www.virtualbox.org.
+;
+; This program is free software; you can redistribute it and/or
+; modify it under the terms of the GNU General Public License
+; as published by the Free Software Foundation, in version 3 of the
+; License.
+;
+; This program is distributed in the hope that it will be useful, but
+; WITHOUT ANY WARRANTY; without even the implied warranty of
+; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+; General Public License for more details.
+;
+; You should have received a copy of the GNU General Public License
+; along with this program; if not, see <https://www.gnu.org/licenses>.
+;
+; The contents of this file may alternatively be used under the terms
+; of the Common Development and Distribution License Version 1.0
+; (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+; in the VirtualBox distribution, in which case the provisions of the
+; CDDL are applicable instead of those of the GPL.
+;
+; You may elect to license modified versions of this file under the
+; terms and conditions of either the GPL or the CDDL or both.
+;
+; SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+;
+
+;*******************************************************************************
+;* Header Files *
+;*******************************************************************************
+%include "iprt/asmdefs.mac"
+%include "VBox/sup.mac"
+
+; This should go into asmdefs.mac
+%ifdef PIC
+ %ifdef ASM_FORMAT_ELF
+ %define RT_ASM_USE_GOT
+ %define RT_ASM_USE_PLT
+ %endif
+%endif
+
+
+;*******************************************************************************
+;* Structures and Typedefs *
+;*******************************************************************************
+struc SUPREQHDR
+ .u32Cookie resd 1
+ .u32SessionCookie resd 1
+ .cbIn resd 1
+ .cbOut resd 1
+ .fFlags resd 1
+ .rc resd 1
+endstruc
+
+struc SUPTRACERUMODFIREPROBE
+ .Hdr resb SUPREQHDR_size
+ .In resb SUPDRVTRACERUSRCTX64_size
+endstruc
+
+
+extern NAME(suplibTracerFireProbe)
+
+
+BEGINCODE
+
+
+;;
+; Set up a SUPTRACERUMODFIREPROBE request package on the stack and a C helper
+; function in SUPLib.cpp to do the rest.
+;
+EXPORTEDNAME SUPTracerFireProbe
+ push xBP
+ mov xBP, xSP
+
+ ;
+ ; Allocate package and set the sizes (the helper does the rest of
+ ; the header). Setting the sizes here allows the helper to verify our
+ ; idea of the request sizes.
+ ;
+ lea xSP, [xBP - SUPTRACERUMODFIREPROBE_size - 8]
+
+ mov dword [xSP + SUPTRACERUMODFIREPROBE.Hdr + SUPREQHDR.cbIn], SUPTRACERUMODFIREPROBE_size
+ mov dword [xSP + SUPTRACERUMODFIREPROBE.Hdr + SUPREQHDR.cbOut], SUPREQHDR_size
+
+%ifdef RT_ARCH_AMD64
+ ;
+ ; Save the AMD64 context.
+ ;
+ mov [xSP + SUPTRACERUMODFIREPROBE.In + SUPDRVTRACERUSRCTX64.u.Amd64.rax], rax
+ mov [xSP + SUPTRACERUMODFIREPROBE.In + SUPDRVTRACERUSRCTX64.u.Amd64.rcx], rcx
+ mov [xSP + SUPTRACERUMODFIREPROBE.In + SUPDRVTRACERUSRCTX64.u.Amd64.rdx], rdx
+ mov [xSP + SUPTRACERUMODFIREPROBE.In + SUPDRVTRACERUSRCTX64.u.Amd64.rbx], rbx
+ mov [xSP + SUPTRACERUMODFIREPROBE.In + SUPDRVTRACERUSRCTX64.u.Amd64.rsi], rsi
+ mov [xSP + SUPTRACERUMODFIREPROBE.In + SUPDRVTRACERUSRCTX64.u.Amd64.rdi], rdi
+ mov [xSP + SUPTRACERUMODFIREPROBE.In + SUPDRVTRACERUSRCTX64.u.Amd64.r8 ], r8
+ mov [xSP + SUPTRACERUMODFIREPROBE.In + SUPDRVTRACERUSRCTX64.u.Amd64.r9 ], r9
+ mov [xSP + SUPTRACERUMODFIREPROBE.In + SUPDRVTRACERUSRCTX64.u.Amd64.r10], r10
+ mov [xSP + SUPTRACERUMODFIREPROBE.In + SUPDRVTRACERUSRCTX64.u.Amd64.r11], r11
+ mov [xSP + SUPTRACERUMODFIREPROBE.In + SUPDRVTRACERUSRCTX64.u.Amd64.r12], r12
+ mov [xSP + SUPTRACERUMODFIREPROBE.In + SUPDRVTRACERUSRCTX64.u.Amd64.r13], r13
+ mov [xSP + SUPTRACERUMODFIREPROBE.In + SUPDRVTRACERUSRCTX64.u.Amd64.r14], r14
+ mov [xSP + SUPTRACERUMODFIREPROBE.In + SUPDRVTRACERUSRCTX64.u.Amd64.r15], r15
+ pushf
+ pop xAX
+ mov [xSP + SUPTRACERUMODFIREPROBE.In + SUPDRVTRACERUSRCTX64.u.Amd64.rflags], xAX
+ mov xAX, [xBP + xCB]
+ mov [xSP + SUPTRACERUMODFIREPROBE.In + SUPDRVTRACERUSRCTX64.u.Amd64.rip], xAX
+ mov xAX, [xBP]
+ mov [xSP + SUPTRACERUMODFIREPROBE.In + SUPDRVTRACERUSRCTX64.u.Amd64.rbp], xAX
+ lea xAX, [xBP + xCB*2]
+ mov [xSP + SUPTRACERUMODFIREPROBE.In + SUPDRVTRACERUSRCTX64.u.Amd64.rsp], xAX
+ %ifdef ASM_CALL64_MSC
+ mov [xSP + SUPTRACERUMODFIREPROBE.In + SUPDRVTRACERUSRCTX64.u.Amd64.uVtgProbeLoc], rcx
+ mov [xSP + SUPTRACERUMODFIREPROBE.In + SUPDRVTRACERUSRCTX64.u.Amd64.aArgs + xCB*0], rdx
+ mov [xSP + SUPTRACERUMODFIREPROBE.In + SUPDRVTRACERUSRCTX64.u.Amd64.aArgs + xCB*1], r8
+ mov [xSP + SUPTRACERUMODFIREPROBE.In + SUPDRVTRACERUSRCTX64.u.Amd64.aArgs + xCB*2], r9
+ mov xAX, [xBP + xCB*2 + 0x20 + xCB*0]
+ mov [xSP + SUPTRACERUMODFIREPROBE.In + SUPDRVTRACERUSRCTX64.u.Amd64.aArgs + xCB*3], xAX
+ mov xAX, [xBP + xCB*2 + 0x20 + xCB*1]
+ mov [xSP + SUPTRACERUMODFIREPROBE.In + SUPDRVTRACERUSRCTX64.u.Amd64.aArgs + xCB*4], xAX
+ mov xAX, [xBP + xCB*2 + 0x20 + xCB*2]
+ mov [xSP + SUPTRACERUMODFIREPROBE.In + SUPDRVTRACERUSRCTX64.u.Amd64.aArgs + xCB*5], xAX
+ mov xAX, [xBP + xCB*2 + 0x20 + xCB*3]
+ mov [xSP + SUPTRACERUMODFIREPROBE.In + SUPDRVTRACERUSRCTX64.u.Amd64.aArgs + xCB*6], xAX
+ mov xAX, [xBP + xCB*2 + 0x20 + xCB*4]
+ mov [xSP + SUPTRACERUMODFIREPROBE.In + SUPDRVTRACERUSRCTX64.u.Amd64.aArgs + xCB*7], xAX
+ mov xAX, [xBP + xCB*2 + 0x20 + xCB*5]
+ mov [xSP + SUPTRACERUMODFIREPROBE.In + SUPDRVTRACERUSRCTX64.u.Amd64.aArgs + xCB*8], xAX
+ mov xAX, [xBP + xCB*2 + 0x20 + xCB*6]
+ mov [xSP + SUPTRACERUMODFIREPROBE.In + SUPDRVTRACERUSRCTX64.u.Amd64.aArgs + xCB*9], xAX
+ mov eax, [xCX + 4] ; VTGPROBELOC::idProbe.
+ %else
+ mov [xSP + SUPTRACERUMODFIREPROBE.In + SUPDRVTRACERUSRCTX64.u.Amd64.uVtgProbeLoc], rdi
+ mov [xSP + SUPTRACERUMODFIREPROBE.In + SUPDRVTRACERUSRCTX64.u.Amd64.aArgs + xCB*0], rsi
+ mov [xSP + SUPTRACERUMODFIREPROBE.In + SUPDRVTRACERUSRCTX64.u.Amd64.aArgs + xCB*1], rdx
+ mov [xSP + SUPTRACERUMODFIREPROBE.In + SUPDRVTRACERUSRCTX64.u.Amd64.aArgs + xCB*2], rcx
+ mov [xSP + SUPTRACERUMODFIREPROBE.In + SUPDRVTRACERUSRCTX64.u.Amd64.aArgs + xCB*3], r8
+ mov [xSP + SUPTRACERUMODFIREPROBE.In + SUPDRVTRACERUSRCTX64.u.Amd64.aArgs + xCB*4], r9
+ mov xAX, [xBP + xCB*2 + xCB*0]
+ mov [xSP + SUPTRACERUMODFIREPROBE.In + SUPDRVTRACERUSRCTX64.u.Amd64.aArgs + xCB*5], xAX
+ mov xAX, [xBP + xCB*2 + xCB*1]
+ mov [xSP + SUPTRACERUMODFIREPROBE.In + SUPDRVTRACERUSRCTX64.u.Amd64.aArgs + xCB*6], xAX
+ mov xAX, [xBP + xCB*2 + xCB*2]
+ mov [xSP + SUPTRACERUMODFIREPROBE.In + SUPDRVTRACERUSRCTX64.u.Amd64.aArgs + xCB*7], xAX
+ mov xAX, [xBP + xCB*2 + xCB*3]
+ mov [xSP + SUPTRACERUMODFIREPROBE.In + SUPDRVTRACERUSRCTX64.u.Amd64.aArgs + xCB*8], xAX
+ mov xAX, [xBP + xCB*2 + xCB*4]
+ mov [xSP + SUPTRACERUMODFIREPROBE.In + SUPDRVTRACERUSRCTX64.u.Amd64.aArgs + xCB*9], xAX
+ mov eax, [xDI + 4] ; VTGPROBELOC::idProbe.
+ %endif
+ mov [xSP + SUPTRACERUMODFIREPROBE.In + SUPDRVTRACERUSRCTX64.idProbe], eax
+ mov dword [xSP + SUPTRACERUMODFIREPROBE.In + SUPDRVTRACERUSRCTX64.cBits], 64
+
+ ;
+ ; Call the helper.
+ ;
+ %ifdef ASM_CALL64_MSC
+ mov xDX, xSP
+ sub xSP, 0x20
+ call NAME(suplibTracerFireProbe)
+ %else
+ mov xSI, xSP
+ %ifdef RT_ASM_USE_PLT
+ call NAME(suplibTracerFireProbe) wrt ..plt
+ %else
+ call NAME(suplibTracerFireProbe)
+ %endif
+ %endif
+
+%elifdef RT_ARCH_X86
+ ;
+ ; Save the X86 context.
+ ;
+ mov [xSP + SUPTRACERUMODFIREPROBE.In + SUPDRVTRACERUSRCTX32.u.X86.eax], eax
+ mov [xSP + SUPTRACERUMODFIREPROBE.In + SUPDRVTRACERUSRCTX32.u.X86.ecx], ecx
+ mov [xSP + SUPTRACERUMODFIREPROBE.In + SUPDRVTRACERUSRCTX32.u.X86.edx], edx
+ mov [xSP + SUPTRACERUMODFIREPROBE.In + SUPDRVTRACERUSRCTX32.u.X86.ebx], ebx
+ mov [xSP + SUPTRACERUMODFIREPROBE.In + SUPDRVTRACERUSRCTX32.u.X86.esi], esi
+ mov [xSP + SUPTRACERUMODFIREPROBE.In + SUPDRVTRACERUSRCTX32.u.X86.edi], edi
+ pushf
+ pop xAX
+ mov [xSP + SUPTRACERUMODFIREPROBE.In + SUPDRVTRACERUSRCTX32.u.X86.eflags], xAX
+ mov xAX, [xBP + xCB]
+ mov [xSP + SUPTRACERUMODFIREPROBE.In + SUPDRVTRACERUSRCTX32.u.X86.eip], xAX
+ mov xAX, [xBP]
+ mov [xSP + SUPTRACERUMODFIREPROBE.In + SUPDRVTRACERUSRCTX32.u.X86.ebp], xAX
+ lea xAX, [xBP + xCB*2]
+ mov [xSP + SUPTRACERUMODFIREPROBE.In + SUPDRVTRACERUSRCTX32.u.X86.esp], xAX
+
+ mov xCX, [xBP + xCB*2 + xCB*0]
+ mov [xSP + SUPTRACERUMODFIREPROBE.In + SUPDRVTRACERUSRCTX32.u.X86.uVtgProbeLoc], xCX ; keep, used below.
+
+ mov edx, 20
+.more:
+ dec edx
+ mov xAX, [xBP + xCB*2 + xCB*xDX]
+ mov [xSP + SUPTRACERUMODFIREPROBE.In + SUPDRVTRACERUSRCTX32.u.X86.aArgs + xCB*xDX], xAX
+ jnz .more
+
+ mov eax, [xCX + 4] ; VTGPROBELOC::idProbe.
+ mov [xSP + SUPTRACERUMODFIREPROBE.In + SUPDRVTRACERUSRCTX32.idProbe], eax
+ mov dword [xSP + SUPTRACERUMODFIREPROBE.In + SUPDRVTRACERUSRCTX32.cBits], 32
+
+ ;
+ ; Call the helper.
+ ;
+ mov xDX, xSP
+ push xDX
+ push xCX
+ %ifdef RT_ASM_USE_PLT
+ call NAME(suplibTracerFireProbe) wrt ..plt
+ %else
+ call NAME(suplibTracerFireProbe)
+ %endif
+%else
+ %error "Arch not supported (or correctly defined)."
+%endif
+
+
+ leave
+ ret
+ENDPROC SUPTracerFireProbe
+
diff --git a/src/VBox/HostDrivers/Support/SUPR0-asm-files.sed b/src/VBox/HostDrivers/Support/SUPR0-asm-files.sed
new file mode 100644
index 00000000..6cdccf0a
--- /dev/null
+++ b/src/VBox/HostDrivers/Support/SUPR0-asm-files.sed
@@ -0,0 +1,66 @@
+# $Id: SUPR0-asm-files.sed $
+## @file
+# IPRT - SED script for generating a list of assembly files for make inclusion.
+#
+
+#
+# Copyright (C) 2012-2023 Oracle and/or its affiliates.
+#
+# This file is part of VirtualBox base platform packages, as
+# available from https://www.virtualbox.org.
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation, in version 3 of the
+# License.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, see <https://www.gnu.org/licenses>.
+#
+# The contents of this file may alternatively be used under the terms
+# of the Common Development and Distribution License Version 1.0
+# (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+# in the VirtualBox distribution, in which case the provisions of the
+# CDDL are applicable instead of those of the GPL.
+#
+# You may elect to license modified versions of this file under the
+# terms and conditions of either the GPL or the CDDL or both.
+#
+# SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+#
+
+# Header and footer.
+1b header
+$b footer
+
+# Drop all lines from the start of the file until the SED: START marker.
+1,/SED: START/d
+
+# Drop all lines from the SED: END marker and till the end of the file.
+/SED: END/,$d
+
+# We are only interested in the SUPEXP_STK_BACK lines.
+/^ *SUPEXP_STK_BACKF*(/!d
+s/^ *SUPEXP_STK_BACKF*( *[0-9][0-9]* *, *\([^)][^)]*\)),.*$*/ \$(SUPR0_0_OUTDIR)\/StkBack_\1.asm \\/
+b end
+
+:header
+i\#
+i\# Autogenerated. DO NOT EDIT!
+i\#
+i\SUPR0_VBOX_FILES = \\
+d
+
+
+:footer
+i
+i\# The end.
+i
+d
+
+:end
diff --git a/src/VBox/HostDrivers/Support/SUPR0-asm.sed b/src/VBox/HostDrivers/Support/SUPR0-asm.sed
new file mode 100644
index 00000000..bf278c9b
--- /dev/null
+++ b/src/VBox/HostDrivers/Support/SUPR0-asm.sed
@@ -0,0 +1,62 @@
+# $Id: SUPR0-asm.sed $
+## @file
+# IPRT - SED script for generating a list of assembly files for make inclusion.
+#
+
+#
+# Copyright (C) 2012-2023 Oracle and/or its affiliates.
+#
+# This file is part of VirtualBox base platform packages, as
+# available from https://www.virtualbox.org.
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation, in version 3 of the
+# License.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, see <https://www.gnu.org/licenses>.
+#
+# The contents of this file may alternatively be used under the terms
+# of the Common Development and Distribution License Version 1.0
+# (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+# in the VirtualBox distribution, in which case the provisions of the
+# CDDL are applicable instead of those of the GPL.
+#
+# You may elect to license modified versions of this file under the
+# terms and conditions of either the GPL or the CDDL or both.
+#
+# SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+#
+
+# Header and footer.
+1b header
+$b footer
+
+# Drop all lines from the start of the file until the SED: START marker.
+1,/SED: START/d
+
+# Drop all lines from the SED: END marker and till the end of the file.
+/SED: END/,$d
+
+# We are only interested in the SUPEXP_STK_BACK lines.
+/^ *SUPEXP_STK_BACKF*(/!d
+s/^ *SUPEXP_STK_BACKF*( *\([0-9][0-9]*\) *, *\([^)][^)]*\)),.*$/\/\/ ##### BEGINFILE \"StkBack_\2.asm\"\n%include "VBox\/SUPR0StackWrapper.mac"\nSUPR0StackWrapperGeneric \2, \1\n\/\/ ##### ENDFILE/
+b end
+
+:header
+i\#
+i\# Autogenerated. DO NOT EDIT!
+i\#
+d
+
+
+:footer
+d
+
+:end
diff --git a/src/VBox/HostDrivers/Support/SUPR0-def-lx.sed b/src/VBox/HostDrivers/Support/SUPR0-def-lx.sed
new file mode 100644
index 00000000..a46751a4
--- /dev/null
+++ b/src/VBox/HostDrivers/Support/SUPR0-def-lx.sed
@@ -0,0 +1,91 @@
+# $Id: SUPR0-def-lx.sed $
+## @file
+# IPRT - SED script for generating SUPR0.def - OS/2 LX.
+#
+
+#
+# Copyright (C) 2012-2023 Oracle and/or its affiliates.
+#
+# This file is part of VirtualBox base platform packages, as
+# available from https://www.virtualbox.org.
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation, in version 3 of the
+# License.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, see <https://www.gnu.org/licenses>.
+#
+# The contents of this file may alternatively be used under the terms
+# of the Common Development and Distribution License Version 1.0
+# (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+# in the VirtualBox distribution, in which case the provisions of the
+# CDDL are applicable instead of those of the GPL.
+#
+# You may elect to license modified versions of this file under the
+# terms and conditions of either the GPL or the CDDL or both.
+#
+# SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+#
+
+# Header and footer.
+1b header
+$b footer
+
+# Drop all lines from the start of the file until the SED: START marker.
+1,/SED: START/d
+
+# Drop all lines from the SED: END marker and till the end of the file.
+/SED: END/,$d
+
+# Drop all lines not specifying an export.
+/^ SUPEXP_/!d
+
+# Extract the export name from these type of statements:
+# SUPEXP_CUSTOM( 0, g_pSUPGlobalInfoPage, &g_pSUPGlobalInfoPage), /* SED: DATA */
+# SUPEXP_STK_OKAY(0, SUPGetGIP),
+# SUPEXP_STK_BACK(22, SUPReadTscWithDelta),
+# Will be transformed to:
+# _g_pSUPGlobalInfoPage /* SED: DATA */
+# _SUPGetGIP
+# _SUPReadTscWithDelta
+s/SUPEXP_CUSTOM( *[0-9][0-9]* *, *\([^),][^),]*\), [^)]*), */_\1 /
+s/SUPEXP_STK_OKAY( *[0-9][0-9]* *, *\([^)][^)]*\)), */_\1 /
+s/SUPEXP_STK_BACKF*( *[0-9][0-9]* *, *\([^)][^)]*\)), */_\1 /
+
+# Handle trailing selection comment (/* solaris-only, os2-only */).
+/\*\/ *$/!b transform
+/only-os2/b transform
+/only-/!b transform
+d
+
+# Remove trailing comments.
+:transform
+s, */\*.*\*/ *$,,
+s, *$,,
+b end
+
+:header
+i\;
+i\; Autogenerated. DO NOT EDIT!
+i\;
+i
+i\LIBRARY VBoxDrv.sys
+i
+i\EXPORTS
+d
+
+
+:footer
+i
+i\; The end.
+i
+d
+
+:end
diff --git a/src/VBox/HostDrivers/Support/SUPR0-def-pe.sed b/src/VBox/HostDrivers/Support/SUPR0-def-pe.sed
new file mode 100644
index 00000000..64208ce2
--- /dev/null
+++ b/src/VBox/HostDrivers/Support/SUPR0-def-pe.sed
@@ -0,0 +1,92 @@
+# $Id: SUPR0-def-pe.sed $
+## @file
+# IPRT - SED script for generating SUPR0.def - Windows PE.
+#
+
+#
+# Copyright (C) 2012-2023 Oracle and/or its affiliates.
+#
+# This file is part of VirtualBox base platform packages, as
+# available from https://www.virtualbox.org.
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation, in version 3 of the
+# License.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, see <https://www.gnu.org/licenses>.
+#
+# The contents of this file may alternatively be used under the terms
+# of the Common Development and Distribution License Version 1.0
+# (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+# in the VirtualBox distribution, in which case the provisions of the
+# CDDL are applicable instead of those of the GPL.
+#
+# You may elect to license modified versions of this file under the
+# terms and conditions of either the GPL or the CDDL or both.
+#
+# SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+#
+
+# Header and footer.
+1b header
+$b footer
+
+# Drop all lines from the start of the file until the SED: START marker.
+1,/SED: START/d
+
+# Drop all lines from the SED: END marker and till the end of the file.
+/SED: END/,$d
+
+# Drop all lines not specifying an export.
+/^ SUPEXP_/!d
+
+# Extract the export name from these type of statements:
+# SUPEXP_CUSTOM( 0, g_pSUPGlobalInfoPage, &g_pSUPGlobalInfoPage), /* SED: DATA */
+# SUPEXP_STK_OKAY(0, SUPGetGIP),
+# SUPEXP_STK_BACK(22, SUPReadTscWithDelta),
+# Will be transformed to:
+# g_pSUPGlobalInfoPage /* SED: DATA */
+# SUPGetGIP
+# SUPReadTscWithDelta
+s/SUPEXP_CUSTOM( *[0-9][0-9]* *, *\([^),][^),]*\), [^)]*), */\1 /
+s/SUPEXP_STK_OKAY( *[0-9][0-9]* *, *\([^)][^)]*\)), */\1 /
+s/SUPEXP_STK_BACKF*( *[0-9][0-9]* *, *\([^)][^)]*\)), */\1 /
+
+# Handle trailing selection comment (/* solaris-only, windows-only */).
+/\*\/ *$/!b transform
+/only-windows/b transform
+/only-/!b transform
+d
+
+# Deal with special /* SED: DATA */ comment.
+:transform
+s,/\* SED: \([A-Z]*\) \*/, \1,
+s, */\*.*\*/ *$,,
+s, *$,,
+b end
+
+:header
+i\;
+i\; Autogenerated. DO NOT EDIT!
+i\;
+i
+i\LIBRARY VBoxSup.sys
+i
+i\EXPORTS
+d
+
+
+:footer
+i
+i\; The end.
+i
+d
+
+:end
diff --git a/src/VBox/HostDrivers/Support/SUPR0IdcClient.c b/src/VBox/HostDrivers/Support/SUPR0IdcClient.c
new file mode 100644
index 00000000..7f93c473
--- /dev/null
+++ b/src/VBox/HostDrivers/Support/SUPR0IdcClient.c
@@ -0,0 +1,222 @@
+/* $Id: SUPR0IdcClient.c $ */
+/** @file
+ * VirtualBox Support Driver - IDC Client Lib, Core.
+ */
+
+/*
+ * Copyright (C) 2008-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include "SUPR0IdcClientInternal.h"
+#include <iprt/errcore.h>
+#include <iprt/asm.h>
+
+
+/*********************************************************************************************************************************
+* Global Variables *
+*********************************************************************************************************************************/
+static PSUPDRVIDCHANDLE volatile g_pMainHandle = NULL;
+
+
+/**
+ * Opens the IDC interface of the support driver.
+ *
+ * This will perform basic version negotiations and fail if the
+ * 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.
+ */
+SUPR0DECL(int) SUPR0IdcOpen(PSUPDRVIDCHANDLE pHandle, uint32_t uReqVersion, uint32_t uMinVersion,
+ uint32_t *puSessionVersion, uint32_t *puDriverVersion, uint32_t *puDriverRevision)
+{
+ unsigned uDefaultMinVersion;
+ SUPDRVIDCREQCONNECT Req;
+ int rc;
+
+ /*
+ * Validate and set failure return values.
+ */
+ AssertPtrReturn(pHandle, VERR_INVALID_POINTER);
+ pHandle->s.pSession = NULL;
+
+ AssertPtrNullReturn(puSessionVersion, VERR_INVALID_POINTER);
+ if (puSessionVersion)
+ *puSessionVersion = 0;
+
+ AssertPtrNullReturn(puDriverVersion, VERR_INVALID_POINTER);
+ if (puDriverVersion)
+ *puDriverVersion = 0;
+
+ AssertPtrNullReturn(puDriverRevision, VERR_INVALID_POINTER);
+ if (puDriverRevision)
+ *puDriverRevision = 0;
+
+ AssertReturn(!uMinVersion || (uMinVersion & UINT32_C(0xffff0000)) == (SUPDRV_IDC_VERSION & UINT32_C(0xffff0000)), VERR_INVALID_PARAMETER);
+ AssertReturn(!uReqVersion || (uReqVersion & UINT32_C(0xffff0000)) == (SUPDRV_IDC_VERSION & UINT32_C(0xffff0000)), VERR_INVALID_PARAMETER);
+
+ /*
+ * Handle default version input and enforce minimum requirements made
+ * by this library.
+ *
+ * The clients will pass defaults (0), and only in the case that some
+ * special API feature was just added will they set an actual version.
+ * So, this is the place where can easily enforce a minimum IDC version
+ * on bugs and similar. It corresponds a bit to what SUPR3Init is
+ * responsible for.
+ */
+ uDefaultMinVersion = SUPDRV_IDC_VERSION & UINT32_C(0xffff0000);
+ if (!uMinVersion || uMinVersion < uDefaultMinVersion)
+ uMinVersion = uDefaultMinVersion;
+ if (!uReqVersion || uReqVersion < uDefaultMinVersion)
+ uReqVersion = uDefaultMinVersion;
+
+ /*
+ * Setup the connect request packet and call the OS specific function.
+ */
+ Req.Hdr.cb = sizeof(Req);
+ Req.Hdr.rc = VERR_WRONG_ORDER;
+ Req.Hdr.pSession = NULL;
+ Req.u.In.u32MagicCookie = SUPDRVIDCREQ_CONNECT_MAGIC_COOKIE;
+ Req.u.In.uMinVersion = uMinVersion;
+ Req.u.In.uReqVersion = uReqVersion;
+ rc = supR0IdcNativeOpen(pHandle, &Req);
+ if (RT_SUCCESS(rc))
+ {
+ pHandle->s.pSession = Req.u.Out.pSession;
+ if (puSessionVersion)
+ *puSessionVersion = Req.u.Out.uSessionVersion;
+ if (puDriverVersion)
+ *puDriverVersion = Req.u.Out.uDriverVersion;
+ if (puDriverRevision)
+ *puDriverRevision = Req.u.Out.uDriverRevision;
+
+ /*
+ * We don't really trust anyone, make sure the returned
+ * session and version values actually makes sense.
+ */
+ if ( RT_VALID_PTR(Req.u.Out.pSession)
+ && Req.u.Out.uSessionVersion >= uMinVersion
+ && (Req.u.Out.uSessionVersion & UINT32_C(0xffff0000)) == (SUPDRV_IDC_VERSION & UINT32_C(0xffff0000)))
+ {
+ ASMAtomicCmpXchgPtr(&g_pMainHandle, pHandle, NULL);
+ return rc;
+ }
+
+ AssertMsgFailed(("pSession=%p uSessionVersion=0x%x (r%u)\n", Req.u.Out.pSession, Req.u.Out.uSessionVersion, Req.u.Out.uDriverRevision));
+ rc = VERR_VERSION_MISMATCH;
+ SUPR0IdcClose(pHandle);
+ }
+
+ return rc;
+}
+
+
+/**
+ * Closes a IDC connection established by SUPR0IdcOpen.
+ *
+ * @returns VBox status code.
+ * @param pHandle The IDC handle.
+ */
+SUPR0DECL(int) SUPR0IdcClose(PSUPDRVIDCHANDLE pHandle)
+{
+ SUPDRVIDCREQHDR Req;
+ int rc;
+
+ /*
+ * Catch closed handles and check that the session is valid.
+ */
+ AssertPtrReturn(pHandle, VERR_INVALID_POINTER);
+ if (!pHandle->s.pSession)
+ return VERR_INVALID_HANDLE;
+ AssertPtrReturn(pHandle->s.pSession, VERR_INVALID_HANDLE);
+
+ /*
+ * Create the request and hand it to the OS specific code.
+ */
+ Req.cb = sizeof(Req);
+ Req.rc = VERR_WRONG_ORDER;
+ Req.pSession = pHandle->s.pSession;
+ rc = supR0IdcNativeClose(pHandle, &Req);
+ if (RT_SUCCESS(rc))
+ {
+ pHandle->s.pSession = NULL;
+ ASMAtomicCmpXchgPtr(&g_pMainHandle, NULL, pHandle);
+ }
+ return rc;
+}
+
+
+/**
+ * Get the SUPDRV session for the IDC connection.
+ *
+ * This is for use with SUPDRV and component APIs that requires a valid
+ * session handle.
+ *
+ * @returns The session handle on success, NULL if the IDC handle is invalid.
+ *
+ * @param pHandle The IDC handle.
+ */
+SUPR0DECL(PSUPDRVSESSION) SUPR0IdcGetSession(PSUPDRVIDCHANDLE pHandle)
+{
+ PSUPDRVSESSION pSession;
+ AssertPtrReturn(pHandle, NULL);
+ pSession = pHandle->s.pSession;
+ AssertPtrReturn(pSession, NULL);
+ return pSession;
+}
+
+
+/**
+ * Looks up a IDC handle by session.
+ *
+ * @returns The IDC handle on success, NULL on failure.
+ * @param pSession The session to lookup.
+ *
+ * @internal
+ */
+PSUPDRVIDCHANDLE supR0IdcGetHandleFromSession(PSUPDRVSESSION pSession)
+{
+ PSUPDRVIDCHANDLE pHandle = ASMAtomicUoReadPtrT(&g_pMainHandle, PSUPDRVIDCHANDLE);
+ if ( RT_VALID_PTR(pHandle)
+ && pHandle->s.pSession == pSession)
+ return pHandle;
+ return NULL;
+}
+
diff --git a/src/VBox/HostDrivers/Support/SUPR0IdcClientComponent.c b/src/VBox/HostDrivers/Support/SUPR0IdcClientComponent.c
new file mode 100644
index 00000000..98e732c9
--- /dev/null
+++ b/src/VBox/HostDrivers/Support/SUPR0IdcClientComponent.c
@@ -0,0 +1,101 @@
+/* $Id: SUPR0IdcClientComponent.c $ */
+/** @file
+ * VirtualBox Support Driver - IDC Client Lib, Component APIs.
+ */
+
+/*
+ * Copyright (C) 2008-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include "SUPR0IdcClientInternal.h"
+#include <iprt/errcore.h>
+
+
+/**
+ * Registers a component factory with SUPDRV.
+ *
+ * @returns VBox status code.
+ * @param pHandle The IDC handle.
+ * @param pFactory The factory to register.
+ */
+SUPR0DECL(int) SUPR0IdcComponentRegisterFactory(PSUPDRVIDCHANDLE pHandle, PCSUPDRVFACTORY pFactory)
+{
+ SUPDRVIDCREQCOMPREGFACTORY Req;
+
+ /*
+ * Validate the handle before we access it.
+ */
+ AssertPtrReturn(pHandle, VERR_INVALID_HANDLE);
+ AssertPtrReturn(pHandle->s.pSession, VERR_INVALID_HANDLE);
+
+ /*
+ * Construct and fire off the request.
+ */
+ Req.Hdr.cb = sizeof(Req);
+ Req.Hdr.rc = VERR_WRONG_ORDER;
+ Req.Hdr.pSession = pHandle->s.pSession;
+ Req.u.In.pFactory = pFactory;
+
+ return supR0IdcNativeCall(pHandle, SUPDRV_IDC_REQ_COMPONENT_REGISTER_FACTORY, &Req.Hdr);
+}
+
+
+/**
+ * Deregisters a component factory with SUPDRV.
+ *
+ * @returns VBox status code.
+ * @param pHandle The IDC handle.
+ * @param pFactory The factory to register.
+ */
+SUPR0DECL(int) SUPR0IdcComponentDeregisterFactory(PSUPDRVIDCHANDLE pHandle, PCSUPDRVFACTORY pFactory)
+{
+ SUPDRVIDCREQCOMPDEREGFACTORY Req;
+
+ /*
+ * Validate the handle before we access it.
+ */
+ AssertPtrReturn(pHandle, VERR_INVALID_HANDLE);
+ AssertPtrReturn(pHandle->s.pSession, VERR_INVALID_HANDLE);
+
+ /*
+ * Construct and fire off the request.
+ */
+ Req.Hdr.cb = sizeof(Req);
+ Req.Hdr.rc = VERR_WRONG_ORDER;
+ Req.Hdr.pSession = pHandle->s.pSession;
+ Req.u.In.pFactory = pFactory;
+
+ return supR0IdcNativeCall(pHandle, SUPDRV_IDC_REQ_COMPONENT_DEREGISTER_FACTORY, &Req.Hdr);
+}
+
diff --git a/src/VBox/HostDrivers/Support/SUPR0IdcClientInternal.h b/src/VBox/HostDrivers/Support/SUPR0IdcClientInternal.h
new file mode 100644
index 00000000..578ce22e
--- /dev/null
+++ b/src/VBox/HostDrivers/Support/SUPR0IdcClientInternal.h
@@ -0,0 +1,80 @@
+/* $Id: SUPR0IdcClientInternal.h $ */
+/** @file
+ * VirtualBox Support Driver - Internal header for the IDC client library.
+ */
+
+/*
+ * Copyright (C) 2008-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+#ifndef VBOX_INCLUDED_SRC_Support_SUPR0IdcClientInternal_h
+#define VBOX_INCLUDED_SRC_Support_SUPR0IdcClientInternal_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+#include <VBox/types.h>
+#include <iprt/assert.h>
+
+#ifdef RT_OS_WINDOWS
+# include <iprt/nt/ntddk.h>
+#endif
+
+
+/**
+ * The hidden part of SUPDRVIDCHANDLE.
+ */
+struct SUPDRVIDCHANDLEPRIVATE
+{
+ /** Pointer to the session handle. */
+ PSUPDRVSESSION pSession;
+# ifdef RT_OS_WINDOWS
+ /** Pointer to the NT device object. */
+ PDEVICE_OBJECT pDeviceObject;
+ /** Pointer to the NT file object. */
+ PFILE_OBJECT pFileObject;
+# endif
+};
+/** Indicate that the structure is present. */
+#define SUPDRVIDCHANDLEPRIVATE_DECLARED 1
+
+#include <VBox/sup.h>
+#include "SUPDrvIDC.h"
+AssertCompile(RT_SIZEOFMEMB(SUPDRVIDCHANDLE, apvPadding) >= sizeof(struct SUPDRVIDCHANDLEPRIVATE));
+
+RT_C_DECLS_BEGIN
+PSUPDRVIDCHANDLE supR0IdcGetHandleFromSession(PSUPDRVSESSION pSession);
+int VBOXCALL supR0IdcNativeOpen(PSUPDRVIDCHANDLE pHandle, PSUPDRVIDCREQCONNECT pReq);
+int VBOXCALL supR0IdcNativeClose(PSUPDRVIDCHANDLE pHandle, PSUPDRVIDCREQHDR pReq);
+int VBOXCALL supR0IdcNativeCall(PSUPDRVIDCHANDLE pHandle, uint32_t iReq, PSUPDRVIDCREQHDR pReq);
+RT_C_DECLS_END
+
+#endif /* !VBOX_INCLUDED_SRC_Support_SUPR0IdcClientInternal_h */
+
diff --git a/src/VBox/HostDrivers/Support/SUPR0IdcClientStubs.c b/src/VBox/HostDrivers/Support/SUPR0IdcClientStubs.c
new file mode 100644
index 00000000..1ca6769f
--- /dev/null
+++ b/src/VBox/HostDrivers/Support/SUPR0IdcClientStubs.c
@@ -0,0 +1,153 @@
+/* $Id: SUPR0IdcClientStubs.c $ */
+/** @file
+ * VirtualBox Support Driver - IDC Client Lib, Stubs for SUPR0 APIs.
+ */
+
+/*
+ * Copyright (C) 2008-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include "SUPR0IdcClientInternal.h"
+#include <iprt/errcore.h>
+#include <iprt/asm.h>
+
+
+/**
+ * Resolves a symbol.
+ *
+ * @param pHandle The IDC handle.
+ * @param ppfn Where to return the address of the symbol.
+ * @param pszName The name of the symbol.
+ */
+static void supR0IdcGetSymbol(PSUPDRVIDCHANDLE pHandle, PFNRT *ppfn, const char *pszName)
+{
+ SUPDRVIDCREQGETSYM Req;
+ int rc;
+
+ /*
+ * Create and send a get symbol request.
+ */
+ Req.Hdr.cb = sizeof(Req);
+ Req.Hdr.rc = VERR_WRONG_ORDER;
+ Req.Hdr.pSession = pHandle->s.pSession;
+ Req.u.In.pszSymbol = pszName;
+ Req.u.In.pszModule = NULL;
+ rc = supR0IdcNativeCall(pHandle, SUPDRV_IDC_REQ_GET_SYMBOL, &Req.Hdr);
+ if (RT_SUCCESS(rc))
+ ASMAtomicWritePtr((void * volatile *)ppfn, (void *)(uintptr_t)Req.u.Out.pfnSymbol);
+}
+
+
+/**
+ * Resolves a symbol.
+ *
+ * @param pSession The IDC session.
+ * @param ppfn Where to return the address of the symbol.
+ * @param pszName The name of the symbol.
+ */
+static void supR0IdcGetSymbolBySession(PSUPDRVSESSION pSession, PFNRT *ppfn, const char *pszName)
+{
+ PSUPDRVIDCHANDLE pHandle = supR0IdcGetHandleFromSession(pSession);
+ if (pHandle)
+ supR0IdcGetSymbol(pHandle, ppfn, pszName);
+}
+
+
+SUPR0DECL(void *) SUPR0ObjRegister(PSUPDRVSESSION pSession, SUPDRVOBJTYPE enmType, PFNSUPDRVDESTRUCTOR pfnDestructor, void *pvUser1, void *pvUser2)
+{
+ static DECLCALLBACKPTR(void *, s_pfn,(PSUPDRVSESSION /* pSession */, SUPDRVOBJTYPE /* enmType */, PFNSUPDRVDESTRUCTOR /* pfnDestructor */, void * /* pvUser1 */, void * /* pvUser2 */));
+ DECLCALLBACKPTR(void *, pfn,(PSUPDRVSESSION /* pSession */, SUPDRVOBJTYPE /* enmType */, PFNSUPDRVDESTRUCTOR /* pfnDestructor */, void * /* pvUser1 */, void * /* pvUser2 */));
+ pfn = s_pfn;
+ if (!pfn)
+ {
+ supR0IdcGetSymbolBySession(pSession, (PFNRT *)&s_pfn, "SUPR0ObjRegister");
+ pfn = s_pfn;
+ if (!pfn)
+ return NULL;
+ }
+
+ return pfn(pSession, enmType, pfnDestructor, pvUser1, pvUser2);
+}
+
+
+SUPR0DECL(int) SUPR0ObjAddRef(void *pvObj, PSUPDRVSESSION pSession)
+{
+ static DECLCALLBACKPTR(int, s_pfn,(void * /* pvObj */, PSUPDRVSESSION /* pSession */));
+ DECLCALLBACKPTR(int, pfn,(void * /* pvObj */, PSUPDRVSESSION /* pSession */));
+ pfn = s_pfn;
+ if (!pfn)
+ {
+ supR0IdcGetSymbolBySession(pSession, (PFNRT *)&s_pfn, "SUPR0ObjAddRef");
+ pfn = s_pfn;
+ if (!pfn)
+ return VERR_NOT_SUPPORTED;
+ }
+
+ return pfn(pvObj, pSession);
+}
+
+
+SUPR0DECL(int) SUPR0ObjRelease(void *pvObj, PSUPDRVSESSION pSession)
+{
+ static DECLCALLBACKPTR(int, s_pfn,(void * /* pvObj */, PSUPDRVSESSION /* pSession */));
+ DECLCALLBACKPTR(int, pfn,(void * /* pvObj */, PSUPDRVSESSION /* pSession */));
+ pfn = s_pfn;
+ if (!pfn)
+ {
+ supR0IdcGetSymbolBySession(pSession, (PFNRT *)&s_pfn, "SUPR0ObjRelease");
+ pfn = s_pfn;
+ if (!pfn)
+ return VERR_NOT_SUPPORTED;
+ }
+
+ return pfn(pvObj, pSession);
+}
+
+
+SUPR0DECL(int) SUPR0ObjVerifyAccess(void *pvObj, PSUPDRVSESSION pSession, const char *pszObjName)
+{
+ static DECLCALLBACKPTR(int, s_pfn,(void * /* pvObj */, PSUPDRVSESSION /* pSession */, const char * /* pszObjName */));
+ DECLCALLBACKPTR(int, pfn,(void * /* pvObj */, PSUPDRVSESSION /* pSession */, const char * /* pszObjName */));
+ pfn = s_pfn;
+ if (!pfn)
+ {
+ supR0IdcGetSymbolBySession(pSession, (PFNRT *)&s_pfn, "SUPR0ObjVerifyAccess");
+ pfn = s_pfn;
+ if (!pfn)
+ return VERR_NOT_SUPPORTED;
+ }
+
+ return pfn(pvObj, pSession, pszObjName);
+}
+
diff --git a/src/VBox/HostDrivers/Support/SUPR3HardenedIPRT.cpp b/src/VBox/HostDrivers/Support/SUPR3HardenedIPRT.cpp
new file mode 100644
index 00000000..74fca0fc
--- /dev/null
+++ b/src/VBox/HostDrivers/Support/SUPR3HardenedIPRT.cpp
@@ -0,0 +1,152 @@
+/* $Id: SUPR3HardenedIPRT.cpp $ */
+/** @file
+ * VirtualBox Support Library - Hardened Support Routines using IPRT.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#define LOG_GROUP LOG_GROUP_SUP
+#include <VBox/sup.h>
+#include <iprt/errcore.h>
+#include <VBox/log.h>
+#include <iprt/string.h>
+#include <iprt/stdarg.h>
+#include <iprt/assert.h>
+#include <iprt/path.h>
+#include <iprt/param.h>
+
+#include "SUPLibInternal.h"
+
+
+DECLHIDDEN(char *) supR3HardenedPathFilename(const char *pszPath)
+{
+ return RTPathFilename(pszPath);
+}
+
+
+DECLHIDDEN(int) supR3HardenedPathAppPrivateNoArch(char *pszPath, size_t cchPath)
+{
+ return RTPathAppPrivateNoArch(pszPath, cchPath);
+}
+
+
+DECLHIDDEN(int) supR3HardenedPathAppPrivateArch(char *pszPath, size_t cchPath)
+{
+ return RTPathAppPrivateArch(pszPath, cchPath);
+}
+
+
+DECLHIDDEN(int) supR3HardenedPathAppSharedLibs(char *pszPath, size_t cchPath)
+{
+ return RTPathSharedLibs(pszPath, cchPath);
+}
+
+
+DECLHIDDEN(int) supR3HardenedPathAppDocs(char *pszPath, size_t cchPath)
+{
+ return RTPathAppDocs(pszPath, cchPath);
+}
+
+
+DECLHIDDEN(int) supR3HardenedPathAppBin(char *pszPath, size_t cchPath)
+{
+ return RTPathExecDir(pszPath, cchPath);
+}
+
+
+DECL_NO_RETURN(DECLHIDDEN(void)) supR3HardenedFatalMsgV(const char *pszWhere, SUPINITOP enmWhat, int rc,
+ const char *pszMsgFmt, va_list va)
+{
+ va_list vaCopy;
+ va_copy(vaCopy, va);
+ AssertFatalMsgFailed(("%s (rc=%Rrc): %N", pszWhere, rc, pszMsgFmt, &vaCopy));
+ NOREF(enmWhat);
+ /* not reached */
+}
+
+
+DECL_NO_RETURN(DECLHIDDEN(void)) supR3HardenedFatalMsg(const char *pszWhere, SUPINITOP enmWhat, int rc,
+ const char *pszMsgFmt, ...)
+{
+ va_list va;
+ va_start(va, pszMsgFmt);
+ supR3HardenedFatalMsgV(pszWhere, enmWhat, rc, pszMsgFmt, va);
+ /* not reached */
+}
+
+
+DECL_NO_RETURN(DECLHIDDEN(void)) supR3HardenedFatalV(const char *pszFormat, va_list va)
+{
+ va_list vaCopy;
+ va_copy(vaCopy, va);
+ AssertFatalMsgFailed(("%N", pszFormat, &vaCopy));
+ /* not reached */
+}
+
+
+DECL_NO_RETURN(DECLHIDDEN(void)) supR3HardenedFatal(const char *pszFormat, ...)
+{
+ va_list va;
+ va_start(va, pszFormat);
+ supR3HardenedFatalV(pszFormat, va);
+ /* not reached */
+}
+
+
+DECLHIDDEN(int) supR3HardenedErrorV(int rc, bool fFatal, const char *pszFormat, va_list va)
+{
+ if (fFatal)
+ supR3HardenedFatalV(pszFormat, va);
+
+ va_list vaCopy;
+ va_copy(vaCopy, va);
+ AssertLogRelMsgFailed(("%N", pszFormat, &vaCopy)); /** @todo figure out why this ain't working, or at seems to be that way... */
+ va_end(vaCopy);
+
+ RTLogRelPrintfV(pszFormat, va);
+ return rc;
+}
+
+
+DECLHIDDEN(int) supR3HardenedError(int rc, bool fFatal, const char *pszFormat, ...)
+{
+ va_list va;
+ va_start(va, pszFormat);
+ supR3HardenedErrorV(rc, fFatal, pszFormat, va);
+ va_end(va);
+ return rc;
+}
+
diff --git a/src/VBox/HostDrivers/Support/SUPR3HardenedMain.cpp b/src/VBox/HostDrivers/Support/SUPR3HardenedMain.cpp
new file mode 100644
index 00000000..99b4278a
--- /dev/null
+++ b/src/VBox/HostDrivers/Support/SUPR3HardenedMain.cpp
@@ -0,0 +1,2687 @@
+/* $Id: SUPR3HardenedMain.cpp $ */
+/** @file
+ * VirtualBox Support Library - Hardened main().
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+/** @page pg_hardening %VirtualBox %VM Process Hardening
+ *
+ * The %VM process hardening is to prevent malicious software from using
+ * %VirtualBox as a vehicle to obtain kernel level access.
+ *
+ * The %VirtualBox %VMM requires supervisor (kernel) level access to the CPU.
+ * For both practical and historical reasons, part of the %VMM is realized in
+ * ring-3, with a rich interface to the kernel part. While the device
+ * emulations can be executed exclusively in ring-3, we have performance
+ * optimizations that loads device emulation code into ring-0 and our special
+ * raw-mode execution context (none VT-x/AMD-V mode) for handling frequent
+ * operations a lot more efficiently. These share data between all three
+ * context (ring-3, ring-0 and raw-mode). All this poses a rather broad attack
+ * surface, which the hardening protects.
+ *
+ * The hardening focuses primarily on restricting access to the support driver,
+ * VBoxDrv or vboxdrv depending on the OS, as it is ultimately the link and
+ * instigator of the communication between ring-3 and the ring-0 and raw-mode
+ * contexts. A secondary focus is to make sure malicious code cannot be loaded
+ * and executed in the %VM process. Exactly how we go about this depends a lot
+ * on the host OS.
+ *
+ * @section sec_hardening_supdrv The Support Driver Interfaces
+ *
+ * The support driver has several interfaces thru which it can be accessed:
+ * - /dev/vboxdrv (win: \\Device\\VBoxDrv) for full unrestricted access.
+ * Offers a rich I/O control interface, which needs protecting.
+ * - /dev/vboxdrvu (win: \\Device\\VBoxDrvU) for restricted access, which
+ * VBoxSVC uses to query VT-x and AMD-V capabilities. This does not
+ * require protecting, though we limit it to the vboxgroup on some
+ * systems.
+ * - \\Device\\VBoxDrvStub on Windows for protecting the second stub
+ * process and its child, the %VM process. This is an open+close
+ * interface, only available to partially verified stub processes.
+ * - \\Device\\VBoxDrvErrorInfo on Windows for obtaining detailed error
+ * information on a previous attempt to open \\Device\\VBoxDrv or
+ * \\Device\\VBoxDrvStub. Open, read and close only interface.
+ *
+ * The rest of VBox accesses the device interface thru the support library,
+ * @ref grp_sup "SUPR3" / sup.h.
+ *
+ * The support driver also exposes a set of functions and data that other VBox
+ * ring-0 modules can import from. This includes much of the IPRT we need in
+ * the ring-0 part of the %VMM and device emulations.
+ *
+ * The ring-0 part of the %VMM and device emulations are loaded via the
+ * #SUPR3LoadModule and #SUPR3LoadServiceModule support library function, which
+ * both translates to a sequence of I/O controls against /dev/vboxdrv. On
+ * Windows we use the native kernel loader to load the module, while on the
+ * other systems ring-3 prepares the bits with help from the IPRT loader code.
+ *
+ *
+ * @section sec_hardening_unix Hardening on UNIX-like OSes
+ *
+ * On UNIX-like systems (Solaris, Linux, darwin, freebsd, ...) we put our trust
+ * in root and that root knows what he/she/it is doing.
+ *
+ * We only allow root to get full unrestricted access to the support driver.
+ * The device node corresponding to unrestricted access (/dev/vboxdrv) is own by
+ * root and has a 0600 access mode (i.e. only accessible to the owner, root). In
+ * addition to this file system level restriction, the support driver also
+ * checks that the effective user ID (EUID) is root when it is being opened.
+ *
+ * The %VM processes temporarily assume root privileges using the set-uid-bit on
+ * the executable with root as owner. In fact, all the files and directories we
+ * install are owned by root and the wheel (or equivalent gid = 0) group,
+ * including extension pack files.
+ *
+ * The executable with the set-uid-to-root-bit set is a stub binary that has no
+ * unnecessary library dependencies (only libc, pthreads, dynamic linker) and
+ * simply calls #SUPR3HardenedMain. It does the following:
+ * 1. Validate the VirtualBox installation (#supR3HardenedVerifyAll):
+ * - Check that the executable file of the process is one of the known
+ * VirtualBox executables.
+ * - Check that all mandatory files are present.
+ * - Check that all installed files and directories (both optional and
+ * mandatory ones) are owned by root:wheel and are not writable by
+ * anyone except root.
+ * - Check that all the parent directories, all the way up to the root
+ * if possible, only permits root (or system admin) to change them.
+ * This is that to rule out unintentional rename races.
+ * - On some systems we may also validate the cryptographic signtures
+ * of executable images.
+ *
+ * 2. Open a file descriptor for the support device driver
+ * (#supR3HardenedMainOpenDevice).
+ *
+ * 3. Grab ICMP capabilities for NAT ping support, if required by the OS
+ * (#supR3HardenedMainGrabCapabilites).
+ *
+ * 4. Correctly drop the root privileges
+ * (#supR3HardenedMainDropPrivileges).
+ *
+ * 5. Load the VBoxRT dynamic link library and hand over the file
+ * descriptor to the support library code in it
+ * (#supR3HardenedMainInitRuntime).
+ *
+ * 6. Load the dynamic library containing the actual %VM front end code and
+ * run it (tail of #SUPR3HardenedMain).
+ *
+ * The set-uid-to-root stub executable is paired with a dynamic link library
+ * which export one TrustedMain entry point (see #FNSUPTRUSTEDMAIN) that we
+ * call. In case of error reporting, the library may also export a TrustedError
+ * function (#FNSUPTRUSTEDERROR).
+ *
+ * That the set-uid-to-root-bit modifies the dynamic linker behavior on all
+ * systems, even after we've dropped back to the real user ID, is something we
+ * take advantage of. The dynamic linkers takes special care to prevent users
+ * from using clever tricks to inject their own code into set-uid processes and
+ * causing privilege escalation issues. This is the exact help we need.
+ *
+ * The VirtualBox installation location is hardcoded, which means the any
+ * dynamic linker paths embedded or inferred from the executable and dynamic
+ * libraries are also hardcoded. This helps eliminating search path attack
+ * vectors at the cost of being inflexible regarding installation location.
+ *
+ * In addition to what the dynamic linker does for us, the VirtualBox code will
+ * not directly be calling either RTLdrLoad or dlopen to load dynamic link
+ * libraries into the process. Instead it will call #SUPR3HardenedLdrLoad,
+ * #SUPR3HardenedLdrLoadAppPriv and #SUPR3HardenedLdrLoadPlugIn to do the
+ * loading. These functions will perform the same validations on the file being
+ * loaded as #SUPR3HardenedMain did in its validation step. So, anything we
+ * load must be installed with root/wheel as owner/group, the directory we load
+ * it from must also be owned by root:wheel and now allow for renaming the file.
+ * Similar ownership restrictions applies to all the parent directories (except
+ * on darwin).
+ *
+ * So, we place the responsibility of not installing malicious software on the
+ * root user on UNIX-like systems. Which is fair enough, in our opinion.
+ *
+ *
+ * @section sec_hardening_win Hardening on Windows
+ *
+ * On Windows we cannot put the same level or trust in the Administrator user(s)
+ * (equivalent of root/wheel on unix) as on the UNIX-like systems, which
+ * complicates things greatly.
+ *
+ * Some of the blame for this can be given to Windows being a descendant /
+ * replacement for a set of single user systems: DOS, Windows 1.0-3.11 Windows
+ * 95-ME, and OS/2. Users of NT 3.1 and later was inclined to want to always
+ * run it with full root/administrator privileges like they had done on the
+ * predecessors, while Microsoft didn't provide much incentive for more secure
+ * alternatives. Bad idea, security wise, but execellent for the security
+ * software industry. For this reason using a set-uid-to-root approach is
+ * pointless, even if Windows had one.
+ *
+ * So, in order to protect access to the support driver and protect the %VM
+ * process while it's running we have to do a lot more work. A keystone in the
+ * defences is cryptographic code signing. Here's the short version of what we
+ * do:
+ * - Minimal stub executable, signed with the same certificate as the
+ * kernel driver.
+ *
+ * - The stub executable respawns itself twice, hooking the NTDLL init
+ * routine to perform protection tasks as early as possible. The parent
+ * stub helps keep in the child clean for verification as does the
+ * support driver.
+ *
+ * - In order to protect against loading unwanted code into the process,
+ * the stub processes installs DLL load hooks with NTDLL as well as
+ * directly intercepting the LdrLoadDll and NtCreateSection APIs.
+ *
+ * - The support driver will verify all but the initial process very
+ * thoroughly before allowing them protection and in the final case full
+ * unrestricted access.
+ *
+ *
+ * @subsection sec_hardening_win_protsoft 3rd Party "Protection" Software
+ *
+ * What makes our life REALLY difficult on Windows is this 3rd party "security"
+ * software which is more or less required to keep a Windows system safe for
+ * normal users and all corporate IT departments rightly insists on installing.
+ * After the kernel patching clampdown in Vista, anti-* software has to do a
+ * lot more mucking about in user mode to get their job (kind of) done. So, it
+ * is common practice to patch a lot of NTDLL, KERNEL32, the executable import
+ * table, load extra DLLs into the process, allocate executable memory in the
+ * process (classic code injection) and more.
+ *
+ * The BIG problem with all this is that it is indistinguishable from what
+ * malicious software would be doing in order to intercept process activity
+ * (network sniffing, maybe password snooping) or gain a level of kernel access
+ * via the support driver. So, the "protection" software is what is currently
+ * forcing us to do the pre-NTDLL initialization.
+ *
+ *
+ * @subsection sec_hardening_win_1st_stub The Initial Stub Process
+ *
+ * We share the stub executable approach with the UNIX-like systems, so there's
+ * the #SUPR3HardenedMain calling stub executable with its partner DLL exporting
+ * TrustedMain and TrustedError. However, the stub executable does a lot more,
+ * while doing it in a more bare metal fashion:
+ * - It does not use the Microsoft CRT, what we need of CRT functions comes
+ * from IPRT.
+ * - It does not statically import anything. This is to avoid having an
+ * import table that can be patched to intercept our calls or extended to
+ * load additional DLLs.
+ * - Direct NT system calls. System calls normally going thru NTDLL, but
+ * since there is so much software out there which wants to patch known
+ * NTDLL entry points to control our software (either for good or
+ * malicious reasons), we do it ourselves.
+ *
+ * The initial stub process is not really to be trusted, though we try our best
+ * to limit potential harm (user mode debugger checks, disable thread creation).
+ * So, when it enters #SUPR3HardenedMain we only call #supR3HardenedVerifyAll to
+ * verify the installation (known executables and DLLs, checking their code
+ * signing signatures, keeping them all open to deny deletion and replacing) and
+ * does a respawn via #supR3HardenedWinReSpawn.
+ *
+ *
+ * @subsection sec_hardening_win_2nd_stub The Second Stub Process
+ *
+ * The second stub process will be created in suspended state, i.e. the main
+ * thread is suspended before it executes a single instruction. It is also
+ * created with a less generous ACLs, though this doesn't protect us from admin
+ * users. In order for #SUPR3HardenedMain to figure that it is the second stub
+ * process, the zeroth command line argument has been replaced by a known magic
+ * string (UUID).
+ *
+ * Now, before the process starts executing, the parent (initial stub) will
+ * patch the LdrInitializeThunk entry point in NTDLL to call
+ * #supR3HardenedEarlyProcessInit via #supR3HardenedEarlyProcessInitThunk. The
+ * parent will also plant some synchronization stuff via #g_ProcParams (NTDLL
+ * location, inherited event handles and associated ping-pong equipment).
+ *
+ * The LdrInitializeThunk entry point of NTDLL is where the kernel sets up
+ * process execution to start executing (via a user alert, so it is not subject
+ * to SetThreadContext). LdrInitializeThunk performs process, NTDLL and
+ * sub-system client (kernel32) initialization. A lot of "protection" software
+ * uses triggers in this initialization sequence (like the KERNEL32.DLL load
+ * event), so we avoid quite a bit of problems by getting our stuff done early
+ * on.
+ *
+ * However, there are also those that uses events that triggers immediately when
+ * the process is created or/and starts executing the first instruction. But we
+ * can easily counter these as we have a known process state we can restore. So,
+ * the first thing that #supR3HardenedEarlyProcessInit does is to signal the
+ * parent to perform a child purification, so the potentially evil influences
+ * can be exorcised.
+ *
+ * What the parent does during the purification is very similar to what the
+ * kernel driver will do later on when verifying the second stub and the %VM
+ * processes, except that instead of failing when encountering an shortcoming it
+ * will take corrective actions:
+ * - Executable memory regions not belonging to a DLL mapping will be
+ * attempted freed, and we'll only fail if we can't evict them.
+ * - All pages in the executable images in the process (should be just the
+ * stub executable and NTDLL) will be compared to the pristine fixed-up
+ * copy prepared by the IPRT PE loader code, restoring any bytes which
+ * appears differently in the child. (#g_ProcParams is exempted,
+ * LdrInitializeThunk is set to call NtTerminateThread.)
+ * - Unwanted DLLs will be unloaded (we have a set of DLLs we like).
+ *
+ * Before signalling the second stub process that it has been purified and should
+ * get on with it, the parent will close all handles with unrestricted access to
+ * the process and thread so that the initial stub process no longer can
+ * influence the child in any really harmful way. (The caller of CreateProcess
+ * usually receives handles with unrestricted access to the child process and
+ * its main thread. These could in theory be used with DuplicateHandle or
+ * WriteProcessMemory to get at the %VM process if we're not careful.)
+ *
+ * #supR3HardenedEarlyProcessInit will continue with opening the log file
+ * (requires command line parsing). It will continue to initialize a bunch of
+ * global variables, system calls and trustworthy/harmless NTDLL imports.
+ * #supR3HardenedWinInit is then called to setup image verification, that is:
+ * - Hook the NtCreateSection entry point in NTDLL so we can check all
+ * executable mappings before they're created and can be mapped. The
+ * NtCreateSection code jumps to #supR3HardenedMonitor_NtCreateSection.
+ * - Hook (ditto) the LdrLoadDll entry point in NTDLL so we can
+ * pre-validate all images that gets loaded the normal way (partly
+ * because the NtCreateSection context is restrictive because the NTDLL
+ * loader lock is usually held, which prevents us from safely calling
+ * WinVerityTrust). The LdrLoadDll code jumps to
+ * #supR3HardenedMonitor_LdrLoadDll.
+ *
+ * The image/DLL verification hooks are at this point able to verify DLLs
+ * containing embedded code signing signatures, and will restrict the locations
+ * from which DLLs will be loaded. When #SUPR3HardenedMain gets going later on,
+ * they will start insisting on everything having valid signatures, either
+ * embedded or in a signed installer catalog file.
+ *
+ * The function also irrevocably disables debug notifications related to the
+ * current thread, just to make attaching a debugging that much more difficult
+ * and less useful.
+ *
+ * Now, the second stub process will open the so called stub device
+ * (\\Device\\VBoxDrvStub), that is a special support driver device node that
+ * tells the support driver to:
+ * - Protect the process against the OpenProcess and OpenThread attack
+ * vectors by stripping risky access rights.
+ * - Check that the process isn't being debugged.
+ * - Check that the process contains exactly one thread.
+ * - Check that the process doesn't have any unknown DLLs loaded into it.
+ * - Check that the process doesn't have any executable memory (other than
+ * DLL sections) in it.
+ * - Check that the process executable is a known VBox executable which may
+ * access the support driver.
+ * - Check that the process executable is signed with the same code signing
+ * certificate as the driver and that the on disk image is valid
+ * according to its embedded signature.
+ * - Check all the signature of all DLLs in the process (NTDLL) if they are
+ * signed, and only accept unsigned ones in versions where they are known
+ * not to be signed.
+ * - Check that the code and readonly parts of the executable and DLLs
+ * mapped into the process matches the on disk content (no patches other
+ * than our own two in NTDLL are allowed).
+ *
+ * Once granted access to the stub device, #supR3HardenedEarlyProcessInit will
+ * restore the LdrInitializeThunk code and let the process perform normal
+ * initialization. Leading us to #SUPR3HardenedMain where we detect that this
+ * is the 2nd stub process and does another respawn.
+ *
+ *
+ * @subsection sec_hardening_win_3rd_stub The Final Stub / VM Process
+ *
+ * The third stub process is what becomes the %VM process. Because the parent
+ * has opened \\Device\\VBoxDrvSub, it is protected from malicious OpenProcess &
+ * OpenThread calls from the moment of inception, practically speaking.
+ *
+ * It goes thru the same suspended creation, patching, purification and such as
+ * its parent (the second stub process). However, instead of opening
+ * \\Device\\VBoxDrvStub from #supR3HardenedEarlyProcessInit, it opens the
+ * support driver for full unrestricted access, i.e. \\Device\\VBoxDrv.
+ *
+ * The support driver will perform the same checks as it did when
+ * \\Device\\VBoxDrvStub was opened, but in addition it will:
+ * - Check that the process is the first child of a process that opened
+ * \\Device\\VBoxDrvStub.
+ * - Check that the parent process is still alive.
+ * - Scan all open handles in the system for potentially harmful ones to
+ * the process or the primary thread.
+ *
+ * Knowing that the process is genuinly signed with the same certificate as the
+ * kernel driver, and the exectuable code in the process is either shipped by us
+ * or Microsoft, the support driver will trust it with full access and to keep
+ * the handle secure.
+ *
+ * We also trust the protection the support driver gives the process to keep out
+ * malicious ring-3 code, and therefore any code, patching or other mysterious
+ * stuff that enteres the process must be from kernel mode and that we can trust
+ * it (the alternative interpretation is that the kernel has been breanched
+ * already, which isn't our responsibility). This means that, the anti-software
+ * products can do whatever they like from this point on. However, should they
+ * do unrevertable changes to the process before this point, VirtualBox won't
+ * work.
+ *
+ * As in the second stub process, we'll now do normal process initialization and
+ * #SUPR3HardenedMain will take control. It will detect that it is being called
+ * by the 3rd stub process because of a different magic string starting the
+ * command line, and not respawn itself any more. #SUPR3HardenedMain will
+ * recheck the VirtualBox installation, keeping all known files open just like
+ * in two previous stub processes.
+ *
+ * It will then load the Windows cryptographic API and load the trusted root
+ * certificates from the Windows store. The API enables using installation
+ * catalog files for signature checking as well as providing a second
+ * verification in addition to our own implementation (IPRT). The certificates
+ * allows our signature validation implementation to validate all embedded
+ * signatures, not just the microsoft ones and the one signed by our own
+ * certificate.
+ *
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#if defined(RT_OS_OS2)
+# define INCL_BASE
+# define INCL_ERRORS
+# include <os2.h>
+# include <stdio.h>
+# include <stdlib.h>
+# include <dlfcn.h>
+# include <unistd.h>
+
+#elif RT_OS_WINDOWS
+# include <iprt/nt/nt-and-windows.h>
+
+#else /* UNIXes */
+# ifdef RT_OS_DARWIN
+# define _POSIX_C_SOURCE 1 /* pick the correct prototype for unsetenv. */
+# endif
+# include <iprt/types.h> /* stdint fun on darwin. */
+
+# include <stdio.h>
+# include <stdlib.h>
+# include <dlfcn.h>
+# include <limits.h>
+# include <errno.h>
+# include <unistd.h>
+# include <sys/stat.h>
+# include <sys/time.h>
+# include <sys/types.h>
+# if defined(RT_OS_LINUX)
+# undef USE_LIB_PCAP /* don't depend on libcap as we had to depend on either
+ libcap1 or libcap2 */
+
+# undef _POSIX_SOURCE
+# include <linux/types.h> /* sys/capabilities from uek-headers require this */
+# include <sys/capability.h>
+# include <sys/prctl.h>
+# ifndef CAP_TO_MASK
+# define CAP_TO_MASK(cap) RT_BIT(cap)
+# endif
+# elif defined(RT_OS_FREEBSD)
+# include <sys/param.h>
+# include <sys/sysctl.h>
+# elif defined(RT_OS_SOLARIS)
+# include <priv.h>
+# endif
+# include <pwd.h>
+# ifdef RT_OS_DARWIN
+# include <mach-o/dyld.h>
+# endif
+
+#endif
+
+#include <VBox/sup.h>
+#include <VBox/err.h>
+#ifdef RT_OS_WINDOWS
+# include <VBox/version.h>
+# include <iprt/utf16.h>
+#endif
+#include <iprt/ctype.h>
+#include <iprt/string.h>
+#include <iprt/initterm.h>
+#include <iprt/param.h>
+#include <iprt/path.h>
+
+#include "SUPLibInternal.h"
+
+
+/*********************************************************************************************************************************
+* Defined Constants And Macros *
+*********************************************************************************************************************************/
+/* This mess is temporary after eliminating a define duplicated in SUPLibInternal.h. */
+#if !defined(RT_OS_OS2) && !defined(RT_OS_WINDOWS) && !defined(RT_OS_L4)
+# ifndef SUP_HARDENED_SUID
+# error "SUP_HARDENED_SUID is NOT defined?!?"
+# endif
+#else
+# ifdef SUP_HARDENED_SUID
+# error "SUP_HARDENED_SUID is defined?!?"
+# endif
+#endif
+
+/** @def SUP_HARDENED_SYM
+ * Decorate a symbol that's resolved dynamically.
+ */
+#ifdef RT_OS_OS2
+# define SUP_HARDENED_SYM(sym) "_" sym
+#else
+# define SUP_HARDENED_SYM(sym) sym
+#endif
+
+
+/*********************************************************************************************************************************
+* Structures and Typedefs *
+*********************************************************************************************************************************/
+/** @see RTR3InitEx */
+typedef DECLCALLBACKTYPE(int, FNRTR3INITEX,(uint32_t iVersion, uint32_t fFlags, int cArgs,
+ char **papszArgs, const char *pszProgramPath));
+typedef FNRTR3INITEX *PFNRTR3INITEX;
+
+/** @see RTLogRelPrintf */
+typedef DECLCALLBACKTYPE(void, FNRTLOGRELPRINTF,(const char *pszFormat, ...));
+typedef FNRTLOGRELPRINTF *PFNRTLOGRELPRINTF;
+
+
+/**
+ * Descriptor of an environment variable to purge.
+ */
+typedef struct SUPENVPURGEDESC
+{
+ /** Name of the environment variable to purge. */
+ const char *pszEnv;
+ /** The length of the variable name. */
+ uint8_t cchEnv;
+ /** Flag whether a failure in purging the variable leads to
+ * a fatal error resulting in an process exit. */
+ bool fPurgeErrFatal;
+} SUPENVPURGEDESC;
+/** Pointer to a environment variable purge descriptor. */
+typedef SUPENVPURGEDESC *PSUPENVPURGEDESC;
+/** Pointer to a const environment variable purge descriptor. */
+typedef const SUPENVPURGEDESC *PCSUPENVPURGEDESC;
+
+/**
+ * Descriptor of an command line argument to purge.
+ */
+typedef struct SUPARGPURGEDESC
+{
+ /** Name of the argument to purge. */
+ const char *pszArg;
+ /** The length of the argument name. */
+ uint8_t cchArg;
+ /** Flag whether the argument is followed by an extra argument
+ * which must be purged too */
+ bool fTakesValue;
+} SUPARGPURGEDESC;
+/** Pointer to a environment variable purge descriptor. */
+typedef SUPARGPURGEDESC *PSUPARGPURGEDESC;
+/** Pointer to a const environment variable purge descriptor. */
+typedef const SUPARGPURGEDESC *PCSUPARGPURGEDESC;
+
+
+/*********************************************************************************************************************************
+* Global Variables *
+*********************************************************************************************************************************/
+/** The pre-init data we pass on to SUPR3 (residing in VBoxRT). */
+static SUPPREINITDATA g_SupPreInitData;
+/** The program executable path. */
+#ifndef RT_OS_WINDOWS
+static
+#endif
+char g_szSupLibHardenedExePath[RTPATH_MAX];
+/** The application bin directory path. */
+static char g_szSupLibHardenedAppBinPath[RTPATH_MAX];
+/** The offset into g_szSupLibHardenedExePath of the executable name. */
+static size_t g_offSupLibHardenedExecName;
+/** The length of the executable name in g_szSupLibHardenedExePath. */
+static size_t g_cchSupLibHardenedExecName;
+
+/** The program name. */
+static const char *g_pszSupLibHardenedProgName;
+/** The flags passed to SUPR3HardenedMain - SUPSECMAIN_FLAGS_XXX. */
+static uint32_t g_fSupHardenedMain;
+
+#ifdef SUP_HARDENED_SUID
+/** The real UID at startup. */
+static uid_t g_uid;
+/** The real GID at startup. */
+static gid_t g_gid;
+# ifdef RT_OS_LINUX
+static uint32_t g_uCaps;
+static uint32_t g_uCapsVersion;
+# endif
+#endif
+
+/** The startup log file. */
+#ifdef RT_OS_WINDOWS
+static HANDLE g_hStartupLog = NULL;
+#else
+static int g_hStartupLog = -1;
+#endif
+/** The number of bytes we've written to the startup log. */
+static uint32_t volatile g_cbStartupLog = 0;
+
+/** The current SUPR3HardenedMain state / location. */
+SUPR3HARDENEDMAINSTATE g_enmSupR3HardenedMainState = SUPR3HARDENEDMAINSTATE_NOT_YET_CALLED;
+AssertCompileSize(g_enmSupR3HardenedMainState, sizeof(uint32_t));
+
+#ifdef RT_OS_WINDOWS
+/** Pointer to VBoxRT's RTLogRelPrintf function so we can write errors to the
+ * release log at runtime. */
+static PFNRTLOGRELPRINTF g_pfnRTLogRelPrintf = NULL;
+/** Log volume name (for attempting volume flush). */
+static RTUTF16 g_wszStartupLogVol[16];
+#endif
+
+/** Environment variables to purge from the process because
+ * they are known to be harmful. */
+static const SUPENVPURGEDESC g_aSupEnvPurgeDescs[] =
+{
+ /* pszEnv fPurgeErrFatal */
+ /* Qt related environment variables: */
+ { RT_STR_TUPLE("QT_QPA_PLATFORM_PLUGIN_PATH"), true },
+ { RT_STR_TUPLE("QT_PLUGIN_PATH"), true },
+ /* ALSA related environment variables: */
+ { RT_STR_TUPLE("ALSA_MIXER_SIMPLE_MODULES"), true },
+ { RT_STR_TUPLE("LADSPA_PATH"), true },
+};
+
+/** Arguments to purge from the argument vector because
+ * they are known to be harmful. */
+static const SUPARGPURGEDESC g_aSupArgPurgeDescs[] =
+{
+ /* pszArg fTakesValue */
+ /* Qt related environment variables: */
+ { RT_STR_TUPLE("-platformpluginpath"), true },
+};
+
+
+/*********************************************************************************************************************************
+* Internal Functions *
+*********************************************************************************************************************************/
+#ifdef SUP_HARDENED_SUID
+static void supR3HardenedMainDropPrivileges(void);
+#endif
+static PFNSUPTRUSTEDERROR supR3HardenedMainGetTrustedError(const char *pszProgName);
+
+
+/**
+ * Safely copy one or more strings into the given buffer.
+ *
+ * @returns VINF_SUCCESS or VERR_BUFFER_OVERFLOW.
+ * @param pszDst The destionation buffer.
+ * @param cbDst The size of the destination buffer.
+ * @param ... One or more zero terminated strings, ending with
+ * a NULL.
+ */
+static int suplibHardenedStrCopyEx(char *pszDst, size_t cbDst, ...)
+{
+ int rc = VINF_SUCCESS;
+
+ if (cbDst == 0)
+ return VERR_BUFFER_OVERFLOW;
+
+ va_list va;
+ va_start(va, cbDst);
+ for (;;)
+ {
+ const char *pszSrc = va_arg(va, const char *);
+ if (!pszSrc)
+ break;
+
+ size_t cchSrc = suplibHardenedStrLen(pszSrc);
+ if (cchSrc < cbDst)
+ {
+ suplibHardenedMemCopy(pszDst, pszSrc, cchSrc);
+ pszDst += cchSrc;
+ cbDst -= cchSrc;
+ }
+ else
+ {
+ rc = VERR_BUFFER_OVERFLOW;
+ if (cbDst > 1)
+ {
+ suplibHardenedMemCopy(pszDst, pszSrc, cbDst - 1);
+ pszDst += cbDst - 1;
+ cbDst = 1;
+ }
+ }
+ *pszDst = '\0';
+ }
+ va_end(va);
+
+ return rc;
+}
+
+
+/**
+ * Exit current process in the quickest possible fashion.
+ *
+ * @param rcExit The exit code.
+ */
+DECLHIDDEN(DECL_NO_RETURN(void)) suplibHardenedExit(RTEXITCODE rcExit)
+{
+ for (;;)
+ {
+#ifdef RT_OS_WINDOWS
+ if (g_enmSupR3HardenedMainState >= SUPR3HARDENEDMAINSTATE_WIN_IMPORTS_RESOLVED)
+ ExitProcess(rcExit);
+ if (RtlExitUserProcess != NULL)
+ RtlExitUserProcess(rcExit);
+ NtTerminateProcess(NtCurrentProcess(), rcExit);
+#else
+ _Exit(rcExit);
+#endif
+ }
+}
+
+
+/**
+ * Writes a substring to standard error.
+ *
+ * @param pch The start of the substring.
+ * @param cch The length of the substring.
+ */
+static void suplibHardenedPrintStrN(const char *pch, size_t cch)
+{
+#ifdef RT_OS_WINDOWS
+ HANDLE hStdOut = NtCurrentPeb()->ProcessParameters->StandardOutput;
+ if (hStdOut != NULL)
+ {
+ if (g_enmSupR3HardenedMainState >= SUPR3HARDENEDMAINSTATE_WIN_IMPORTS_RESOLVED)
+ {
+ DWORD cbWritten;
+ WriteFile(hStdOut, pch, (DWORD)cch, &cbWritten, NULL);
+ }
+ /* Windows 7 and earlier uses fake handles, with the last two bits set ((hStdOut & 3) == 3). */
+ else if (NtWriteFile != NULL && ((uintptr_t)hStdOut & 3) == 0)
+ {
+ IO_STATUS_BLOCK Ios = RTNT_IO_STATUS_BLOCK_INITIALIZER;
+ NtWriteFile(hStdOut, NULL /*Event*/, NULL /*ApcRoutine*/, NULL /*ApcContext*/,
+ &Ios, (PVOID)pch, (ULONG)cch, NULL /*ByteOffset*/, NULL /*Key*/);
+ }
+ }
+#else
+ int res = write(2, pch, cch);
+ NOREF(res);
+#endif
+}
+
+
+/**
+ * Writes a string to standard error.
+ *
+ * @param psz The string.
+ */
+static void suplibHardenedPrintStr(const char *psz)
+{
+ suplibHardenedPrintStrN(psz, suplibHardenedStrLen(psz));
+}
+
+
+/**
+ * Writes a char to standard error.
+ *
+ * @param ch The character value to write.
+ */
+static void suplibHardenedPrintChr(char ch)
+{
+ suplibHardenedPrintStrN(&ch, 1);
+}
+
+#ifndef IPRT_NO_CRT
+
+/**
+ * Writes a decimal number to stdard error.
+ *
+ * @param uValue The value.
+ */
+static void suplibHardenedPrintDecimal(uint64_t uValue)
+{
+ char szBuf[64];
+ char *pszEnd = &szBuf[sizeof(szBuf) - 1];
+ char *psz = pszEnd;
+
+ *psz-- = '\0';
+
+ do
+ {
+ *psz-- = '0' + (uValue % 10);
+ uValue /= 10;
+ } while (uValue > 0);
+
+ psz++;
+ suplibHardenedPrintStrN(psz, pszEnd - psz);
+}
+
+
+/**
+ * Writes a hexadecimal or octal number to standard error.
+ *
+ * @param uValue The value.
+ * @param uBase The base (16 or 8).
+ * @param fFlags Format flags.
+ */
+static void suplibHardenedPrintHexOctal(uint64_t uValue, unsigned uBase, uint32_t fFlags)
+{
+ static char const s_achDigitsLower[17] = "0123456789abcdef";
+ static char const s_achDigitsUpper[17] = "0123456789ABCDEF";
+ const char *pchDigits = !(fFlags & RTSTR_F_CAPITAL) ? s_achDigitsLower : s_achDigitsUpper;
+ unsigned cShift = uBase == 16 ? 4 : 3;
+ unsigned fDigitMask = uBase == 16 ? 0xf : 7;
+ char szBuf[64];
+ char *pszEnd = &szBuf[sizeof(szBuf) - 1];
+ char *psz = pszEnd;
+
+ *psz-- = '\0';
+
+ do
+ {
+ *psz-- = pchDigits[uValue & fDigitMask];
+ uValue >>= cShift;
+ } while (uValue > 0);
+
+ if ((fFlags & RTSTR_F_SPECIAL) && uBase == 16)
+ {
+ *psz-- = !(fFlags & RTSTR_F_CAPITAL) ? 'x' : 'X';
+ *psz-- = '0';
+ }
+
+ psz++;
+ suplibHardenedPrintStrN(psz, pszEnd - psz);
+}
+
+
+/**
+ * Writes a wide character string to standard error.
+ *
+ * @param pwsz The string.
+ */
+static void suplibHardenedPrintWideStr(PCRTUTF16 pwsz)
+{
+ for (;;)
+ {
+ RTUTF16 wc = *pwsz++;
+ if (!wc)
+ return;
+ if ( (wc < 0x7f && wc >= 0x20)
+ || wc == '\n'
+ || wc == '\r')
+ suplibHardenedPrintChr((char)wc);
+ else
+ {
+ suplibHardenedPrintStrN(RT_STR_TUPLE("\\x"));
+ suplibHardenedPrintHexOctal(wc, 16, 0);
+ }
+ }
+}
+
+#else /* IPRT_NO_CRT */
+
+/** Buffer structure used by suplibHardenedOutput. */
+struct SUPLIBHARDENEDOUTPUTBUF
+{
+ size_t off;
+ char szBuf[2048];
+};
+
+/** Callback for RTStrFormatV, see FNRTSTROUTPUT. */
+static DECLCALLBACK(size_t) suplibHardenedOutput(void *pvArg, const char *pachChars, size_t cbChars)
+{
+ SUPLIBHARDENEDOUTPUTBUF *pBuf = (SUPLIBHARDENEDOUTPUTBUF *)pvArg;
+ size_t cbTodo = cbChars;
+ for (;;)
+ {
+ size_t cbSpace = sizeof(pBuf->szBuf) - pBuf->off - 1;
+
+ /* Flush the buffer? */
+ if ( cbSpace == 0
+ || (cbTodo == 0 && pBuf->off))
+ {
+ suplibHardenedPrintStrN(pBuf->szBuf, pBuf->off);
+# ifdef RT_OS_WINDOWS
+ if (g_enmSupR3HardenedMainState >= SUPR3HARDENEDMAINSTATE_WIN_IMPORTS_RESOLVED)
+ OutputDebugString(pBuf->szBuf);
+# endif
+ pBuf->off = 0;
+ cbSpace = sizeof(pBuf->szBuf) - 1;
+ }
+
+ /* Copy the string into the buffer. */
+ if (cbTodo == 1)
+ {
+ pBuf->szBuf[pBuf->off++] = *pachChars;
+ break;
+ }
+ if (cbSpace >= cbTodo)
+ {
+ memcpy(&pBuf->szBuf[pBuf->off], pachChars, cbTodo);
+ pBuf->off += cbTodo;
+ break;
+ }
+ memcpy(&pBuf->szBuf[pBuf->off], pachChars, cbSpace);
+ pBuf->off += cbSpace;
+ cbTodo -= cbSpace;
+ }
+ pBuf->szBuf[pBuf->off] = '\0';
+
+ return cbChars;
+}
+
+#endif /* IPRT_NO_CRT */
+
+/**
+ * Simple printf to standard error.
+ *
+ * @param pszFormat The format string.
+ * @param va Arguments to format.
+ */
+DECLHIDDEN(void) suplibHardenedPrintFV(const char *pszFormat, va_list va)
+{
+#ifdef IPRT_NO_CRT
+ /*
+ * Use buffered output here to avoid character mixing on the windows
+ * console and to enable us to use OutputDebugString.
+ */
+ SUPLIBHARDENEDOUTPUTBUF Buf;
+ Buf.off = 0;
+ Buf.szBuf[0] = '\0';
+ RTStrFormatV(suplibHardenedOutput, &Buf, NULL, NULL, pszFormat, va);
+
+#else /* !IPRT_NO_CRT */
+ /*
+ * Format loop.
+ */
+ char ch;
+ const char *pszLast = pszFormat;
+ for (;;)
+ {
+ ch = *pszFormat;
+ if (!ch)
+ break;
+ pszFormat++;
+
+ if (ch == '%')
+ {
+ /*
+ * Format argument.
+ */
+
+ /* Flush unwritten bits. */
+ if (pszLast != pszFormat - 1)
+ suplibHardenedPrintStrN(pszLast, pszFormat - pszLast - 1);
+ pszLast = pszFormat;
+ ch = *pszFormat++;
+
+ /* flags. */
+ uint32_t fFlags = 0;
+ for (;;)
+ {
+ if (ch == '#') fFlags |= RTSTR_F_SPECIAL;
+ else if (ch == '-') fFlags |= RTSTR_F_LEFT;
+ else if (ch == '+') fFlags |= RTSTR_F_PLUS;
+ else if (ch == ' ') fFlags |= RTSTR_F_BLANK;
+ else if (ch == '0') fFlags |= RTSTR_F_ZEROPAD;
+ else if (ch == '\'') fFlags |= RTSTR_F_THOUSAND_SEP;
+ else break;
+ ch = *pszFormat++;
+ }
+
+ /* Width and precision - ignored. */
+ while (RT_C_IS_DIGIT(ch))
+ ch = *pszFormat++;
+ if (ch == '*')
+ va_arg(va, int);
+ if (ch == '.')
+ {
+ do ch = *pszFormat++;
+ while (RT_C_IS_DIGIT(ch));
+ if (ch == '*')
+ va_arg(va, int);
+ }
+
+ /* Size. */
+ char chArgSize = 0;
+ switch (ch)
+ {
+ case 'z':
+ case 'L':
+ case 'j':
+ case 't':
+ chArgSize = ch;
+ ch = *pszFormat++;
+ break;
+
+ case 'l':
+ chArgSize = ch;
+ ch = *pszFormat++;
+ if (ch == 'l')
+ {
+ chArgSize = 'L';
+ ch = *pszFormat++;
+ }
+ break;
+
+ case 'h':
+ chArgSize = ch;
+ ch = *pszFormat++;
+ if (ch == 'h')
+ {
+ chArgSize = 'H';
+ ch = *pszFormat++;
+ }
+ break;
+ }
+
+ /*
+ * Do type specific formatting.
+ */
+ switch (ch)
+ {
+ case 'c':
+ ch = (char)va_arg(va, int);
+ suplibHardenedPrintChr(ch);
+ break;
+
+ case 's':
+ if (chArgSize == 'l')
+ {
+ PCRTUTF16 pwszStr = va_arg(va, PCRTUTF16 );
+ if (RT_VALID_PTR(pwszStr))
+ suplibHardenedPrintWideStr(pwszStr);
+ else
+ suplibHardenedPrintStr("<NULL>");
+ }
+ else
+ {
+ const char *pszStr = va_arg(va, const char *);
+ if (!RT_VALID_PTR(pszStr))
+ pszStr = "<NULL>";
+ suplibHardenedPrintStr(pszStr);
+ }
+ break;
+
+ case 'd':
+ case 'i':
+ {
+ int64_t iValue;
+ if (chArgSize == 'L' || chArgSize == 'j')
+ iValue = va_arg(va, int64_t);
+ else if (chArgSize == 'l')
+ iValue = va_arg(va, signed long);
+ else if (chArgSize == 'z' || chArgSize == 't')
+ iValue = va_arg(va, intptr_t);
+ else
+ iValue = va_arg(va, signed int);
+ if (iValue < 0)
+ {
+ suplibHardenedPrintChr('-');
+ iValue = -iValue;
+ }
+ suplibHardenedPrintDecimal(iValue);
+ break;
+ }
+
+ case 'p':
+ case 'x':
+ case 'X':
+ case 'u':
+ case 'o':
+ {
+ unsigned uBase = 10;
+ uint64_t uValue;
+
+ switch (ch)
+ {
+ case 'p':
+ fFlags |= RTSTR_F_ZEROPAD; /* Note not standard behaviour (but I like it this way!) */
+ uBase = 16;
+ break;
+ case 'X':
+ fFlags |= RTSTR_F_CAPITAL;
+ RT_FALL_THRU();
+ case 'x':
+ uBase = 16;
+ break;
+ case 'u':
+ uBase = 10;
+ break;
+ case 'o':
+ uBase = 8;
+ break;
+ }
+
+ if (ch == 'p' || chArgSize == 'z' || chArgSize == 't')
+ uValue = va_arg(va, uintptr_t);
+ else if (chArgSize == 'L' || chArgSize == 'j')
+ uValue = va_arg(va, uint64_t);
+ else if (chArgSize == 'l')
+ uValue = va_arg(va, unsigned long);
+ else
+ uValue = va_arg(va, unsigned int);
+
+ if (uBase == 10)
+ suplibHardenedPrintDecimal(uValue);
+ else
+ suplibHardenedPrintHexOctal(uValue, uBase, fFlags);
+ break;
+ }
+
+ case 'R':
+ if (pszFormat[0] == 'r' && pszFormat[1] == 'c')
+ {
+ int iValue = va_arg(va, int);
+ if (iValue < 0)
+ {
+ suplibHardenedPrintChr('-');
+ iValue = -iValue;
+ }
+ suplibHardenedPrintDecimal(iValue);
+ pszFormat += 2;
+ break;
+ }
+ RT_FALL_THRU();
+
+ /*
+ * Custom format.
+ */
+ default:
+ suplibHardenedPrintStr("[bad format: ");
+ suplibHardenedPrintStrN(pszLast, pszFormat - pszLast);
+ suplibHardenedPrintChr(']');
+ break;
+ }
+
+ /* continue */
+ pszLast = pszFormat;
+ }
+ }
+
+ /* Flush the last bits of the string. */
+ if (pszLast != pszFormat)
+ suplibHardenedPrintStrN(pszLast, pszFormat - pszLast);
+#endif /* !IPRT_NO_CRT */
+}
+
+
+/**
+ * Prints to standard error.
+ *
+ * @param pszFormat The format string.
+ * @param ... Arguments to format.
+ */
+DECLHIDDEN(void) suplibHardenedPrintF(const char *pszFormat, ...)
+{
+ va_list va;
+ va_start(va, pszFormat);
+ suplibHardenedPrintFV(pszFormat, va);
+ va_end(va);
+}
+
+
+/**
+ * @copydoc RTPathStripFilename
+ */
+static void suplibHardenedPathStripFilename(char *pszPath)
+{
+ char *psz = pszPath;
+ char *pszLastSep = pszPath;
+
+ for (;; psz++)
+ {
+ switch (*psz)
+ {
+ /* handle separators. */
+#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
+ case ':':
+ pszLastSep = psz + 1;
+ break;
+
+ case '\\':
+#endif
+ case '/':
+ pszLastSep = psz;
+ break;
+
+ /* the end */
+ case '\0':
+ if (pszLastSep == pszPath)
+ *pszLastSep++ = '.';
+ *pszLastSep = '\0';
+ return;
+ }
+ }
+ /* will never get here */
+}
+
+
+DECLHIDDEN(char *) supR3HardenedPathFilename(const char *pszPath)
+{
+ const char *psz = pszPath;
+ const char *pszLastComp = pszPath;
+
+ for (;; psz++)
+ {
+ switch (*psz)
+ {
+ /* handle separators. */
+#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
+ case ':':
+ pszLastComp = psz + 1;
+ break;
+
+ case '\\':
+#endif
+ case '/':
+ pszLastComp = psz + 1;
+ break;
+
+ /* the end */
+ case '\0':
+ if (*pszLastComp)
+ return (char *)(void *)pszLastComp;
+ return NULL;
+ }
+ }
+
+ /* will never get here */
+}
+
+
+DECLHIDDEN(int) supR3HardenedPathAppPrivateNoArch(char *pszPath, size_t cchPath)
+{
+#if !defined(RT_OS_WINDOWS) && defined(RTPATH_APP_PRIVATE)
+ const char *pszSrcPath = RTPATH_APP_PRIVATE;
+ size_t cchPathPrivateNoArch = suplibHardenedStrLen(pszSrcPath);
+ if (cchPathPrivateNoArch >= cchPath)
+ supR3HardenedFatal("supR3HardenedPathAppPrivateNoArch: Buffer overflow, %zu >= %zu\n", cchPathPrivateNoArch, cchPath);
+ suplibHardenedMemCopy(pszPath, pszSrcPath, cchPathPrivateNoArch + 1);
+ return VINF_SUCCESS;
+
+#else
+ return supR3HardenedPathAppBin(pszPath, cchPath);
+#endif
+}
+
+
+DECLHIDDEN(int) supR3HardenedPathAppPrivateArch(char *pszPath, size_t cchPath)
+{
+#if !defined(RT_OS_WINDOWS) && defined(RTPATH_APP_PRIVATE_ARCH)
+ const char *pszSrcPath = RTPATH_APP_PRIVATE_ARCH;
+ size_t cchPathPrivateArch = suplibHardenedStrLen(pszSrcPath);
+ if (cchPathPrivateArch >= cchPath)
+ supR3HardenedFatal("supR3HardenedPathAppPrivateArch: Buffer overflow, %zu >= %zu\n", cchPathPrivateArch, cchPath);
+ suplibHardenedMemCopy(pszPath, pszSrcPath, cchPathPrivateArch + 1);
+ return VINF_SUCCESS;
+
+#else
+ return supR3HardenedPathAppBin(pszPath, cchPath);
+#endif
+}
+
+
+DECLHIDDEN(int) supR3HardenedPathAppSharedLibs(char *pszPath, size_t cchPath)
+{
+#if !defined(RT_OS_WINDOWS) && defined(RTPATH_SHARED_LIBS)
+ const char *pszSrcPath = RTPATH_SHARED_LIBS;
+ size_t cchPathSharedLibs = suplibHardenedStrLen(pszSrcPath);
+ if (cchPathSharedLibs >= cchPath)
+ supR3HardenedFatal("supR3HardenedPathAppSharedLibs: Buffer overflow, %zu >= %zu\n", cchPathSharedLibs, cchPath);
+ suplibHardenedMemCopy(pszPath, pszSrcPath, cchPathSharedLibs + 1);
+ return VINF_SUCCESS;
+
+#else
+ return supR3HardenedPathAppBin(pszPath, cchPath);
+#endif
+}
+
+
+DECLHIDDEN(int) supR3HardenedPathAppDocs(char *pszPath, size_t cchPath)
+{
+#if !defined(RT_OS_WINDOWS) && defined(RTPATH_APP_DOCS)
+ const char *pszSrcPath = RTPATH_APP_DOCS;
+ size_t cchPathAppDocs = suplibHardenedStrLen(pszSrcPath);
+ if (cchPathAppDocs >= cchPath)
+ supR3HardenedFatal("supR3HardenedPathAppDocs: Buffer overflow, %zu >= %zu\n", cchPathAppDocs, cchPath);
+ suplibHardenedMemCopy(pszPath, pszSrcPath, cchPathAppDocs + 1);
+ return VINF_SUCCESS;
+
+#else
+ return supR3HardenedPathAppBin(pszPath, cchPath);
+#endif
+}
+
+
+/**
+ * Returns the full path to the executable in g_szSupLibHardenedExePath.
+ */
+static void supR3HardenedGetFullExePath(void)
+{
+ /*
+ * Get the program filename.
+ *
+ * Most UNIXes have no API for obtaining the executable path, but provides a symbolic
+ * link in the proc file system that tells who was exec'ed. The bad thing about this
+ * is that we have to use readlink, one of the weirder UNIX APIs.
+ *
+ * Darwin, OS/2 and Windows all have proper APIs for getting the program file name.
+ */
+#if defined(RT_OS_LINUX) || defined(RT_OS_FREEBSD) || defined(RT_OS_SOLARIS)
+# ifdef RT_OS_LINUX
+ int cchLink = readlink("/proc/self/exe", &g_szSupLibHardenedExePath[0], sizeof(g_szSupLibHardenedExePath) - 1);
+
+# elif defined(RT_OS_SOLARIS)
+ char szFileBuf[PATH_MAX + 1];
+ sprintf(szFileBuf, "/proc/%ld/path/a.out", (long)getpid());
+ int cchLink = readlink(szFileBuf, &g_szSupLibHardenedExePath[0], sizeof(g_szSupLibHardenedExePath) - 1);
+
+# else /* RT_OS_FREEBSD */
+ int aiName[4];
+ aiName[0] = CTL_KERN;
+ aiName[1] = KERN_PROC;
+ aiName[2] = KERN_PROC_PATHNAME;
+ aiName[3] = getpid();
+
+ size_t cbPath = sizeof(g_szSupLibHardenedExePath);
+ if (sysctl(aiName, RT_ELEMENTS(aiName), g_szSupLibHardenedExePath, &cbPath, NULL, 0) < 0)
+ supR3HardenedFatal("supR3HardenedExecDir: sysctl failed\n");
+ g_szSupLibHardenedExePath[sizeof(g_szSupLibHardenedExePath) - 1] = '\0';
+ int cchLink = suplibHardenedStrLen(g_szSupLibHardenedExePath); /* paranoid? can't we use cbPath? */
+
+# endif
+ if (cchLink < 0 || cchLink == sizeof(g_szSupLibHardenedExePath) - 1)
+ supR3HardenedFatal("supR3HardenedExecDir: couldn't read \"%s\", errno=%d cchLink=%d\n",
+ g_szSupLibHardenedExePath, errno, cchLink);
+ g_szSupLibHardenedExePath[cchLink] = '\0';
+
+#elif defined(RT_OS_OS2) || defined(RT_OS_L4)
+ _execname(g_szSupLibHardenedExePath, sizeof(g_szSupLibHardenedExePath));
+
+#elif defined(RT_OS_DARWIN)
+ const char *pszImageName = _dyld_get_image_name(0);
+ if (!pszImageName)
+ supR3HardenedFatal("supR3HardenedExecDir: _dyld_get_image_name(0) failed\n");
+ size_t cchImageName = suplibHardenedStrLen(pszImageName);
+ if (!cchImageName || cchImageName >= sizeof(g_szSupLibHardenedExePath))
+ supR3HardenedFatal("supR3HardenedExecDir: _dyld_get_image_name(0) failed, cchImageName=%d\n", cchImageName);
+ suplibHardenedMemCopy(g_szSupLibHardenedExePath, pszImageName, cchImageName + 1);
+ /** @todo abspath the string or this won't work:
+ * cd /Applications/VirtualBox.app/Contents/Resources/VirtualBoxVM.app/Contents/MacOS/ && ./VirtualBoxVM --startvm name */
+
+#elif defined(RT_OS_WINDOWS)
+ char *pszDst = g_szSupLibHardenedExePath;
+ int rc = RTUtf16ToUtf8Ex(g_wszSupLibHardenedExePath, RTSTR_MAX, &pszDst, sizeof(g_szSupLibHardenedExePath), NULL);
+ if (RT_FAILURE(rc))
+ supR3HardenedFatal("supR3HardenedExecDir: RTUtf16ToUtf8Ex failed, rc=%Rrc\n", rc);
+#else
+# error needs porting.
+#endif
+
+ /*
+ * Determine the application binary directory location.
+ */
+ suplibHardenedStrCopy(g_szSupLibHardenedAppBinPath, g_szSupLibHardenedExePath);
+ suplibHardenedPathStripFilename(g_szSupLibHardenedAppBinPath);
+
+ g_offSupLibHardenedExecName = suplibHardenedStrLen(g_szSupLibHardenedAppBinPath);
+ while (RTPATH_IS_SEP(g_szSupLibHardenedExePath[g_offSupLibHardenedExecName]))
+ g_offSupLibHardenedExecName++;
+ g_cchSupLibHardenedExecName = suplibHardenedStrLen(&g_szSupLibHardenedExePath[g_offSupLibHardenedExecName]);
+
+ if (g_enmSupR3HardenedMainState < SUPR3HARDENEDMAINSTATE_HARDENED_MAIN_CALLED)
+ supR3HardenedFatal("supR3HardenedExecDir: Called before SUPR3HardenedMain! (%d)\n", g_enmSupR3HardenedMainState);
+ switch (g_fSupHardenedMain & SUPSECMAIN_FLAGS_LOC_MASK)
+ {
+ case SUPSECMAIN_FLAGS_LOC_APP_BIN:
+ break;
+ case SUPSECMAIN_FLAGS_LOC_TESTCASE:
+ suplibHardenedPathStripFilename(g_szSupLibHardenedAppBinPath);
+ break;
+#ifdef RT_OS_DARWIN
+ case SUPSECMAIN_FLAGS_LOC_OSX_HLP_APP:
+ {
+ /* We must ascend to the parent bundle's Contents directory then decend into its MacOS: */
+ static const RTSTRTUPLE s_aComponentsToSkip[] =
+ { { RT_STR_TUPLE("MacOS") }, { RT_STR_TUPLE("Contents") }, { NULL /*some.app*/, 0 }, { RT_STR_TUPLE("Resources") } };
+ size_t cchPath = suplibHardenedStrLen(g_szSupLibHardenedAppBinPath);
+ for (uintptr_t i = 0; i < RT_ELEMENTS(s_aComponentsToSkip); i++)
+ {
+ while (cchPath > 1 && g_szSupLibHardenedAppBinPath[cchPath - 1] == '/')
+ cchPath--;
+ size_t const cchMatch = s_aComponentsToSkip[i].cch;
+ if (cchMatch > 0)
+ {
+ if ( cchPath >= cchMatch + sizeof("VirtualBox.app/Contents")
+ && g_szSupLibHardenedAppBinPath[cchPath - cchMatch - 1] == '/'
+ && suplibHardenedMemComp(&g_szSupLibHardenedAppBinPath[cchPath - cchMatch],
+ s_aComponentsToSkip[i].psz, cchMatch) == 0)
+ cchPath -= cchMatch;
+ else
+ supR3HardenedFatal("supR3HardenedExecDir: Bad helper app path (tail component #%u '%s'): %s\n",
+ i, s_aComponentsToSkip[i].psz, g_szSupLibHardenedAppBinPath);
+ }
+ else if ( cchPath > g_cchSupLibHardenedExecName + sizeof("VirtualBox.app/Contents/Resources/.app")
+ && suplibHardenedMemComp(&g_szSupLibHardenedAppBinPath[cchPath - 4], ".app", 4) == 0
+ && suplibHardenedMemComp(&g_szSupLibHardenedAppBinPath[cchPath - 4 - g_cchSupLibHardenedExecName],
+ &g_szSupLibHardenedExePath[g_offSupLibHardenedExecName],
+ g_cchSupLibHardenedExecName) == 0)
+ cchPath -= g_cchSupLibHardenedExecName + 4;
+ else
+ supR3HardenedFatal("supR3HardenedExecDir: Bad helper app path (tail component #%u '%s.app'): %s\n",
+ i, &g_szSupLibHardenedExePath[g_offSupLibHardenedExecName], g_szSupLibHardenedAppBinPath);
+ }
+ suplibHardenedMemCopy(&g_szSupLibHardenedAppBinPath[cchPath], "MacOS", sizeof("MacOS"));
+ break;
+ }
+#endif /* RT_OS_DARWIN */
+ default:
+ supR3HardenedFatal("supR3HardenedExecDir: Unknown program binary location: %#x\n", g_fSupHardenedMain);
+ }
+}
+
+
+#ifdef RT_OS_LINUX
+/**
+ * Checks if we can read /proc/self/exe.
+ *
+ * This is used on linux to see if we have to call init
+ * with program path or not.
+ *
+ * @returns true / false.
+ */
+static bool supR3HardenedMainIsProcSelfExeAccssible(void)
+{
+ char szPath[RTPATH_MAX];
+ int cchLink = readlink("/proc/self/exe", szPath, sizeof(szPath));
+ return cchLink != -1;
+}
+#endif /* RT_OS_LINUX */
+
+
+
+/**
+ * @remarks not quite like RTPathExecDir actually...
+ */
+DECLHIDDEN(int) supR3HardenedPathAppBin(char *pszPath, size_t cchPath)
+{
+ /*
+ * Lazy init (probably not required).
+ */
+ if (!g_szSupLibHardenedAppBinPath[0])
+ supR3HardenedGetFullExePath();
+
+ /*
+ * Calc the length and check if there is space before copying.
+ */
+ size_t cch = suplibHardenedStrLen(g_szSupLibHardenedAppBinPath) + 1;
+ if (cch <= cchPath)
+ {
+ suplibHardenedMemCopy(pszPath, g_szSupLibHardenedAppBinPath, cch + 1);
+ return VINF_SUCCESS;
+ }
+
+ supR3HardenedFatal("supR3HardenedPathAppBin: Buffer too small (%u < %u)\n", cchPath, cch);
+ /* not reached */
+}
+
+
+#ifdef RT_OS_WINDOWS
+extern "C" uint32_t g_uNtVerCombined;
+#endif
+
+DECLHIDDEN(void) supR3HardenedOpenLog(int *pcArgs, char **papszArgs)
+{
+ static const char s_szLogOption[] = "--sup-hardening-log=";
+
+ /*
+ * Scan the argument vector.
+ */
+ int cArgs = *pcArgs;
+ for (int iArg = 1; iArg < cArgs; iArg++)
+ if (strncmp(papszArgs[iArg], s_szLogOption, sizeof(s_szLogOption) - 1) == 0)
+ {
+#ifdef RT_OS_WINDOWS
+ const char *pszLogFile = &papszArgs[iArg][sizeof(s_szLogOption) - 1];
+#endif
+
+ /*
+ * Drop the argument from the vector (has trailing NULL entry).
+ */
+// memmove(&papszArgs[iArg], &papszArgs[iArg + 1], (cArgs - iArg) * sizeof(papszArgs[0]));
+ *pcArgs -= 1;
+ cArgs -= 1;
+
+ /*
+ * Open the log file, unless we've already opened one.
+ * First argument takes precedence
+ */
+#ifdef RT_OS_WINDOWS
+ if (g_hStartupLog == NULL)
+ {
+ int rc = RTNtPathOpen(pszLogFile,
+ GENERIC_WRITE | SYNCHRONIZE,
+ FILE_ATTRIBUTE_NORMAL,
+ FILE_SHARE_READ | FILE_SHARE_WRITE,
+ FILE_OPEN_IF,
+ FILE_NON_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT,
+ OBJ_CASE_INSENSITIVE,
+ &g_hStartupLog,
+ NULL);
+ if (RT_SUCCESS(rc))
+ {
+// SUP_DPRINTF(("Log file opened: " VBOX_VERSION_STRING "r%u g_hStartupLog=%p g_uNtVerCombined=%#x\n",
+// VBOX_SVN_REV, g_hStartupLog, g_uNtVerCombined));
+
+ /*
+ * If the path contains a drive volume, save it so we can
+ * use it to flush the volume containing the log file.
+ */
+ if (RT_C_IS_ALPHA(pszLogFile[0]) && pszLogFile[1] == ':')
+ {
+// RTUtf16CopyAscii(g_wszStartupLogVol, RT_ELEMENTS(g_wszStartupLogVol), "\\??\\");
+ g_wszStartupLogVol[sizeof("\\??\\") - 1] = RT_C_TO_UPPER(pszLogFile[0]);
+ g_wszStartupLogVol[sizeof("\\??\\") + 0] = ':';
+ g_wszStartupLogVol[sizeof("\\??\\") + 1] = '\0';
+ }
+ }
+ else
+ g_hStartupLog = NULL;
+ }
+#else
+ /* Just some mumbo jumbo to shut up the compiler. */
+ g_hStartupLog -= 1;
+ g_cbStartupLog += 1;
+ //g_hStartupLog = open()
+#endif
+ }
+}
+
+
+DECLHIDDEN(void) supR3HardenedLogV(const char *pszFormat, va_list va)
+{
+#ifdef RT_OS_WINDOWS
+ if ( g_hStartupLog != NULL
+ && g_cbStartupLog < 16*_1M)
+ {
+ char szBuf[5120];
+ PCLIENT_ID pSelfId = &((PTEB)NtCurrentTeb())->ClientId;
+ size_t cchPrefix = RTStrPrintf(szBuf, sizeof(szBuf), "%x.%x: ", pSelfId->UniqueProcess, pSelfId->UniqueThread);
+ size_t cch = RTStrPrintfV(&szBuf[cchPrefix], sizeof(szBuf) - cchPrefix, pszFormat, va) + cchPrefix;
+
+ if ((size_t)cch >= sizeof(szBuf))
+ cch = sizeof(szBuf) - 1;
+
+ if (!cch || szBuf[cch - 1] != '\n')
+ szBuf[cch++] = '\n';
+
+ ASMAtomicAddU32(&g_cbStartupLog, (uint32_t)cch);
+
+ IO_STATUS_BLOCK Ios = RTNT_IO_STATUS_BLOCK_INITIALIZER;
+ LARGE_INTEGER Offset;
+ Offset.QuadPart = -1; /* Write to end of file. */
+ NtWriteFile(g_hStartupLog, NULL /*Event*/, NULL /*ApcRoutine*/, NULL /*ApcContext*/,
+ &Ios, szBuf, (ULONG)cch, &Offset, NULL /*Key*/);
+ }
+#else
+ RT_NOREF(pszFormat, va);
+ /* later */
+#endif
+}
+
+
+DECLHIDDEN(void) supR3HardenedLog(const char *pszFormat, ...)
+{
+ va_list va;
+ va_start(va, pszFormat);
+ supR3HardenedLogV(pszFormat, va);
+ va_end(va);
+}
+
+
+DECLHIDDEN(void) supR3HardenedLogFlush(void)
+{
+#ifdef RT_OS_WINDOWS
+ if ( g_hStartupLog != NULL
+ && g_cbStartupLog < 16*_1M)
+ {
+ IO_STATUS_BLOCK Ios = RTNT_IO_STATUS_BLOCK_INITIALIZER;
+ NTSTATUS rcNt = NtFlushBuffersFile(g_hStartupLog, &Ios);
+
+ /*
+ * Try flush the volume containing the log file too.
+ */
+ if (g_wszStartupLogVol[0])
+ {
+ HANDLE hLogVol = RTNT_INVALID_HANDLE_VALUE;
+ UNICODE_STRING NtName;
+ NtName.Buffer = g_wszStartupLogVol;
+ NtName.Length = (USHORT)(RTUtf16Len(g_wszStartupLogVol) * sizeof(RTUTF16));
+ NtName.MaximumLength = NtName.Length + 1;
+ OBJECT_ATTRIBUTES ObjAttr;
+ InitializeObjectAttributes(&ObjAttr, &NtName, OBJ_CASE_INSENSITIVE, NULL /*hRootDir*/, NULL /*pSecDesc*/);
+ RTNT_IO_STATUS_BLOCK_REINIT(&Ios);
+ rcNt = NtCreateFile(&hLogVol,
+ GENERIC_WRITE | GENERIC_READ | SYNCHRONIZE | FILE_READ_ATTRIBUTES,
+ &ObjAttr,
+ &Ios,
+ NULL /* Allocation Size*/,
+ 0 /*FileAttributes*/,
+ FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
+ FILE_OPEN,
+ FILE_NON_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT,
+ NULL /*EaBuffer*/,
+ 0 /*EaLength*/);
+ if (NT_SUCCESS(rcNt))
+ rcNt = Ios.Status;
+ if (NT_SUCCESS(rcNt))
+ {
+ RTNT_IO_STATUS_BLOCK_REINIT(&Ios);
+ rcNt = NtFlushBuffersFile(hLogVol, &Ios);
+ NtClose(hLogVol);
+ }
+ else
+ {
+ /* This may have sideeffects similar to what we want... */
+ hLogVol = RTNT_INVALID_HANDLE_VALUE;
+ RTNT_IO_STATUS_BLOCK_REINIT(&Ios);
+ rcNt = NtCreateFile(&hLogVol,
+ GENERIC_READ | SYNCHRONIZE | FILE_READ_ATTRIBUTES,
+ &ObjAttr,
+ &Ios,
+ NULL /* Allocation Size*/,
+ 0 /*FileAttributes*/,
+ FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
+ FILE_OPEN,
+ FILE_NON_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT,
+ NULL /*EaBuffer*/,
+ 0 /*EaLength*/);
+ if (NT_SUCCESS(rcNt) && NT_SUCCESS(Ios.Status))
+ NtClose(hLogVol);
+ }
+ }
+ }
+#else
+ /* later */
+#endif
+}
+
+
+/**
+ * Prints the message prefix.
+ */
+static void suplibHardenedPrintPrefix(void)
+{
+ if (g_pszSupLibHardenedProgName)
+ suplibHardenedPrintStr(g_pszSupLibHardenedProgName);
+ suplibHardenedPrintStr(": ");
+}
+
+
+DECL_NO_RETURN(DECLHIDDEN(void)) supR3HardenedFatalMsgV(const char *pszWhere, SUPINITOP enmWhat, int rc,
+ const char *pszMsgFmt, va_list va)
+{
+ /*
+ * First to the log.
+ */
+ supR3HardenedLog("Error %d in %s! (enmWhat=%d)\n", rc, pszWhere, enmWhat);
+ va_list vaCopy;
+ va_copy(vaCopy, va);
+ supR3HardenedLogV(pszMsgFmt, vaCopy);
+ va_end(vaCopy);
+
+#ifdef RT_OS_WINDOWS
+ /*
+ * The release log.
+ */
+ if (g_pfnRTLogRelPrintf)
+ {
+ va_copy(vaCopy, va);
+ g_pfnRTLogRelPrintf("supR3HardenedFatalMsgV: %s enmWhat=%d rc=%Rrc (%#x)\n", pszWhere, enmWhat, rc);
+ g_pfnRTLogRelPrintf("supR3HardenedFatalMsgV: %N\n", pszMsgFmt, &vaCopy);
+ va_end(vaCopy);
+ }
+#endif
+
+ /*
+ * Then to the console.
+ */
+ suplibHardenedPrintPrefix();
+ suplibHardenedPrintF("Error %d in %s!\n", rc, pszWhere);
+
+ suplibHardenedPrintPrefix();
+ va_copy(vaCopy, va);
+ suplibHardenedPrintFV(pszMsgFmt, vaCopy);
+ va_end(vaCopy);
+ suplibHardenedPrintChr('\n');
+
+ switch (enmWhat)
+ {
+ case kSupInitOp_Driver:
+ suplibHardenedPrintChr('\n');
+ suplibHardenedPrintPrefix();
+ suplibHardenedPrintStr("Tip! Make sure the kernel module is loaded. It may also help to reinstall VirtualBox.\n");
+ break;
+
+ case kSupInitOp_Misc:
+ case kSupInitOp_IPRT:
+ case kSupInitOp_Integrity:
+ case kSupInitOp_RootCheck:
+ suplibHardenedPrintChr('\n');
+ suplibHardenedPrintPrefix();
+ suplibHardenedPrintStr("Tip! It may help to reinstall VirtualBox.\n");
+ break;
+
+ default:
+ /* no hints here */
+ break;
+ }
+
+ /*
+ * Finally, TrustedError if appropriate.
+ */
+ if (g_enmSupR3HardenedMainState >= SUPR3HARDENEDMAINSTATE_WIN_IMPORTS_RESOLVED)
+ {
+#ifdef SUP_HARDENED_SUID
+ /* Drop any root privileges we might be holding, this won't return
+ if it fails but end up calling supR3HardenedFatal[V]. */
+ supR3HardenedMainDropPrivileges();
+#endif
+ /* Close the driver, if we succeeded opening it. Both because
+ TrustedError may be untrustworthy and because the driver deosn't
+ like us if we fork(). @bugref{8838} */
+ suplibOsTerm(&g_SupPreInitData.Data);
+
+ /*
+ * Now try resolve and call the TrustedError entry point if we can find it.
+ * Note! Loader involved, so we must guard against loader hooks calling us.
+ */
+ static volatile bool s_fRecursive = false;
+ if (!s_fRecursive)
+ {
+ s_fRecursive = true;
+
+ PFNSUPTRUSTEDERROR pfnTrustedError = supR3HardenedMainGetTrustedError(g_pszSupLibHardenedProgName);
+ if (pfnTrustedError)
+ {
+ /* We'll fork before we make the call because that way the session management
+ in main will see us exiting immediately (if it's involved with us) and possibly
+ get an error back to the API / user. */
+#if !defined(RT_OS_WINDOWS) && !defined(RT_OS_OS2) && /* @bugref{10170}: */ !defined(RT_OS_DARWIN)
+ int pid = fork();
+ if (pid <= 0)
+#endif
+ {
+ pfnTrustedError(pszWhere, enmWhat, rc, pszMsgFmt, va);
+ }
+ }
+
+ s_fRecursive = false;
+ }
+ }
+#if defined(RT_OS_WINDOWS)
+ /*
+ * Report the error to the parent if this happens during early VM init.
+ */
+ else if ( g_enmSupR3HardenedMainState < SUPR3HARDENEDMAINSTATE_WIN_IMPORTS_RESOLVED
+ && g_enmSupR3HardenedMainState != SUPR3HARDENEDMAINSTATE_NOT_YET_CALLED)
+ supR3HardenedWinReportErrorToParent(pszWhere, enmWhat, rc, pszMsgFmt, va);
+#endif
+
+ /*
+ * Quit
+ */
+ suplibHardenedExit(RTEXITCODE_FAILURE);
+}
+
+
+DECL_NO_RETURN(DECLHIDDEN(void)) supR3HardenedFatalMsg(const char *pszWhere, SUPINITOP enmWhat, int rc,
+ const char *pszMsgFmt, ...)
+{
+ va_list va;
+ va_start(va, pszMsgFmt);
+ supR3HardenedFatalMsgV(pszWhere, enmWhat, rc, pszMsgFmt, va);
+ /* not reached */
+}
+
+
+DECL_NO_RETURN(DECLHIDDEN(void)) supR3HardenedFatalV(const char *pszFormat, va_list va)
+{
+ supR3HardenedLog("Fatal error:\n");
+ va_list vaCopy;
+ va_copy(vaCopy, va);
+ supR3HardenedLogV(pszFormat, vaCopy);
+ va_end(vaCopy);
+
+#if defined(RT_OS_WINDOWS)
+ /*
+ * Report the error to the parent if this happens during early VM init.
+ */
+ if ( g_enmSupR3HardenedMainState < SUPR3HARDENEDMAINSTATE_WIN_IMPORTS_RESOLVED
+ && g_enmSupR3HardenedMainState != SUPR3HARDENEDMAINSTATE_NOT_YET_CALLED)
+ supR3HardenedWinReportErrorToParent(NULL, kSupInitOp_Invalid, VERR_INTERNAL_ERROR, pszFormat, va);
+ else
+#endif
+ {
+#ifdef RT_OS_WINDOWS
+ if (g_pfnRTLogRelPrintf)
+ {
+ va_copy(vaCopy, va);
+ g_pfnRTLogRelPrintf("supR3HardenedFatalV: %N", pszFormat, &vaCopy);
+ va_end(vaCopy);
+ }
+#endif
+
+ suplibHardenedPrintPrefix();
+ suplibHardenedPrintFV(pszFormat, va);
+ }
+
+ suplibHardenedExit(RTEXITCODE_FAILURE);
+}
+
+
+DECL_NO_RETURN(DECLHIDDEN(void)) supR3HardenedFatal(const char *pszFormat, ...)
+{
+ va_list va;
+ va_start(va, pszFormat);
+ supR3HardenedFatalV(pszFormat, va);
+ /* not reached */
+}
+
+
+DECLHIDDEN(int) supR3HardenedErrorV(int rc, bool fFatal, const char *pszFormat, va_list va)
+{
+ if (fFatal)
+ supR3HardenedFatalV(pszFormat, va);
+
+ supR3HardenedLog("Error (rc=%d):\n", rc);
+ va_list vaCopy;
+ va_copy(vaCopy, va);
+ supR3HardenedLogV(pszFormat, vaCopy);
+ va_end(vaCopy);
+
+#ifdef RT_OS_WINDOWS
+ if (g_pfnRTLogRelPrintf)
+ {
+ va_copy(vaCopy, va);
+ g_pfnRTLogRelPrintf("supR3HardenedErrorV: %N", pszFormat, &vaCopy);
+ va_end(vaCopy);
+ }
+#endif
+
+ suplibHardenedPrintPrefix();
+ suplibHardenedPrintFV(pszFormat, va);
+
+ return rc;
+}
+
+
+DECLHIDDEN(int) supR3HardenedError(int rc, bool fFatal, const char *pszFormat, ...)
+{
+ va_list va;
+ va_start(va, pszFormat);
+ supR3HardenedErrorV(rc, fFatal, pszFormat, va);
+ va_end(va);
+ return rc;
+}
+
+
+
+/**
+ * Attempts to open /dev/vboxdrv (or equvivalent).
+ *
+ * @remarks This function will not return on failure.
+ */
+DECLHIDDEN(void) supR3HardenedMainOpenDevice(void)
+{
+ RTERRINFOSTATIC ErrInfo;
+ SUPINITOP enmWhat = kSupInitOp_Driver;
+ uint32_t fFlags = SUPR3INIT_F_UNRESTRICTED;
+ if (g_fSupHardenedMain & SUPSECMAIN_FLAGS_DRIVERLESS)
+ fFlags |= SUPR3INIT_F_DRIVERLESS;
+ if (g_fSupHardenedMain & SUPSECMAIN_FLAGS_DRIVERLESS_IEM_ALLOWED)
+ fFlags |= SUPR3INIT_F_DRIVERLESS_IEM_ALLOWED;
+#ifdef VBOX_WITH_DRIVERLESS_NEM_FALLBACK
+ if (g_fSupHardenedMain & SUPSECMAIN_FLAGS_DRIVERLESS_NEM_FALLBACK)
+ fFlags |= SUPR3INIT_F_DRIVERLESS_NEM_FALLBACK;
+#endif
+ int rc = suplibOsInit(&g_SupPreInitData.Data, false /*fPreInit*/, fFlags, &enmWhat, RTErrInfoInitStatic(&ErrInfo));
+ if (RT_SUCCESS(rc))
+ return;
+
+ if (RTErrInfoIsSet(&ErrInfo.Core))
+ supR3HardenedFatalMsg("suplibOsInit", enmWhat, rc, "%s", ErrInfo.szMsg);
+
+ switch (rc)
+ {
+ /** @todo better messages! */
+ case VERR_VM_DRIVER_NOT_INSTALLED:
+ supR3HardenedFatalMsg("suplibOsInit", kSupInitOp_Driver, rc, "Kernel driver not installed");
+ case VERR_VM_DRIVER_NOT_ACCESSIBLE:
+ supR3HardenedFatalMsg("suplibOsInit", kSupInitOp_Driver, rc, "Kernel driver not accessible");
+ case VERR_VM_DRIVER_LOAD_ERROR:
+ supR3HardenedFatalMsg("suplibOsInit", kSupInitOp_Driver, rc, "VERR_VM_DRIVER_LOAD_ERROR");
+ case VERR_VM_DRIVER_OPEN_ERROR:
+ supR3HardenedFatalMsg("suplibOsInit", kSupInitOp_Driver, rc, "VERR_VM_DRIVER_OPEN_ERROR");
+ case VERR_VM_DRIVER_VERSION_MISMATCH:
+ supR3HardenedFatalMsg("suplibOsInit", kSupInitOp_Driver, rc, "Kernel driver version mismatch");
+ case VERR_ACCESS_DENIED:
+ supR3HardenedFatalMsg("suplibOsInit", kSupInitOp_Driver, rc, "VERR_ACCESS_DENIED");
+ case VERR_NO_MEMORY:
+ supR3HardenedFatalMsg("suplibOsInit", kSupInitOp_Driver, rc, "Kernel memory allocation/mapping failed");
+ case VERR_SUPDRV_HARDENING_EVIL_HANDLE:
+ supR3HardenedFatalMsg("suplibOsInit", kSupInitOp_Integrity, rc, "VERR_SUPDRV_HARDENING_EVIL_HANDLE");
+ case VERR_SUPLIB_NT_PROCESS_UNTRUSTED_0:
+ supR3HardenedFatalMsg("suplibOsInit", kSupInitOp_Integrity, rc, "VERR_SUPLIB_NT_PROCESS_UNTRUSTED_0");
+ case VERR_SUPLIB_NT_PROCESS_UNTRUSTED_1:
+ supR3HardenedFatalMsg("suplibOsInit", kSupInitOp_Integrity, rc, "VERR_SUPLIB_NT_PROCESS_UNTRUSTED_1");
+ case VERR_SUPLIB_NT_PROCESS_UNTRUSTED_2:
+ supR3HardenedFatalMsg("suplibOsInit", kSupInitOp_Integrity, rc, "VERR_SUPLIB_NT_PROCESS_UNTRUSTED_2");
+ default:
+ supR3HardenedFatalMsg("suplibOsInit", kSupInitOp_Driver, rc, "Unknown rc=%d (%Rrc)", rc, rc);
+ }
+}
+
+
+#ifdef SUP_HARDENED_SUID
+
+/**
+ * Grabs extra non-root capabilities / privileges that we might require.
+ *
+ * This is currently only used for being able to do ICMP from the NAT engine
+ * and for being able to raise thread scheduling priority
+ *
+ * @note We still have root privileges at the time of this call.
+ */
+static void supR3HardenedMainGrabCapabilites(void)
+{
+# if defined(RT_OS_LINUX)
+ /*
+ * We are about to drop all our privileges. Remove all capabilities but
+ * keep the cap_net_raw capability for ICMP sockets for the NAT stack,
+ * also keep cap_sys_nice capability for priority tweaking.
+ */
+ if (g_uCaps != 0)
+ {
+# ifdef USE_LIB_PCAP
+ /* XXX cap_net_bind_service */
+ if (!cap_set_proc(cap_from_text("all-eip cap_net_raw+ep cap_sys_nice+ep")))
+ prctl(PR_SET_KEEPCAPS, 1 /*keep=*/, 0, 0, 0);
+ prctl(PR_SET_DUMPABLE, 1 /*dump*/, 0, 0, 0);
+# else
+ cap_user_header_t hdr = (cap_user_header_t)alloca(sizeof(*hdr));
+ cap_user_data_t cap = (cap_user_data_t)alloca(2 /*_LINUX_CAPABILITY_U32S_3*/ * sizeof(*cap));
+ memset(hdr, 0, sizeof(*hdr));
+ capget(hdr, NULL);
+ if ( hdr->version != 0x19980330 /* _LINUX_CAPABILITY_VERSION_1, _LINUX_CAPABILITY_U32S_1 = 1 */
+ && hdr->version != 0x20071026 /* _LINUX_CAPABILITY_VERSION_2, _LINUX_CAPABILITY_U32S_2 = 2 */
+ && hdr->version != 0x20080522 /* _LINUX_CAPABILITY_VERSION_3, _LINUX_CAPABILITY_U32S_3 = 2 */)
+ hdr->version = _LINUX_CAPABILITY_VERSION;
+ g_uCapsVersion = hdr->version;
+ memset(cap, 0, 2 /* _LINUX_CAPABILITY_U32S_3 */ * sizeof(*cap));
+ cap->effective = g_uCaps;
+ cap->permitted = g_uCaps;
+ if (!capset(hdr, cap))
+ prctl(PR_SET_KEEPCAPS, 1 /*keep*/, 0, 0, 0);
+ prctl(PR_SET_DUMPABLE, 1 /*dump*/, 0, 0, 0);
+# endif /* !USE_LIB_PCAP */
+ }
+
+# elif defined(RT_OS_SOLARIS)
+ /*
+ * Add net_icmpaccess privilege to effective privileges and limit
+ * permitted privileges before completely dropping root privileges.
+ * This requires dropping root privileges temporarily to get the normal
+ * user's privileges.
+ */
+ seteuid(g_uid);
+ priv_set_t *pPrivEffective = priv_allocset();
+ priv_set_t *pPrivNew = priv_allocset();
+ if (pPrivEffective && pPrivNew)
+ {
+ int rc = getppriv(PRIV_EFFECTIVE, pPrivEffective);
+ seteuid(0);
+ if (!rc)
+ {
+ priv_copyset(pPrivEffective, pPrivNew);
+ rc = priv_addset(pPrivNew, PRIV_NET_ICMPACCESS);
+ if (!rc)
+ {
+ /* Order is important, as one can't set a privilege which is
+ * not in the permitted privilege set. */
+ rc = setppriv(PRIV_SET, PRIV_EFFECTIVE, pPrivNew);
+ if (rc)
+ supR3HardenedError(rc, false, "SUPR3HardenedMain: failed to set effective privilege set.\n");
+ rc = setppriv(PRIV_SET, PRIV_PERMITTED, pPrivNew);
+ if (rc)
+ supR3HardenedError(rc, false, "SUPR3HardenedMain: failed to set permitted privilege set.\n");
+ }
+ else
+ supR3HardenedError(rc, false, "SUPR3HardenedMain: failed to add NET_ICMPACCESS privilege.\n");
+ }
+ }
+ else
+ {
+ /* for memory allocation failures just continue */
+ seteuid(0);
+ }
+
+ if (pPrivEffective)
+ priv_freeset(pPrivEffective);
+ if (pPrivNew)
+ priv_freeset(pPrivNew);
+# endif
+}
+
+/*
+ * Look at the environment for some special options.
+ */
+static void supR3GrabOptions(void)
+{
+# ifdef RT_OS_LINUX
+ g_uCaps = 0;
+
+ /*
+ * Do _not_ perform any capability-related system calls for root processes
+ * (leaving g_uCaps at 0).
+ * (Hint: getuid gets the real user id, not the effective.)
+ */
+ if (getuid() != 0)
+ {
+ /*
+ * CAP_NET_RAW.
+ * Default: enabled.
+ * Can be disabled with 'export VBOX_HARD_CAP_NET_RAW=0'.
+ */
+ const char *pszOpt = getenv("VBOX_HARD_CAP_NET_RAW");
+ if ( !pszOpt
+ || memcmp(pszOpt, "0", sizeof("0")) != 0)
+ g_uCaps = CAP_TO_MASK(CAP_NET_RAW);
+
+ /*
+ * CAP_NET_BIND_SERVICE.
+ * Default: disabled.
+ * Can be enabled with 'export VBOX_HARD_CAP_NET_BIND_SERVICE=1'.
+ */
+ pszOpt = getenv("VBOX_HARD_CAP_NET_BIND_SERVICE");
+ if ( pszOpt
+ && memcmp(pszOpt, "0", sizeof("0")) != 0)
+ g_uCaps |= CAP_TO_MASK(CAP_NET_BIND_SERVICE);
+
+ /*
+ * CAP_SYS_NICE.
+ * Default: enabled.
+ * Can be disabled with 'export VBOX_HARD_CAP_SYS_NICE=0'.
+ */
+ pszOpt = getenv("VBOX_HARD_CAP_SYS_NICE");
+ if ( !pszOpt
+ || memcmp(pszOpt, "0", sizeof("0")) != 0)
+ g_uCaps |= CAP_TO_MASK(CAP_SYS_NICE);
+ }
+# endif
+}
+
+/**
+ * Drop any root privileges we might be holding.
+ */
+static void supR3HardenedMainDropPrivileges(void)
+{
+ /*
+ * Try use setre[ug]id since this will clear the save uid/gid and thus
+ * leave fewer traces behind that libs like GTK+ may pick up.
+ */
+ uid_t euid, ruid, suid;
+ gid_t egid, rgid, sgid;
+# if defined(RT_OS_DARWIN)
+ /* The really great thing here is that setreuid isn't available on
+ OS X 10.4, libc emulates it. While 10.4 have a slightly different and
+ non-standard setuid implementation compared to 10.5, the following
+ works the same way with both version since we're super user (10.5 req).
+ The following will set all three variants of the group and user IDs. */
+ setgid(g_gid);
+ setuid(g_uid);
+ euid = geteuid();
+ ruid = suid = getuid();
+ egid = getegid();
+ rgid = sgid = getgid();
+
+# elif defined(RT_OS_SOLARIS)
+ /* Solaris doesn't have setresuid, but the setreuid interface is BSD
+ compatible and will set the saved uid to euid when we pass it a ruid
+ that isn't -1 (which we do). */
+ setregid(g_gid, g_gid);
+ setreuid(g_uid, g_uid);
+ euid = geteuid();
+ ruid = suid = getuid();
+ egid = getegid();
+ rgid = sgid = getgid();
+
+# else
+ /* This is the preferred one, full control no questions about semantics.
+ PORTME: If this isn't work, try join one of two other gangs above. */
+ int res = setresgid(g_gid, g_gid, g_gid);
+ NOREF(res);
+ res = setresuid(g_uid, g_uid, g_uid);
+ NOREF(res);
+ if (getresuid(&ruid, &euid, &suid) != 0)
+ {
+ euid = geteuid();
+ ruid = suid = getuid();
+ }
+ if (getresgid(&rgid, &egid, &sgid) != 0)
+ {
+ egid = getegid();
+ rgid = sgid = getgid();
+ }
+# endif
+
+
+ /* Check that it worked out all right. */
+ if ( euid != g_uid
+ || ruid != g_uid
+ || suid != g_uid
+ || egid != g_gid
+ || rgid != g_gid
+ || sgid != g_gid)
+ supR3HardenedFatal("SUPR3HardenedMain: failed to drop root privileges!"
+ " (euid=%d ruid=%d suid=%d egid=%d rgid=%d sgid=%d; wanted uid=%d and gid=%d)\n",
+ euid, ruid, suid, egid, rgid, sgid, g_uid, g_gid);
+
+# if RT_OS_LINUX
+ /*
+ * Re-enable the cap_net_raw and cap_sys_nice capabilities which were disabled during setresuid.
+ */
+ if (g_uCaps != 0)
+ {
+# ifdef USE_LIB_PCAP
+ /** @todo Warn if that does not work? */
+ /* XXX cap_net_bind_service */
+ cap_set_proc(cap_from_text("cap_net_raw+ep cap_sys_nice+ep"));
+# else
+ cap_user_header_t hdr = (cap_user_header_t)alloca(sizeof(*hdr));
+ cap_user_data_t cap = (cap_user_data_t)alloca(2 /* _LINUX_CAPABILITY_U32S_3 */ * sizeof(*cap));
+ memset(hdr, 0, sizeof(*hdr));
+ hdr->version = g_uCapsVersion;
+ memset(cap, 0, 2 /* _LINUX_CAPABILITY_U32S_3 */ * sizeof(*cap));
+ cap->effective = g_uCaps;
+ cap->permitted = g_uCaps;
+ /** @todo Warn if that does not work? */
+ capset(hdr, cap);
+# endif /* !USE_LIB_PCAP */
+ }
+# endif
+}
+
+#endif /* SUP_HARDENED_SUID */
+
+/**
+ * Purge the process environment from any environment vairable which can lead
+ * to loading untrusted binaries compromising the process address space.
+ *
+ * @param envp The initial environment vector. (Can be NULL.)
+ */
+static void supR3HardenedMainPurgeEnvironment(char **envp)
+{
+ for (unsigned i = 0; i < RT_ELEMENTS(g_aSupEnvPurgeDescs); i++)
+ {
+ /*
+ * Update the initial environment vector, just in case someone actually cares about it.
+ */
+ if (envp)
+ {
+ const char * const pszEnv = g_aSupEnvPurgeDescs[i].pszEnv;
+ size_t const cchEnv = g_aSupEnvPurgeDescs[i].cchEnv;
+ unsigned iSrc = 0;
+ unsigned iDst = 0;
+ char *pszTmp;
+
+ while ((pszTmp = envp[iSrc]) != NULL)
+ {
+ if ( memcmp(pszTmp, pszEnv, cchEnv) != 0
+ || (pszTmp[cchEnv] != '=' && pszTmp[cchEnv] != '\0'))
+ {
+ if (iDst != iSrc)
+ envp[iDst] = pszTmp;
+ iDst++;
+ }
+ else
+ SUP_DPRINTF(("supR3HardenedMainPurgeEnvironment: dropping envp[%d]=%s\n", iSrc, pszTmp));
+ iSrc++;
+ }
+
+ if (iDst != iSrc)
+ while (iDst <= iSrc)
+ envp[iDst++] = NULL;
+ }
+
+ /*
+ * Remove from the process environment if present.
+ */
+#ifndef RT_OS_WINDOWS
+ const char *pszTmp = getenv(g_aSupEnvPurgeDescs[i].pszEnv);
+ if (pszTmp != NULL)
+ {
+ if (unsetenv((char *)g_aSupEnvPurgeDescs[i].pszEnv) == 0)
+ SUP_DPRINTF(("supR3HardenedMainPurgeEnvironment: dropped %s\n", pszTmp));
+ else
+ if (g_aSupEnvPurgeDescs[i].fPurgeErrFatal)
+ supR3HardenedFatal("SUPR3HardenedMain: failed to purge %s environment variable! (errno=%d %s)\n",
+ g_aSupEnvPurgeDescs[i].pszEnv, errno, strerror(errno));
+ else
+ SUP_DPRINTF(("supR3HardenedMainPurgeEnvironment: dropping %s failed! errno=%d\n", pszTmp, errno));
+ }
+#else
+ /** @todo Call NT API to do the same. */
+#endif
+ }
+}
+
+
+/**
+ * Returns the argument purge descriptor of the given argument if available.
+ *
+ * @retval 0 if it should not be purged.
+ * @retval 1 if it only the current argument should be purged.
+ * @retval 2 if the argument and the following (if present) should be purged.
+ * @param pszArg The argument to look for.
+ */
+static unsigned supR3HardenedMainShouldPurgeArg(const char *pszArg)
+{
+ for (unsigned i = 0; i < RT_ELEMENTS(g_aSupArgPurgeDescs); i++)
+ {
+ size_t const cchPurge = g_aSupArgPurgeDescs[i].cchArg;
+ if (!memcmp(pszArg, g_aSupArgPurgeDescs[i].pszArg, cchPurge))
+ {
+ if (pszArg[cchPurge] == '\0')
+ return 1 + g_aSupArgPurgeDescs[i].fTakesValue;
+ if ( g_aSupArgPurgeDescs[i].fTakesValue
+ && (pszArg[cchPurge] == ':' || pszArg[cchPurge] == '='))
+ return 1;
+ }
+ }
+
+ return 0;
+}
+
+
+/**
+ * Purges any command line arguments considered harmful.
+ *
+ * @param cArgsOrig The original number of arguments.
+ * @param papszArgsOrig The original argument vector.
+ * @param pcArgsNew Where to store the new number of arguments on success.
+ * @param ppapszArgsNew Where to store the pointer to the purged argument vector.
+ */
+static void supR3HardenedMainPurgeArgs(int cArgsOrig, char **papszArgsOrig, int *pcArgsNew, char ***ppapszArgsNew)
+{
+ int iDst = 0;
+#ifdef RT_OS_WINDOWS
+ char **papszArgsNew = papszArgsOrig; /* We allocated this, no need to allocate again. */
+#else
+ char **papszArgsNew = (char **)malloc((cArgsOrig + 1) * sizeof(char *));
+#endif
+ if (papszArgsNew)
+ {
+ for (int iSrc = 0; iSrc < cArgsOrig; iSrc++)
+ {
+ unsigned cPurgedArgs = supR3HardenedMainShouldPurgeArg(papszArgsOrig[iSrc]);
+ if (!cPurgedArgs)
+ papszArgsNew[iDst++] = papszArgsOrig[iSrc];
+ else
+ iSrc += cPurgedArgs - 1;
+ }
+
+ papszArgsNew[iDst] = NULL; /* The array is NULL terminated, just like envp. */
+ }
+ else
+ supR3HardenedFatal("SUPR3HardenedMain: failed to allocate memory for purged command line!\n");
+ *pcArgsNew = iDst;
+ *ppapszArgsNew = papszArgsNew;
+
+#ifdef RT_OS_WINDOWS
+ /** @todo Update command line pointers in PEB, wont really work without it. */
+#endif
+}
+
+
+/**
+ * Loads the VBoxRT DLL/SO/DYLIB, hands it the open driver,
+ * and calls RTR3InitEx.
+ *
+ * @param fFlags The SUPR3HardenedMain fFlags argument, passed to supR3PreInit.
+ *
+ * @remarks VBoxRT contains both IPRT and SUPR3.
+ * @remarks This function will not return on failure.
+ */
+static void supR3HardenedMainInitRuntime(uint32_t fFlags)
+{
+ /*
+ * Construct the name.
+ */
+ char szPath[RTPATH_MAX];
+ supR3HardenedPathAppSharedLibs(szPath, sizeof(szPath) - sizeof("/VBoxRT" SUPLIB_DLL_SUFF));
+ suplibHardenedStrCat(szPath, "/VBoxRT" SUPLIB_DLL_SUFF);
+
+ /*
+ * Open it and resolve the symbols.
+ */
+#if defined(RT_OS_WINDOWS)
+ HMODULE hMod = (HMODULE)supR3HardenedWinLoadLibrary(szPath, false /*fSystem32Only*/, g_fSupHardenedMain);
+ if (!hMod)
+ supR3HardenedFatalMsg("supR3HardenedMainInitRuntime", kSupInitOp_IPRT, VERR_MODULE_NOT_FOUND,
+ "LoadLibrary \"%s\" failed (rc=%d)",
+ szPath, RtlGetLastWin32Error());
+ PFNRTR3INITEX pfnRTInitEx = (PFNRTR3INITEX)GetProcAddress(hMod, SUP_HARDENED_SYM("RTR3InitEx"));
+ if (!pfnRTInitEx)
+ supR3HardenedFatalMsg("supR3HardenedMainInitRuntime", kSupInitOp_IPRT, VERR_SYMBOL_NOT_FOUND,
+ "Entrypoint \"RTR3InitEx\" not found in \"%s\" (rc=%d)",
+ szPath, RtlGetLastWin32Error());
+
+ PFNSUPR3PREINIT pfnSUPPreInit = (PFNSUPR3PREINIT)GetProcAddress(hMod, SUP_HARDENED_SYM("supR3PreInit"));
+ if (!pfnSUPPreInit)
+ supR3HardenedFatalMsg("supR3HardenedMainInitRuntime", kSupInitOp_IPRT, VERR_SYMBOL_NOT_FOUND,
+ "Entrypoint \"supR3PreInit\" not found in \"%s\" (rc=%d)",
+ szPath, RtlGetLastWin32Error());
+
+ g_pfnRTLogRelPrintf = (PFNRTLOGRELPRINTF)GetProcAddress(hMod, SUP_HARDENED_SYM("RTLogRelPrintf"));
+ Assert(g_pfnRTLogRelPrintf); /* Not fatal in non-strict builds. */
+
+#else
+ /* the dlopen crowd */
+ void *pvMod = dlopen(szPath, RTLD_NOW | RTLD_GLOBAL);
+ if (!pvMod)
+ supR3HardenedFatalMsg("supR3HardenedMainInitRuntime", kSupInitOp_IPRT, VERR_MODULE_NOT_FOUND,
+ "dlopen(\"%s\",) failed: %s",
+ szPath, dlerror());
+ PFNRTR3INITEX pfnRTInitEx = (PFNRTR3INITEX)(uintptr_t)dlsym(pvMod, SUP_HARDENED_SYM("RTR3InitEx"));
+ if (!pfnRTInitEx)
+ supR3HardenedFatalMsg("supR3HardenedMainInitRuntime", kSupInitOp_IPRT, VERR_SYMBOL_NOT_FOUND,
+ "Entrypoint \"RTR3InitEx\" not found in \"%s\"!\ndlerror: %s",
+ szPath, dlerror());
+ PFNSUPR3PREINIT pfnSUPPreInit = (PFNSUPR3PREINIT)(uintptr_t)dlsym(pvMod, SUP_HARDENED_SYM("supR3PreInit"));
+ if (!pfnSUPPreInit)
+ supR3HardenedFatalMsg("supR3HardenedMainInitRuntime", kSupInitOp_IPRT, VERR_SYMBOL_NOT_FOUND,
+ "Entrypoint \"supR3PreInit\" not found in \"%s\"!\ndlerror: %s",
+ szPath, dlerror());
+#endif
+
+ /*
+ * Make the calls.
+ */
+ supR3HardenedGetPreInitData(&g_SupPreInitData);
+ int rc = pfnSUPPreInit(&g_SupPreInitData, fFlags);
+ if (RT_FAILURE(rc))
+ supR3HardenedFatalMsg("supR3HardenedMainInitRuntime", kSupInitOp_IPRT, rc,
+ "supR3PreInit failed with rc=%d", rc);
+
+ /* Get the executable path for the IPRT init on linux if /proc/self/exe isn't accessible. */
+ const char *pszExePath = NULL;
+#ifdef RT_OS_LINUX
+ if (!supR3HardenedMainIsProcSelfExeAccssible())
+ pszExePath = g_szSupLibHardenedExePath;
+#endif
+
+ /* Assemble the IPRT init flags. We could probably just pass RTR3INIT_FLAGS_TRY_SUPLIB
+ here and be done with it, but it's not too much hazzle to convert fFlags 1:1. */
+ uint32_t fRtInit = 0;
+ if (!(fFlags & SUPSECMAIN_FLAGS_DONT_OPEN_DEV))
+ {
+ if (fFlags & SUPSECMAIN_FLAGS_DRIVERLESS)
+ fRtInit |= (SUPR3INIT_F_DRIVERLESS << RTR3INIT_FLAGS_SUPLIB_SHIFT) | RTR3INIT_FLAGS_TRY_SUPLIB;
+ if (fFlags & SUPSECMAIN_FLAGS_DRIVERLESS_IEM_ALLOWED)
+ fRtInit |= (SUPR3INIT_F_DRIVERLESS_IEM_ALLOWED << RTR3INIT_FLAGS_SUPLIB_SHIFT) | RTR3INIT_FLAGS_TRY_SUPLIB;
+#ifdef VBOX_WITH_DRIVERLESS_NEM_FALLBACK
+ if (fFlags & SUPSECMAIN_FLAGS_DRIVERLESS_NEM_FALLBACK)
+ fRtInit |= (SUPR3INIT_F_DRIVERLESS_NEM_FALLBACK << RTR3INIT_FLAGS_SUPLIB_SHIFT) | RTR3INIT_FLAGS_TRY_SUPLIB;
+#endif
+ if (!(fRtInit & RTR3INIT_FLAGS_TRY_SUPLIB))
+ fRtInit |= RTR3INIT_FLAGS_SUPLIB;
+ }
+
+ /* Now do the IPRT init. */
+ rc = pfnRTInitEx(RTR3INIT_VER_CUR, fRtInit, 0 /*cArgs*/, NULL /*papszArgs*/, pszExePath);
+ if (RT_FAILURE(rc))
+ supR3HardenedFatalMsg("supR3HardenedMainInitRuntime", kSupInitOp_IPRT, rc,
+ "RTR3InitEx failed with rc=%d (fRtFlags=%#x)", rc, fRtInit);
+
+#if defined(RT_OS_WINDOWS)
+ /*
+ * Windows: Create thread that terminates the process when the parent stub
+ * process terminates (VBoxNetDHCP, Ctrl-C, etc).
+ */
+ if (!(fFlags & SUPSECMAIN_FLAGS_DONT_OPEN_DEV))
+ supR3HardenedWinCreateParentWatcherThread(hMod);
+#endif
+}
+
+
+/**
+ * Construct the path to the DLL/SO/DYLIB containing the actual program.
+ *
+ * @returns VBox status code.
+ * @param pszProgName The program name.
+ * @param fMainFlags The flags passed to SUPR3HardenedMain.
+ * @param pszPath The output buffer.
+ * @param cbPath The size of the output buffer, in bytes. Must be at
+ * least 128 bytes!
+ */
+static int supR3HardenedMainGetTrustedLib(const char *pszProgName, uint32_t fMainFlags, char *pszPath, size_t cbPath)
+{
+ supR3HardenedPathAppPrivateArch(pszPath, sizeof(cbPath) - 10);
+ const char *pszSubDirSlash;
+ switch (g_fSupHardenedMain & SUPSECMAIN_FLAGS_LOC_MASK)
+ {
+ case SUPSECMAIN_FLAGS_LOC_APP_BIN:
+#ifdef RT_OS_DARWIN
+ case SUPSECMAIN_FLAGS_LOC_OSX_HLP_APP:
+#endif
+ pszSubDirSlash = "/";
+ break;
+ case SUPSECMAIN_FLAGS_LOC_TESTCASE:
+ pszSubDirSlash = "/testcase/";
+ break;
+ default:
+ pszSubDirSlash = "/";
+ supR3HardenedFatal("supR3HardenedMainGetTrustedMain: Unknown program binary location: %#x\n", g_fSupHardenedMain);
+ }
+#ifdef RT_OS_DARWIN
+ if (fMainFlags & SUPSECMAIN_FLAGS_OSX_VM_APP)
+ pszProgName = "VirtualBox";
+#else
+ RT_NOREF1(fMainFlags);
+#endif
+ size_t cch = suplibHardenedStrLen(pszPath);
+ return suplibHardenedStrCopyEx(&pszPath[cch], cbPath - cch, pszSubDirSlash, pszProgName, SUPLIB_DLL_SUFF, NULL);
+}
+
+
+/**
+ * Loads the DLL/SO/DYLIB containing the actual program and
+ * resolves the TrustedError symbol.
+ *
+ * This is very similar to supR3HardenedMainGetTrustedMain().
+ *
+ * @returns Pointer to the trusted error symbol if it is exported, NULL
+ * and no error messages otherwise.
+ * @param pszProgName The program name.
+ */
+static PFNSUPTRUSTEDERROR supR3HardenedMainGetTrustedError(const char *pszProgName)
+{
+ /*
+ * Don't bother if the main() function didn't advertise any TrustedError
+ * export. It's both a waste of time and may trigger additional problems,
+ * confusing or obscuring the original issue.
+ */
+ if (!(g_fSupHardenedMain & SUPSECMAIN_FLAGS_TRUSTED_ERROR))
+ return NULL;
+
+ /*
+ * Construct the name.
+ */
+ char szPath[RTPATH_MAX];
+ supR3HardenedMainGetTrustedLib(pszProgName, g_fSupHardenedMain, szPath, sizeof(szPath));
+
+ /*
+ * Open it and resolve the symbol.
+ */
+#if defined(RT_OS_WINDOWS)
+ supR3HardenedWinEnableThreadCreation();
+ HMODULE hMod = (HMODULE)supR3HardenedWinLoadLibrary(szPath, false /*fSystem32Only*/, 0 /*fMainFlags*/);
+ if (!hMod)
+ return NULL;
+ FARPROC pfn = GetProcAddress(hMod, SUP_HARDENED_SYM("TrustedError"));
+ if (!pfn)
+ return NULL;
+ return (PFNSUPTRUSTEDERROR)pfn;
+
+#else
+ /* the dlopen crowd */
+ void *pvMod = dlopen(szPath, RTLD_NOW | RTLD_GLOBAL);
+ if (!pvMod)
+ return NULL;
+ void *pvSym = dlsym(pvMod, SUP_HARDENED_SYM("TrustedError"));
+ if (!pvSym)
+ return NULL;
+ return (PFNSUPTRUSTEDERROR)(uintptr_t)pvSym;
+#endif
+}
+
+
+/**
+ * Loads the DLL/SO/DYLIB containing the actual program and
+ * resolves the TrustedMain symbol.
+ *
+ * @returns Pointer to the trusted main of the actual program.
+ * @param pszProgName The program name.
+ * @param fMainFlags The flags passed to SUPR3HardenedMain.
+ * @remarks This function will not return on failure.
+ */
+static PFNSUPTRUSTEDMAIN supR3HardenedMainGetTrustedMain(const char *pszProgName, uint32_t fMainFlags)
+{
+ /*
+ * Construct the name.
+ */
+ char szPath[RTPATH_MAX];
+ supR3HardenedMainGetTrustedLib(pszProgName, fMainFlags, szPath, sizeof(szPath));
+
+ /*
+ * Open it and resolve the symbol.
+ */
+#if defined(RT_OS_WINDOWS)
+ HMODULE hMod = (HMODULE)supR3HardenedWinLoadLibrary(szPath, false /*fSystem32Only*/, 0 /*fMainFlags*/);
+ if (!hMod)
+ supR3HardenedFatal("supR3HardenedMainGetTrustedMain: LoadLibrary \"%s\" failed, rc=%d\n",
+ szPath, RtlGetLastWin32Error());
+ FARPROC pfn = GetProcAddress(hMod, SUP_HARDENED_SYM("TrustedMain"));
+ if (!pfn)
+ supR3HardenedFatal("supR3HardenedMainGetTrustedMain: Entrypoint \"TrustedMain\" not found in \"%s\" (rc=%d)\n",
+ szPath, RtlGetLastWin32Error());
+ return (PFNSUPTRUSTEDMAIN)pfn;
+
+#else
+ /* the dlopen crowd */
+ void *pvMod = dlopen(szPath, RTLD_NOW | RTLD_GLOBAL);
+ if (!pvMod)
+ supR3HardenedFatal("supR3HardenedMainGetTrustedMain: dlopen(\"%s\",) failed: %s\n",
+ szPath, dlerror());
+ void *pvSym = dlsym(pvMod, SUP_HARDENED_SYM("TrustedMain"));
+ if (!pvSym)
+ supR3HardenedFatal("supR3HardenedMainGetTrustedMain: Entrypoint \"TrustedMain\" not found in \"%s\"!\ndlerror: %s\n",
+ szPath, dlerror());
+ return (PFNSUPTRUSTEDMAIN)(uintptr_t)pvSym;
+#endif
+}
+
+
+DECLHIDDEN(int) SUPR3HardenedMain(const char *pszProgName, uint32_t fFlags, int argc, char **argv, char **envp)
+{
+ SUP_DPRINTF(("SUPR3HardenedMain: pszProgName=%s fFlags=%#x\n", pszProgName, fFlags));
+ g_enmSupR3HardenedMainState = SUPR3HARDENEDMAINSTATE_HARDENED_MAIN_CALLED;
+
+ /*
+ * Note! At this point there is no IPRT, so we will have to stick
+ * to basic CRT functions that everyone agree upon.
+ */
+ g_pszSupLibHardenedProgName = pszProgName;
+ g_fSupHardenedMain = fFlags;
+ g_SupPreInitData.u32Magic = SUPPREINITDATA_MAGIC;
+ g_SupPreInitData.u32EndMagic = SUPPREINITDATA_MAGIC;
+#ifdef RT_OS_WINDOWS
+ if (!g_fSupEarlyProcessInit)
+#endif
+ g_SupPreInitData.Data.hDevice = SUP_HDEVICE_NIL;
+
+ /*
+ * Determine the full exe path as we'll be needing it for the verify all
+ * call(s) below. (We have to do this early on Linux because we * *might*
+ * not be able to access /proc/self/exe after the seteuid call.)
+ */
+ supR3HardenedGetFullExePath();
+#ifdef RT_OS_WINDOWS
+ supR3HardenedWinInitAppBin(fFlags);
+#endif
+
+#ifdef SUP_HARDENED_SUID
+ /*
+ * Grab any options from the environment.
+ */
+ supR3GrabOptions();
+
+ /*
+ * Check that we're root, if we aren't then the installation is butchered.
+ */
+ g_uid = getuid();
+ g_gid = getgid();
+ if (geteuid() != 0 /* root */)
+ supR3HardenedFatalMsg("SUPR3HardenedMain", kSupInitOp_RootCheck, VERR_PERMISSION_DENIED,
+ "Effective UID is not root (euid=%d egid=%d uid=%d gid=%d)",
+ geteuid(), getegid(), g_uid, g_gid);
+#endif /* SUP_HARDENED_SUID */
+
+#ifdef RT_OS_WINDOWS
+ /*
+ * Windows: First respawn. On Windows we will respawn the process twice to establish
+ * something we can put some kind of reliable trust in. The first respawning aims
+ * at dropping compatibility layers and process "security" solutions.
+ */
+ if ( !g_fSupEarlyProcessInit
+ && !(fFlags & SUPSECMAIN_FLAGS_DONT_OPEN_DEV)
+ && supR3HardenedWinIsReSpawnNeeded(1 /*iWhich*/, argc, argv))
+ {
+ SUP_DPRINTF(("SUPR3HardenedMain: Respawn #1\n"));
+ supR3HardenedWinInit(SUPSECMAIN_FLAGS_DONT_OPEN_DEV | SUPSECMAIN_FLAGS_FIRST_PROCESS, false /*fAvastKludge*/);
+ supR3HardenedVerifyAll(true /* fFatal */, pszProgName, g_szSupLibHardenedExePath, fFlags);
+ return supR3HardenedWinReSpawn(1 /*iWhich*/);
+ }
+
+ /*
+ * Windows: Initialize the image verification global data so we can verify the
+ * signature of the process image and hook the core of the DLL loader API so we
+ * can check the signature of all DLLs mapped into the process. (Already done
+ * by early VM process init.)
+ */
+ if (!g_fSupEarlyProcessInit)
+ supR3HardenedWinInit(fFlags, true /*fAvastKludge*/);
+#endif /* RT_OS_WINDOWS */
+
+ /*
+ * Validate the installation.
+ */
+ supR3HardenedVerifyAll(true /* fFatal */, pszProgName, g_szSupLibHardenedExePath, fFlags);
+
+ /*
+ * The next steps are only taken if we actually need to access the support
+ * driver. (Already done by early process init.)
+ */
+ if (!(fFlags & SUPSECMAIN_FLAGS_DONT_OPEN_DEV))
+ {
+#ifdef RT_OS_WINDOWS
+ /*
+ * Windows: Must have done early process init if we get here.
+ */
+ if (!g_fSupEarlyProcessInit)
+ supR3HardenedFatalMsg("SUPR3HardenedMain", kSupInitOp_Integrity, VERR_WRONG_ORDER,
+ "Early process init was somehow skipped.");
+
+ /*
+ * Windows: The second respawn. This time we make a special arrangement
+ * with vboxdrv to monitor access to the new process from its inception.
+ */
+ if (supR3HardenedWinIsReSpawnNeeded(2 /* iWhich*/, argc, argv))
+ {
+ SUP_DPRINTF(("SUPR3HardenedMain: Respawn #2\n"));
+ return supR3HardenedWinReSpawn(2 /* iWhich*/);
+ }
+ SUP_DPRINTF(("SUPR3HardenedMain: Final process, opening VBoxDrv...\n"));
+ supR3HardenedWinFlushLoaderCache();
+
+#else
+ /*
+ * Open the vboxdrv device.
+ */
+ supR3HardenedMainOpenDevice();
+#endif /* !RT_OS_WINDOWS */
+ }
+
+#ifdef RT_OS_WINDOWS
+ /*
+ * Windows: Enable the use of windows APIs to verify images at load time.
+ */
+ supR3HardenedWinEnableThreadCreation();
+ supR3HardenedWinFlushLoaderCache();
+ supR3HardenedWinResolveVerifyTrustApiAndHookThreadCreation(g_pszSupLibHardenedProgName);
+ g_enmSupR3HardenedMainState = SUPR3HARDENEDMAINSTATE_WIN_VERIFY_TRUST_READY;
+#else /* !RT_OS_WINDOWS */
+# if defined(RT_OS_DARWIN)
+ supR3HardenedDarwinInit();
+# elif !defined(RT_OS_FREEBSD) /** @todo Portme. */
+ /*
+ * Posix: Hook the load library interface interface.
+ */
+ supR3HardenedPosixInit();
+# endif
+#endif /* !RT_OS_WINDOWS */
+
+#ifdef SUP_HARDENED_SUID
+ /*
+ * Grab additional capabilities / privileges.
+ */
+ supR3HardenedMainGrabCapabilites();
+
+ /*
+ * Drop any root privileges we might be holding (won't return on failure)
+ */
+ supR3HardenedMainDropPrivileges();
+#endif
+
+ /*
+ * Purge any environment variables and command line arguments considered harmful.
+ */
+ /** @todo May need to move this to a much earlier stage on windows. */
+ supR3HardenedMainPurgeEnvironment(envp);
+ supR3HardenedMainPurgeArgs(argc, argv, &argc, &argv);
+
+ /*
+ * Load the IPRT, hand the SUPLib part the open driver and
+ * call RTR3InitEx.
+ */
+ SUP_DPRINTF(("SUPR3HardenedMain: Load Runtime...\n"));
+ g_enmSupR3HardenedMainState = SUPR3HARDENEDMAINSTATE_INIT_RUNTIME;
+ supR3HardenedMainInitRuntime(fFlags);
+#ifdef RT_OS_WINDOWS
+ supR3HardenedWinModifyDllSearchPath(fFlags, g_szSupLibHardenedAppBinPath);
+#endif
+
+ /*
+ * Load the DLL/SO/DYLIB containing the actual program
+ * and pass control to it.
+ */
+ SUP_DPRINTF(("SUPR3HardenedMain: Load TrustedMain...\n"));
+ g_enmSupR3HardenedMainState = SUPR3HARDENEDMAINSTATE_GET_TRUSTED_MAIN;
+ PFNSUPTRUSTEDMAIN pfnTrustedMain = supR3HardenedMainGetTrustedMain(pszProgName, fFlags);
+
+ SUP_DPRINTF(("SUPR3HardenedMain: Calling TrustedMain (%p)...\n", pfnTrustedMain));
+ g_enmSupR3HardenedMainState = SUPR3HARDENEDMAINSTATE_CALLED_TRUSTED_MAIN;
+ return pfnTrustedMain(argc, argv, envp);
+}
+
diff --git a/src/VBox/HostDrivers/Support/SUPR3HardenedMainTemplate.cpp b/src/VBox/HostDrivers/Support/SUPR3HardenedMainTemplate.cpp
new file mode 100644
index 00000000..1c36e386
--- /dev/null
+++ b/src/VBox/HostDrivers/Support/SUPR3HardenedMainTemplate.cpp
@@ -0,0 +1,44 @@
+/* $Id: SUPR3HardenedMainTemplate.cpp $ */
+/** @file
+ * Hardened main() template.
+ */
+
+/*
+ * Copyright (C) 2008-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+#include <VBox/sup.h>
+
+
+int main(int argc, char **argv, char **envp)
+{
+ return SUPR3HardenedMain(PROGRAM_NAME_STR, 0, argc, argv, envp);
+}
+
diff --git a/src/VBox/HostDrivers/Support/SUPR3HardenedMainTemplateTestcase.cpp b/src/VBox/HostDrivers/Support/SUPR3HardenedMainTemplateTestcase.cpp
new file mode 100644
index 00000000..c01ea868
--- /dev/null
+++ b/src/VBox/HostDrivers/Support/SUPR3HardenedMainTemplateTestcase.cpp
@@ -0,0 +1,44 @@
+/* $Id: SUPR3HardenedMainTemplateTestcase.cpp $ */
+/** @file
+ * Hardened main() template for testcases (in testcase subdir).
+ */
+
+/*
+ * Copyright (C) 2008-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+#include <VBox/sup.h>
+
+
+int main(int argc, char **argv, char **envp)
+{
+ return SUPR3HardenedMain(PROGRAM_NAME_STR, SUPSECMAIN_FLAGS_LOC_TESTCASE, argc, argv, envp);
+}
+
diff --git a/src/VBox/HostDrivers/Support/SUPR3HardenedNoCrt.cpp b/src/VBox/HostDrivers/Support/SUPR3HardenedNoCrt.cpp
new file mode 100644
index 00000000..ce456e3b
--- /dev/null
+++ b/src/VBox/HostDrivers/Support/SUPR3HardenedNoCrt.cpp
@@ -0,0 +1,189 @@
+/* $Id: SUPR3HardenedNoCrt.cpp $ */
+/** @file
+ * VirtualBox Support Library - Hardened main() no-crt routines.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#if RT_OS_WINDOWS
+# include <iprt/win/windows.h>
+#endif
+
+#include <VBox/sup.h>
+
+#include "SUPLibInternal.h"
+
+
+#ifdef SUP_HARDENED_NEED_CRT_FUNCTIONS /** @todo this crap is obsolete. */
+
+/** memcmp */
+DECLHIDDEN(int) suplibHardenedMemComp(void const *pvDst, const void *pvSrc, size_t cbToComp)
+{
+ size_t const *puDst = (size_t const *)pvDst;
+ size_t const *puSrc = (size_t const *)pvSrc;
+ while (cbToComp >= sizeof(size_t))
+ {
+ if (*puDst != *puSrc)
+ break;
+ puDst++;
+ puSrc++;
+ cbToComp -= sizeof(size_t);
+ }
+
+ uint8_t const *pbDst = (uint8_t const *)puDst;
+ uint8_t const *pbSrc = (uint8_t const *)puSrc;
+ while (cbToComp > 0)
+ {
+ if (*pbDst != *pbSrc)
+ {
+ if (*pbDst < *pbSrc)
+ return -1;
+ return 1;
+ }
+
+ pbDst++;
+ pbSrc++;
+ cbToComp--;
+ }
+
+ return 0;
+}
+
+
+/** memcpy */
+DECLHIDDEN(void *) suplibHardenedMemCopy(void *pvDst, const void *pvSrc, size_t cbToCopy)
+{
+ size_t *puDst = (size_t *)pvDst;
+ size_t const *puSrc = (size_t const *)pvSrc;
+ while (cbToCopy >= sizeof(size_t))
+ {
+ *puDst++ = *puSrc++;
+ cbToCopy -= sizeof(size_t);
+ }
+
+ uint8_t *pbDst = (uint8_t *)puDst;
+ uint8_t const *pbSrc = (uint8_t const *)puSrc;
+ while (cbToCopy > 0)
+ {
+ *pbDst++ = *pbSrc++;
+ cbToCopy--;
+ }
+
+ return pvDst;
+}
+
+
+/** memset */
+DECLHIDDEN(void *) suplibHardenedMemSet(void *pvDst, int ch, size_t cbToSet)
+{
+ uint8_t *pbDst = (uint8_t *)pvDst;
+ while (cbToSet > 0)
+ {
+ *pbDst++ = (uint8_t)ch;
+ cbToSet--;
+ }
+
+ return pvDst;
+}
+
+
+/** strcpy */
+DECLHIDDEN(char *) suplibHardenedStrCopy(char *pszDst, const char *pszSrc)
+{
+ char *pszRet = pszDst;
+ char ch;
+ do
+ {
+ ch = *pszSrc++;
+ *pszDst++ = ch;
+ } while (ch);
+ return pszRet;
+}
+
+
+/** strlen */
+DECLHIDDEN(size_t) suplibHardenedStrLen(const char *psz)
+{
+ const char *pszStart = psz;
+ while (*psz)
+ psz++;
+ return psz - pszStart;
+}
+
+
+/** strcat */
+DECLHIDDEN(char *) suplibHardenedStrCat(char *pszDst, const char *pszSrc)
+{
+ char *pszRet = pszDst;
+ while (*pszDst)
+ pszDst++;
+ suplibHardenedStrCopy(pszDst, pszSrc);
+ return pszRet;
+}
+
+
+/** strcmp */
+DECLHIDDEN(int) suplibHardenedStrCmp(const char *psz1, const char *psz2)
+{
+ for (;;)
+ {
+ char ch1 = *psz1++;
+ char ch2 = *psz2++;
+ if (ch1 != ch2)
+ return ch1 < ch2 ? -1 : 1;
+ if (ch1 == 0)
+ return 0;
+ }
+}
+
+
+/** strncmp */
+DECLHIDDEN(int) suplibHardenedStrNCmp(const char *psz1, const char *psz2, size_t cchMax)
+{
+ while (cchMax-- > 0)
+ {
+ char ch1 = *psz1++;
+ char ch2 = *psz2++;
+ if (ch1 != ch2)
+ return ch1 < ch2 ? -1 : 1;
+ if (ch1 == 0)
+ break;
+ }
+ return 0;
+}
+
+#endif /* SUP_HARDENED_NEED_CRT_FUNCTIONS */
+
diff --git a/src/VBox/HostDrivers/Support/SUPR3HardenedVerify.cpp b/src/VBox/HostDrivers/Support/SUPR3HardenedVerify.cpp
new file mode 100644
index 00000000..e78a397c
--- /dev/null
+++ b/src/VBox/HostDrivers/Support/SUPR3HardenedVerify.cpp
@@ -0,0 +1,2149 @@
+/* $Id: SUPR3HardenedVerify.cpp $ */
+/** @file
+ * VirtualBox Support Library - Verification of Hardened Installation.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#if defined(RT_OS_OS2)
+# define INCL_BASE
+# define INCL_ERRORS
+# include <os2.h>
+# include <stdio.h>
+# include <stdlib.h>
+# include <unistd.h>
+# include <sys/fcntl.h>
+# include <sys/errno.h>
+# include <sys/syslimits.h>
+
+#elif defined(RT_OS_WINDOWS)
+# include <iprt/nt/nt-and-windows.h>
+# ifndef IN_SUP_HARDENED_R3
+# include <stdio.h>
+# endif
+
+#else /* UNIXes */
+# include <sys/types.h>
+# include <stdio.h>
+# include <stdlib.h>
+# include <dirent.h>
+# include <dlfcn.h>
+# include <fcntl.h>
+# include <limits.h>
+# include <errno.h>
+# include <unistd.h>
+# include <sys/stat.h>
+# include <sys/time.h>
+# include <sys/fcntl.h>
+# include <pwd.h>
+# ifdef RT_OS_DARWIN
+# include <mach-o/dyld.h>
+# endif
+
+#endif
+
+#include <VBox/sup.h>
+#include <VBox/err.h>
+#include <iprt/asm.h>
+#include <iprt/ctype.h>
+#include <iprt/param.h>
+#include <iprt/path.h>
+#include <iprt/string.h>
+#include <iprt/utf16.h>
+
+#include "SUPLibInternal.h"
+#if defined(RT_OS_WINDOWS) && defined(VBOX_WITH_HARDENING)
+# define SUPHNTVI_NO_NT_STUFF
+# include "win/SUPHardenedVerify-win.h"
+#endif
+
+
+/*********************************************************************************************************************************
+* Defined Constants And Macros *
+*********************************************************************************************************************************/
+/** The max path length acceptable for a trusted path. */
+#define SUPR3HARDENED_MAX_PATH 260U
+
+/** Enable to resolve symlinks using realpath() instead of cooking our own stuff. */
+#define SUP_HARDENED_VERIFY_FOLLOW_SYMLINKS_USE_REALPATH 1
+
+#ifdef RT_OS_SOLARIS
+# define dirfd(d) ((d)->d_fd)
+#endif
+
+/** Compare table file names with externally supplied names. */
+#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
+# define SUP_COMP_FILENAME RTStrICmp
+#else
+# define SUP_COMP_FILENAME suplibHardenedStrCmp
+#endif
+
+
+/*********************************************************************************************************************************
+* Global Variables *
+*********************************************************************************************************************************/
+/**
+ * The files that gets verified.
+ *
+ * @todo This needs reviewing against the linux packages.
+ * @todo The excessive use of kSupID_AppSharedLib needs to be reviewed at some point. For
+ * the time being we're building the linux packages with SharedLib pointing to
+ * AppPrivArch (lazy bird).
+ *
+ * @remarks If you add executables here, you might need to update
+ * g_apszSupNtVpAllowedVmExes in SUPHardenedVerifyProcess-win.cpp.
+ */
+static SUPINSTFILE const g_aSupInstallFiles[] =
+{
+ /* type, dir, fOpt, "pszFile" */
+ /* ---------------------------------------------------------------------- */
+ { kSupIFT_Dll, kSupID_AppPrivArch, false, "VMMR0.r0" },
+ { kSupIFT_Dll, kSupID_AppPrivArch, false, "VBoxDDR0.r0" },
+
+#ifdef VBOX_WITH_RAW_MODE
+ { kSupIFT_Rc, kSupID_AppPrivArch, false, "VMMRC.rc" },
+ { kSupIFT_Rc, kSupID_AppPrivArch, false, "VBoxDDRC.rc" },
+#endif
+
+ { kSupIFT_Dll, kSupID_AppSharedLib, false, "VBoxRT" SUPLIB_DLL_SUFF },
+ { kSupIFT_Dll, kSupID_AppSharedLib, false, "VBoxVMM" SUPLIB_DLL_SUFF },
+#if HC_ARCH_BITS == 32
+ { kSupIFT_Dll, kSupID_AppSharedLib, true, "VBoxREM32" SUPLIB_DLL_SUFF },
+ { kSupIFT_Dll, kSupID_AppSharedLib, true, "VBoxREM64" SUPLIB_DLL_SUFF },
+#endif
+ { kSupIFT_Dll, kSupID_AppSharedLib, false, "VBoxDD" SUPLIB_DLL_SUFF },
+ { kSupIFT_Dll, kSupID_AppSharedLib, false, "VBoxDD2" SUPLIB_DLL_SUFF },
+ { kSupIFT_Dll, kSupID_AppSharedLib, false, "VBoxDDU" SUPLIB_DLL_SUFF },
+ { kSupIFT_Exe, kSupID_AppBin, true, "VBoxVMMPreload" SUPLIB_EXE_SUFF },
+ { kSupIFT_Dll, kSupID_AppPrivArch, true, "VBoxVMMPreload" SUPLIB_DLL_SUFF },
+
+//#ifdef VBOX_WITH_DEBUGGER_GUI
+ { kSupIFT_Dll, kSupID_AppSharedLib, true, "VBoxDbg" SUPLIB_DLL_SUFF },
+ { kSupIFT_Dll, kSupID_AppSharedLib, true, "VBoxDbg3" SUPLIB_DLL_SUFF },
+//#endif
+
+//#ifdef VBOX_WITH_SHARED_CLIPBOARD
+ { kSupIFT_Dll, kSupID_AppPrivArch, true, "VBoxSharedClipboard" SUPLIB_DLL_SUFF },
+//#endif
+//#ifdef VBOX_WITH_SHARED_FOLDERS
+ { kSupIFT_Dll, kSupID_AppPrivArch, true, "VBoxSharedFolders" SUPLIB_DLL_SUFF },
+//#endif
+//#ifdef VBOX_WITH_DRAG_AND_DROP
+ { kSupIFT_Dll, kSupID_AppPrivArch, true, "VBoxDragAndDropSvc" SUPLIB_DLL_SUFF },
+//#endif
+//#ifdef VBOX_WITH_GUEST_PROPS
+ { kSupIFT_Dll, kSupID_AppPrivArch, true, "VBoxGuestPropSvc" SUPLIB_DLL_SUFF },
+//#endif
+//#ifdef VBOX_WITH_GUEST_CONTROL
+ { kSupIFT_Dll, kSupID_AppPrivArch, true, "VBoxGuestControlSvc" SUPLIB_DLL_SUFF },
+//#endif
+ { kSupIFT_Dll, kSupID_AppPrivArch, true, "VBoxHostChannel" SUPLIB_DLL_SUFF },
+ { kSupIFT_Dll, kSupID_AppPrivArch, true, "VBoxSharedCrOpenGL" SUPLIB_DLL_SUFF },
+ { kSupIFT_Dll, kSupID_AppPrivArch, true, "VBoxOGLhostcrutil" SUPLIB_DLL_SUFF },
+ { kSupIFT_Dll, kSupID_AppPrivArch, true, "VBoxOGLhosterrorspu" SUPLIB_DLL_SUFF },
+ { kSupIFT_Dll, kSupID_AppPrivArch, true, "VBoxOGLrenderspu" SUPLIB_DLL_SUFF },
+
+ { kSupIFT_Exe, kSupID_AppBin, true, "VBoxManage" SUPLIB_EXE_SUFF },
+
+#ifdef VBOX_WITH_MAIN
+ { kSupIFT_Exe, kSupID_AppBin, false, "VBoxSVC" SUPLIB_EXE_SUFF },
+ #ifdef RT_OS_WINDOWS
+ { kSupIFT_Dll, kSupID_AppSharedLib, false, "VBoxC" SUPLIB_DLL_SUFF },
+ #else
+ { kSupIFT_Exe, kSupID_AppPrivArch, false, "VBoxXPCOMIPCD" SUPLIB_EXE_SUFF },
+ { kSupIFT_Dll, kSupID_AppSharedLib, false, "VBoxXPCOM" SUPLIB_DLL_SUFF },
+ { kSupIFT_Dll, kSupID_AppPrivArchComp, false, "VBoxXPCOMIPCC" SUPLIB_DLL_SUFF },
+ { kSupIFT_Dll, kSupID_AppPrivArchComp, false, "VBoxC" SUPLIB_DLL_SUFF },
+ { kSupIFT_Dll, kSupID_AppPrivArchComp, false, "VBoxSVCM" SUPLIB_DLL_SUFF },
+ { kSupIFT_Data, kSupID_AppPrivArchComp, false, "VBoxXPCOMBase.xpt" },
+ #endif
+#endif
+
+ { kSupIFT_Dll, kSupID_AppSharedLib, true, "VRDPAuth" SUPLIB_DLL_SUFF },
+ { kSupIFT_Dll, kSupID_AppSharedLib, true, "VBoxAuth" SUPLIB_DLL_SUFF },
+ { kSupIFT_Dll, kSupID_AppSharedLib, true, "VBoxVRDP" SUPLIB_DLL_SUFF },
+
+//#ifdef VBOX_WITH_HEADLESS
+ { kSupIFT_Exe, kSupID_AppBin, true, "VBoxHeadless" SUPLIB_EXE_SUFF },
+ { kSupIFT_Dll, kSupID_AppPrivArch, true, "VBoxHeadless" SUPLIB_DLL_SUFF },
+ { kSupIFT_Dll, kSupID_AppPrivArch, true, "VBoxVideoRecFB" SUPLIB_DLL_SUFF },
+//#endif
+
+//#ifdef VBOX_WITH_QTGUI
+ { kSupIFT_Exe, kSupID_AppBin, true, "VirtualBox" SUPLIB_EXE_SUFF },
+# ifdef RT_OS_DARWIN
+ { kSupIFT_Exe, kSupID_AppMacHelper, true, "VirtualBoxVM" SUPLIB_EXE_SUFF },
+# else
+ { kSupIFT_Exe, kSupID_AppBin, true, "VirtualBoxVM" SUPLIB_EXE_SUFF },
+# endif
+ { kSupIFT_Dll, kSupID_AppPrivArch, true, "VirtualBoxVM" SUPLIB_DLL_SUFF },
+ { kSupIFT_Dll, kSupID_AppPrivArch, true, "UICommon" SUPLIB_DLL_SUFF },
+# if !defined(RT_OS_DARWIN) && !defined(RT_OS_WINDOWS) && !defined(RT_OS_OS2)
+ { kSupIFT_Dll, kSupID_AppSharedLib, true, "VBoxKeyboard" SUPLIB_DLL_SUFF },
+# endif
+//#endif
+
+//#ifdef VBOX_WITH_VBOXSDL
+ { kSupIFT_Exe, kSupID_AppBin, true, "VBoxSDL" SUPLIB_EXE_SUFF },
+ { kSupIFT_Dll, kSupID_AppPrivArch, true, "VBoxSDL" SUPLIB_DLL_SUFF },
+//#endif
+
+//#ifdef VBOX_WITH_WEBSERVICES
+ { kSupIFT_Exe, kSupID_AppBin, true, "vboxwebsrv" SUPLIB_EXE_SUFF },
+//#endif
+
+#ifdef RT_OS_LINUX
+ { kSupIFT_Exe, kSupID_AppBin, true, "VBoxTunctl" SUPLIB_EXE_SUFF },
+#endif
+
+//#ifdef VBOX_WITH_NETFLT
+ { kSupIFT_Exe, kSupID_AppBin, true, "VBoxNetDHCP" SUPLIB_EXE_SUFF },
+ { kSupIFT_Dll, kSupID_AppPrivArch, true, "VBoxNetDHCP" SUPLIB_DLL_SUFF },
+//#endif
+
+//#ifdef VBOX_WITH_LWIP_NAT
+ { kSupIFT_Exe, kSupID_AppBin, true, "VBoxNetNAT" SUPLIB_EXE_SUFF },
+ { kSupIFT_Dll, kSupID_AppPrivArch, true, "VBoxNetNAT" SUPLIB_DLL_SUFF },
+//#endif
+#if defined(VBOX_WITH_HARDENING) && defined(RT_OS_WINDOWS)
+# define HARDENED_TESTCASE_BIN_ENTRY(a_szName) \
+ { kSupIFT_TestExe, kSupID_AppBin, true, a_szName SUPLIB_EXE_SUFF }, \
+ { kSupIFT_TestDll, kSupID_AppBin, true, a_szName SUPLIB_DLL_SUFF }
+ HARDENED_TESTCASE_BIN_ENTRY("tstMicro"),
+ HARDENED_TESTCASE_BIN_ENTRY("tstPDMAsyncCompletion"),
+ HARDENED_TESTCASE_BIN_ENTRY("tstPDMAsyncCompletionStress"),
+ HARDENED_TESTCASE_BIN_ENTRY("tstVMM"),
+ HARDENED_TESTCASE_BIN_ENTRY("tstVMREQ"),
+# define HARDENED_TESTCASE_ENTRY(a_szName) \
+ { kSupIFT_TestExe, kSupID_Testcase, true, a_szName SUPLIB_EXE_SUFF }, \
+ { kSupIFT_TestDll, kSupID_Testcase, true, a_szName SUPLIB_DLL_SUFF }
+ HARDENED_TESTCASE_ENTRY("tstCFGM"),
+ HARDENED_TESTCASE_ENTRY("tstGIP-2"),
+ HARDENED_TESTCASE_ENTRY("tstIntNet-1"),
+ HARDENED_TESTCASE_ENTRY("tstMMHyperHeap"),
+ HARDENED_TESTCASE_ENTRY("tstRTR0ThreadPreemptionDriver"),
+ HARDENED_TESTCASE_ENTRY("tstRTR0MemUserKernelDriver"),
+ HARDENED_TESTCASE_ENTRY("tstRTR0SemMutexDriver"),
+ HARDENED_TESTCASE_ENTRY("tstRTR0TimerDriver"),
+ HARDENED_TESTCASE_ENTRY("tstSSM"),
+#endif
+};
+
+
+/** Array parallel to g_aSupInstallFiles containing per-file status info. */
+static SUPVERIFIEDFILE g_aSupVerifiedFiles[RT_ELEMENTS(g_aSupInstallFiles)];
+
+/** Array index by install directory specifier containing info about verified directories. */
+static SUPVERIFIEDDIR g_aSupVerifiedDirs[kSupID_End];
+
+
+/**
+ * Assembles the path to a directory.
+ *
+ * @returns VINF_SUCCESS on success, some error code on failure (fFatal
+ * decides whether it returns or not).
+ *
+ * @param enmDir The directory.
+ * @param pszDst Where to assemble the path.
+ * @param cchDst The size of the buffer.
+ * @param fFatal Whether failures should be treated as fatal (true) or not (false).
+ * @param pFile The file (for darwin helper app paths).
+ */
+static int supR3HardenedMakePath(SUPINSTDIR enmDir, char *pszDst, size_t cchDst, bool fFatal, PCSUPINSTFILE pFile)
+{
+ int rc;
+ switch (enmDir)
+ {
+ case kSupID_AppBin:
+ rc = supR3HardenedPathAppBin(pszDst, cchDst);
+ break;
+ case kSupID_AppSharedLib:
+ rc = supR3HardenedPathAppSharedLibs(pszDst, cchDst);
+ break;
+ case kSupID_AppPrivArch:
+ rc = supR3HardenedPathAppPrivateArch(pszDst, cchDst);
+ break;
+ case kSupID_AppPrivArchComp:
+ rc = supR3HardenedPathAppPrivateArch(pszDst, cchDst);
+ if (RT_SUCCESS(rc))
+ {
+ size_t off = suplibHardenedStrLen(pszDst);
+ if (cchDst - off >= sizeof("/components"))
+ suplibHardenedMemCopy(&pszDst[off], "/components", sizeof("/components"));
+ else
+ rc = VERR_BUFFER_OVERFLOW;
+ }
+ break;
+ case kSupID_AppPrivNoArch:
+ rc = supR3HardenedPathAppPrivateNoArch(pszDst, cchDst);
+ break;
+ case kSupID_Testcase:
+ rc = supR3HardenedPathAppBin(pszDst, cchDst);
+ if (RT_SUCCESS(rc))
+ {
+ size_t off = suplibHardenedStrLen(pszDst);
+ if (cchDst - off >= sizeof("/testcase"))
+ suplibHardenedMemCopy(&pszDst[off], "/testcase", sizeof("/testcase"));
+ else
+ rc = VERR_BUFFER_OVERFLOW;
+ }
+ break;
+#ifdef RT_OS_DARWIN
+ case kSupID_AppMacHelper:
+ rc = supR3HardenedPathAppBin(pszDst, cchDst);
+ if (RT_SUCCESS(rc))
+ {
+ /* Up one level from the VirtualBox.app/Contents/MacOS directory: */
+ size_t offDst = suplibHardenedStrLen(pszDst);
+ while (offDst > 1 && pszDst[offDst - 1] == '/')
+ offDst--;
+ while (offDst > 1 && pszDst[offDst - 1] != '/')
+ offDst--;
+
+ /* Construct the path to the helper application's Contents/MacOS directory: */
+ size_t cchFile = suplibHardenedStrLen(pFile->pszFile);
+ if (offDst + cchFile + sizeof("Resources/.app/Contents/MacOS") <= cchDst)
+ {
+ suplibHardenedMemCopy(&pszDst[offDst], RT_STR_TUPLE("Resources/"));
+ offDst += sizeof("Resources/") - 1;
+ suplibHardenedMemCopy(&pszDst[offDst], pFile->pszFile, cchFile);
+ offDst += cchFile;
+ suplibHardenedMemCopy(&pszDst[offDst], RT_STR_TUPLE(".app/Contents/MacOS") + 1);
+ }
+ else
+ rc = VERR_BUFFER_OVERFLOW;
+ }
+ break;
+#endif
+ default:
+ return supR3HardenedError(VERR_INTERNAL_ERROR, fFatal,
+ "supR3HardenedMakePath: enmDir=%d\n", enmDir);
+ }
+ if (RT_FAILURE(rc))
+ supR3HardenedError(rc, fFatal,
+ "supR3HardenedMakePath: enmDir=%d rc=%d\n", enmDir, rc);
+ NOREF(pFile);
+ return rc;
+}
+
+
+
+/**
+ * Assembles the path to a file table entry, with or without the actual filename.
+ *
+ * @returns VINF_SUCCESS on success, some error code on failure (fFatal
+ * decides whether it returns or not).
+ *
+ * @param pFile The file table entry.
+ * @param pszDst Where to assemble the path.
+ * @param cchDst The size of the buffer.
+ * @param fWithFilename If set, the filename is included, otherwise it is omitted (no trailing slash).
+ * @param fFatal Whether failures should be treated as fatal (true) or not (false).
+ */
+static int supR3HardenedMakeFilePath(PCSUPINSTFILE pFile, char *pszDst, size_t cchDst, bool fWithFilename, bool fFatal)
+{
+ /*
+ * Combine supR3HardenedMakePath and the filename.
+ */
+ int rc = supR3HardenedMakePath(pFile->enmDir, pszDst, cchDst, fFatal, pFile);
+ if (RT_SUCCESS(rc) && fWithFilename)
+ {
+ size_t cchFile = suplibHardenedStrLen(pFile->pszFile);
+ size_t off = suplibHardenedStrLen(pszDst);
+ if (cchDst - off >= cchFile + 2)
+ {
+ pszDst[off++] = '/';
+ suplibHardenedMemCopy(&pszDst[off], pFile->pszFile, cchFile + 1);
+ }
+ else
+ rc = supR3HardenedError(VERR_BUFFER_OVERFLOW, fFatal,
+ "supR3HardenedMakeFilePath: pszFile=%s off=%lu\n",
+ pFile->pszFile, (long)off);
+ }
+ return rc;
+}
+
+
+/**
+ * Verifies a directory.
+ *
+ * @returns VINF_SUCCESS on success. On failure, an error code is returned if
+ * fFatal is clear and if it's set the function wont return.
+ * @param enmDir The directory specifier.
+ * @param fFatal Whether validation failures should be treated as
+ * fatal (true) or not (false).
+ * @param pFile The file (for darwin helper app paths).
+ */
+DECLHIDDEN(int) supR3HardenedVerifyFixedDir(SUPINSTDIR enmDir, bool fFatal, PCSUPINSTFILE pFile)
+{
+ /*
+ * Validate the index just to be on the safe side...
+ */
+ if (enmDir <= kSupID_Invalid || enmDir >= kSupID_End)
+ return supR3HardenedError(VERR_INTERNAL_ERROR, fFatal,
+ "supR3HardenedVerifyDir: enmDir=%d\n", enmDir);
+
+ /*
+ * Already validated?
+ */
+ if (g_aSupVerifiedDirs[enmDir].fValidated)
+ return VINF_SUCCESS; /** @todo revalidate? */
+
+ /* initialize the entry. */
+ if (g_aSupVerifiedDirs[enmDir].hDir != 0)
+ supR3HardenedError(VERR_INTERNAL_ERROR, fFatal,
+ "supR3HardenedVerifyDir: hDir=%p enmDir=%d\n",
+ (void *)g_aSupVerifiedDirs[enmDir].hDir, enmDir);
+ g_aSupVerifiedDirs[enmDir].hDir = -1;
+ g_aSupVerifiedDirs[enmDir].fValidated = false;
+
+ /*
+ * Make the path and open the directory.
+ */
+ char szPath[RTPATH_MAX];
+ int rc = supR3HardenedMakePath(enmDir, szPath, sizeof(szPath), fFatal, pFile);
+ if (RT_SUCCESS(rc))
+ {
+#if defined(RT_OS_WINDOWS)
+ PRTUTF16 pwszPath;
+ rc = RTStrToUtf16(szPath, &pwszPath);
+ if (RT_SUCCESS(rc))
+ {
+ HANDLE hDir = CreateFileW(pwszPath,
+ GENERIC_READ,
+ FILE_SHARE_READ | FILE_SHARE_DELETE | FILE_SHARE_WRITE,
+ NULL,
+ OPEN_EXISTING,
+ FILE_ATTRIBUTE_NORMAL | FILE_FLAG_BACKUP_SEMANTICS,
+ NULL);
+ if (hDir != INVALID_HANDLE_VALUE)
+ {
+ /** @todo check the type */
+ /* That's all on windows, for now at least... */
+ g_aSupVerifiedDirs[enmDir].hDir = (intptr_t)hDir;
+ g_aSupVerifiedDirs[enmDir].fValidated = true;
+ }
+ else if (enmDir == kSupID_Testcase)
+ {
+ g_aSupVerifiedDirs[enmDir].fValidated = true;
+ rc = VINF_SUCCESS; /* Optional directory, ignore if missing. */
+ }
+ else
+ {
+ int err = RtlGetLastWin32Error();
+ rc = supR3HardenedError(VERR_PATH_NOT_FOUND, fFatal,
+ "supR3HardenedVerifyDir: Failed to open \"%s\": err=%d\n",
+ szPath, err);
+ }
+ RTUtf16Free(pwszPath);
+ }
+ else
+ rc = supR3HardenedError(rc, fFatal,
+ "supR3HardenedVerifyDir: Failed to convert \"%s\" to UTF-16: err=%d\n", szPath, rc);
+
+#else /* UNIXY */
+ int fd = open(szPath, O_RDONLY, 0);
+ if (fd >= 0)
+ {
+ /*
+ * On unixy systems we'll make sure the directory is owned by root
+ * and not writable by the group and user.
+ */
+ struct stat st;
+ if (!fstat(fd, &st))
+ {
+
+ if ( st.st_uid == 0
+ && !(st.st_mode & (S_IWGRP | S_IWOTH))
+ && S_ISDIR(st.st_mode))
+ {
+ g_aSupVerifiedDirs[enmDir].hDir = fd;
+ g_aSupVerifiedDirs[enmDir].fValidated = true;
+ }
+ else
+ {
+ if (!S_ISDIR(st.st_mode))
+ rc = supR3HardenedError(VERR_NOT_A_DIRECTORY, fFatal,
+ "supR3HardenedVerifyDir: \"%s\" is not a directory\n",
+ szPath, (long)st.st_uid);
+ else if (st.st_uid)
+ rc = supR3HardenedError(VERR_ACCESS_DENIED, fFatal,
+ "supR3HardenedVerifyDir: Cannot trust the directory \"%s\": not owned by root (st_uid=%ld)\n",
+ szPath, (long)st.st_uid);
+ else
+ rc = supR3HardenedError(VERR_ACCESS_DENIED, fFatal,
+ "supR3HardenedVerifyDir: Cannot trust the directory \"%s\": group and/or other writable (st_mode=0%lo)\n",
+ szPath, (long)st.st_mode);
+ close(fd);
+ }
+ }
+ else
+ {
+ int err = errno;
+ rc = supR3HardenedError(VERR_ACCESS_DENIED, fFatal,
+ "supR3HardenedVerifyDir: Failed to fstat \"%s\": %s (%d)\n",
+ szPath, strerror(err), err);
+ close(fd);
+ }
+ }
+ else if (enmDir == kSupID_Testcase)
+ {
+ g_aSupVerifiedDirs[enmDir].fValidated = true;
+ rc = VINF_SUCCESS; /* Optional directory, ignore if missing. */
+ }
+ else
+ {
+ int err = errno;
+ rc = supR3HardenedError(VERR_PATH_NOT_FOUND, fFatal,
+ "supR3HardenedVerifyDir: Failed to open \"%s\": %s (%d)\n",
+ szPath, strerror(err), err);
+ }
+#endif /* UNIXY */
+ }
+
+ return rc;
+}
+
+
+#ifdef RT_OS_WINDOWS
+/**
+ * Opens the file for verification.
+ *
+ * @returns VINF_SUCCESS on success. On failure, an error code is returned if
+ * fFatal is clear and if it's set the function wont return.
+ * @param pFile The file entry.
+ * @param fFatal Whether validation failures should be treated as
+ * kl fatal (true) or not (false).
+ * @param phFile The file handle, set to -1 if we failed to open
+ * the file. The function may return VINF_SUCCESS
+ * and a -1 handle if the file is optional.
+ */
+static int supR3HardenedVerifyFileOpen(PCSUPINSTFILE pFile, bool fFatal, intptr_t *phFile)
+{
+ *phFile = -1;
+
+ char szPath[RTPATH_MAX];
+ int rc = supR3HardenedMakeFilePath(pFile, szPath, sizeof(szPath), true /*fWithFilename*/, fFatal);
+ if (RT_SUCCESS(rc))
+ {
+ PRTUTF16 pwszPath;
+ rc = RTStrToUtf16(szPath, &pwszPath);
+ if (RT_SUCCESS(rc))
+ {
+ HANDLE hFile = CreateFileW(pwszPath,
+ GENERIC_READ,
+ FILE_SHARE_READ,
+ NULL,
+ OPEN_EXISTING,
+ FILE_ATTRIBUTE_NORMAL,
+ NULL);
+ if (hFile != INVALID_HANDLE_VALUE)
+ {
+ *phFile = (intptr_t)hFile;
+ rc = VINF_SUCCESS;
+ }
+ else
+ {
+ int err = RtlGetLastWin32Error();
+ if ( !pFile->fOptional
+ || ( err != ERROR_FILE_NOT_FOUND
+ && (err != ERROR_PATH_NOT_FOUND || pFile->enmDir != kSupID_Testcase) ) )
+ rc = supR3HardenedError(VERR_PATH_NOT_FOUND, fFatal,
+ "supR3HardenedVerifyFileInternal: Failed to open '%s': err=%d\n", szPath, err);
+ }
+ RTUtf16Free(pwszPath);
+ }
+ else
+ rc = supR3HardenedError(rc, fFatal, "supR3HardenedVerifyFileInternal: Failed to convert '%s' to UTF-16: %Rrc\n",
+ szPath, rc);
+ }
+ return rc;
+}
+
+
+/**
+ * Worker for supR3HardenedVerifyFileInternal.
+ *
+ * @returns VINF_SUCCESS on success. On failure, an error code is returned if
+ * fFatal is clear and if it's set the function wont return.
+ * @param pFile The file entry.
+ * @param pVerified The verification record.
+ * @param fFatal Whether validation failures should be treated as
+ * fatal (true) or not (false).
+ * @param fLeaveFileOpen Whether the file should be left open.
+ */
+static int supR3HardenedVerifyFileSignature(PCSUPINSTFILE pFile, PSUPVERIFIEDFILE pVerified, bool fFatal, bool fLeaveFileOpen)
+{
+# if defined(VBOX_WITH_HARDENING) && !defined(IN_SUP_R3_STATIC) /* Latter: Not in VBoxCpuReport and friends. */
+
+ /*
+ * Open the file if we have to.
+ */
+ int rc;
+ intptr_t hFileOpened;
+ intptr_t hFile = pVerified->hFile;
+ if (hFile != -1)
+ hFileOpened = -1;
+ else
+ {
+ rc = supR3HardenedVerifyFileOpen(pFile, fFatal, &hFileOpened);
+ if (RT_FAILURE(rc))
+ return rc;
+ hFile = hFileOpened;
+ }
+
+ /*
+ * Verify the signature.
+ */
+ char szErr[1024];
+ RTERRINFO ErrInfo;
+ RTErrInfoInit(&ErrInfo, szErr, sizeof(szErr));
+
+ uint32_t fFlags = SUPHNTVI_F_REQUIRE_BUILD_CERT;
+ if (pFile->enmType == kSupIFT_Rc)
+ fFlags |= SUPHNTVI_F_RC_IMAGE;
+
+ rc = supHardenedWinVerifyImageByHandleNoName((HANDLE)hFile, fFlags, &ErrInfo);
+ if (RT_SUCCESS(rc))
+ pVerified->fCheckedSignature = true;
+ else
+ {
+ pVerified->fCheckedSignature = false;
+ rc = supR3HardenedError(rc, fFatal, "supR3HardenedVerifyFileInternal: '%s': Image verify error rc=%Rrc: %s\n",
+ pFile->pszFile, rc, szErr);
+
+ }
+
+ /*
+ * Close the handle if we opened the file and we should close it.
+ */
+ if (hFileOpened != -1)
+ {
+ if (fLeaveFileOpen && RT_SUCCESS(rc))
+ pVerified->hFile = hFileOpened;
+ else
+ NtClose((HANDLE)hFileOpened);
+ }
+
+ return rc;
+
+# else /* Not checking signatures. */
+ RT_NOREF4(pFile, pVerified, fFatal, fLeaveFileOpen);
+ return VINF_SUCCESS;
+# endif /* Not checking signatures. */
+}
+#endif
+
+
+/**
+ * Verifies a file entry.
+ *
+ * @returns VINF_SUCCESS on success. On failure, an error code is returned if
+ * fFatal is clear and if it's set the function wont return.
+ *
+ * @param iFile The file table index of the file to be verified.
+ * @param fFatal Whether validation failures should be treated as
+ * fatal (true) or not (false).
+ * @param fLeaveFileOpen Whether the file should be left open.
+ * @param fVerifyAll Set if this is an verify all call and we will
+ * postpone signature checking.
+ */
+static int supR3HardenedVerifyFileInternal(int iFile, bool fFatal, bool fLeaveFileOpen, bool fVerifyAll)
+{
+#ifndef RT_OS_WINDOWS
+ RT_NOREF1(fVerifyAll);
+#endif
+ PCSUPINSTFILE pFile = &g_aSupInstallFiles[iFile];
+ PSUPVERIFIEDFILE pVerified = &g_aSupVerifiedFiles[iFile];
+
+ /*
+ * Already done validation? Do signature validation if we haven't yet.
+ */
+ if (pVerified->fValidated)
+ {
+ /** @todo revalidate? Check that the file hasn't been replace or similar. */
+#ifdef RT_OS_WINDOWS
+ if (!pVerified->fCheckedSignature && !fVerifyAll)
+ return supR3HardenedVerifyFileSignature(pFile, pVerified, fFatal, fLeaveFileOpen);
+#endif
+ return VINF_SUCCESS;
+ }
+
+
+ /* initialize the entry. */
+ if (pVerified->hFile != 0)
+ supR3HardenedError(VERR_INTERNAL_ERROR, fFatal,
+ "supR3HardenedVerifyFileInternal: hFile=%p (%s)\n",
+ (void *)pVerified->hFile, pFile->pszFile);
+ pVerified->hFile = -1;
+ pVerified->fValidated = false;
+#ifdef RT_OS_WINDOWS
+ pVerified->fCheckedSignature = false;
+#endif
+
+ /*
+ * Verify the directory then proceed to open it.
+ * (This'll make sure the directory is opened and that we can (later)
+ * use openat if we wish.)
+ */
+ int rc = supR3HardenedVerifyFixedDir(pFile->enmDir, fFatal, pFile);
+ if (RT_SUCCESS(rc))
+ {
+#if defined(RT_OS_WINDOWS)
+ rc = supR3HardenedVerifyFileOpen(pFile, fFatal, &pVerified->hFile);
+ if (RT_SUCCESS(rc))
+ {
+ if (!fVerifyAll)
+ rc = supR3HardenedVerifyFileSignature(pFile, pVerified, fFatal, fLeaveFileOpen);
+ if (RT_SUCCESS(rc))
+ {
+ pVerified->fValidated = true;
+ if (!fLeaveFileOpen)
+ {
+ NtClose((HANDLE)pVerified->hFile);
+ pVerified->hFile = -1;
+ }
+ }
+ }
+#else /* !RT_OS_WINDOWS */
+ char szPath[RTPATH_MAX];
+ rc = supR3HardenedMakeFilePath(pFile, szPath, sizeof(szPath), true /*fWithFilename*/, fFatal);
+ if (RT_SUCCESS(rc))
+ {
+ int fd = open(szPath, O_RDONLY, 0);
+ if (fd >= 0)
+ {
+ /*
+ * On unixy systems we'll make sure the file is owned by root
+ * and not writable by the group and user.
+ */
+ struct stat st;
+ if (!fstat(fd, &st))
+ {
+ if ( st.st_uid == 0
+ && !(st.st_mode & (S_IWGRP | S_IWOTH))
+ && S_ISREG(st.st_mode))
+ {
+ /* it's valid. */
+ if (fLeaveFileOpen)
+ pVerified->hFile = fd;
+ else
+ close(fd);
+ pVerified->fValidated = true;
+ }
+ else
+ {
+ if (!S_ISREG(st.st_mode))
+ rc = supR3HardenedError(VERR_IS_A_DIRECTORY, fFatal,
+ "supR3HardenedVerifyFileInternal: \"%s\" is not a regular file\n",
+ szPath, (long)st.st_uid);
+ else if (st.st_uid)
+ rc = supR3HardenedError(VERR_ACCESS_DENIED, fFatal,
+ "supR3HardenedVerifyFileInternal: Cannot trust the file \"%s\": not owned by root (st_uid=%ld)\n",
+ szPath, (long)st.st_uid);
+ else
+ rc = supR3HardenedError(VERR_ACCESS_DENIED, fFatal,
+ "supR3HardenedVerifyFileInternal: Cannot trust the file \"%s\": group and/or other writable (st_mode=0%lo)\n",
+ szPath, (long)st.st_mode);
+ close(fd);
+ }
+ }
+ else
+ {
+ int err = errno;
+ rc = supR3HardenedError(VERR_ACCESS_DENIED, fFatal,
+ "supR3HardenedVerifyFileInternal: Failed to fstat \"%s\": %s (%d)\n",
+ szPath, strerror(err), err);
+ close(fd);
+ }
+ }
+ else
+ {
+ int err = errno;
+ if (!pFile->fOptional || err != ENOENT)
+ rc = supR3HardenedError(VERR_PATH_NOT_FOUND, fFatal,
+ "supR3HardenedVerifyFileInternal: Failed to open \"%s\": %s (%d)\n",
+ szPath, strerror(err), err);
+ }
+ }
+#endif /* !RT_OS_WINDOWS */
+ }
+
+ return rc;
+}
+
+
+/**
+ * Verifies that the specified table entry matches the given filename.
+ *
+ * @returns VINF_SUCCESS if matching. On mismatch fFatal indicates whether an
+ * error is returned or we terminate the application.
+ *
+ * @param iFile The file table index.
+ * @param pszFilename The filename.
+ * @param fFatal Whether validation failures should be treated as
+ * fatal (true) or not (false).
+ */
+static int supR3HardenedVerifySameFile(int iFile, const char *pszFilename, bool fFatal)
+{
+ PCSUPINSTFILE pFile = &g_aSupInstallFiles[iFile];
+
+ /*
+ * Construct the full path for the file table entry
+ * and compare it with the specified file.
+ */
+ char szName[RTPATH_MAX];
+ int rc = supR3HardenedMakeFilePath(pFile, szName, sizeof(szName), true /*fWithFilename*/, fFatal);
+ if (RT_FAILURE(rc))
+ return rc;
+ if (SUP_COMP_FILENAME(szName, pszFilename))
+ {
+ /*
+ * Normalize the two paths and compare again.
+ */
+ rc = VERR_NOT_SAME_DEVICE;
+#if defined(RT_OS_WINDOWS)
+ LPSTR pszIgnored;
+ char szName2[RTPATH_MAX]; /** @todo Must use UTF-16 here! Code is mixing UTF-8 and native. */
+ if ( GetFullPathName(szName, RT_ELEMENTS(szName2), &szName2[0], &pszIgnored)
+ && GetFullPathName(pszFilename, RT_ELEMENTS(szName), &szName[0], &pszIgnored))
+ if (!SUP_COMP_FILENAME(szName2, szName))
+ rc = VINF_SUCCESS;
+#else
+ AssertCompile(RTPATH_MAX >= PATH_MAX);
+ char szName2[RTPATH_MAX];
+ if ( realpath(szName, szName2) != NULL
+ && realpath(pszFilename, szName) != NULL)
+ if (!SUP_COMP_FILENAME(szName2, szName))
+ rc = VINF_SUCCESS;
+#endif
+
+ if (RT_FAILURE(rc))
+ {
+ supR3HardenedMakeFilePath(pFile, szName, sizeof(szName), true /*fWithFilename*/, fFatal);
+ return supR3HardenedError(rc, fFatal,
+ "supR3HardenedVerifySameFile: \"%s\" isn't the same as \"%s\"\n",
+ pszFilename, szName);
+ }
+ }
+
+ /*
+ * Check more stuff like the stat info if it's an already open file?
+ */
+
+
+
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * Verifies a file.
+ *
+ * @returns VINF_SUCCESS on success.
+ * VERR_NOT_FOUND if the file isn't in the table, this isn't ever a fatal error.
+ * On verification failure, an error code will be returned when fFatal is clear,
+ * otherwise the program will be terminated.
+ *
+ * @param pszFilename The filename.
+ * @param fFatal Whether validation failures should be treated as
+ * fatal (true) or not (false).
+ */
+DECLHIDDEN(int) supR3HardenedVerifyFixedFile(const char *pszFilename, bool fFatal)
+{
+ /*
+ * Lookup the file and check if it's the same file.
+ */
+ const char *pszName = supR3HardenedPathFilename(pszFilename);
+ for (unsigned iFile = 0; iFile < RT_ELEMENTS(g_aSupInstallFiles); iFile++)
+ if (!SUP_COMP_FILENAME(pszName, g_aSupInstallFiles[iFile].pszFile))
+ {
+ int rc = supR3HardenedVerifySameFile(iFile, pszFilename, fFatal);
+ if (RT_SUCCESS(rc))
+ rc = supR3HardenedVerifyFileInternal(iFile, fFatal, false /* fLeaveFileOpen */, false /* fVerifyAll */);
+ return rc;
+ }
+
+ return VERR_NOT_FOUND;
+}
+
+
+/**
+ * Verifies a program, worker for supR3HardenedVerifyAll.
+ *
+ * @returns See supR3HardenedVerifyAll.
+ * @param pszProgName See supR3HardenedVerifyAll.
+ * @param pszExePath The path to the executable.
+ * @param fFatal See supR3HardenedVerifyAll.
+ * @param fLeaveOpen The leave open setting used by
+ * supR3HardenedVerifyAll.
+ * @param fMainFlags Flags supplied to SUPR3HardenedMain.
+ */
+static int supR3HardenedVerifyProgram(const char *pszProgName, const char *pszExePath, bool fFatal,
+ bool fLeaveOpen, uint32_t fMainFlags)
+{
+ /*
+ * Search the table looking for the executable and the DLL/DYLIB/SO.
+ * Note! On darwin we have a hack in place for VirtualBoxVM helper app
+ * to share VirtualBox.dylib with the VirtualBox app. This ASSUMES
+ * that cchProgNameDll is equal or shorter to the exe name.
+ */
+ int rc = VINF_SUCCESS;
+ bool fExe = false;
+ bool fDll = false;
+ size_t const cchProgNameExe = suplibHardenedStrLen(pszProgName);
+#ifndef RT_OS_DARWIN
+ size_t const cchProgNameDll = cchProgNameExe;
+ NOREF(fMainFlags);
+#else
+ size_t const cchProgNameDll = fMainFlags & SUPSECMAIN_FLAGS_OSX_VM_APP
+ ? sizeof("VirtualBox") - 1
+ : cchProgNameExe;
+ if (cchProgNameDll > cchProgNameExe)
+ return supR3HardenedError(VERR_INTERNAL_ERROR, fFatal,
+ "supR3HardenedVerifyProgram: SUPSECMAIN_FLAGS_OSX_VM_APP + '%s'", pszProgName);
+#endif
+ for (unsigned iFile = 0; iFile < RT_ELEMENTS(g_aSupInstallFiles); iFile++)
+ if (!suplibHardenedStrNCmp(pszProgName, g_aSupInstallFiles[iFile].pszFile, cchProgNameDll))
+ {
+ if ( ( g_aSupInstallFiles[iFile].enmType == kSupIFT_Dll
+ || g_aSupInstallFiles[iFile].enmType == kSupIFT_TestDll)
+ && !suplibHardenedStrCmp(&g_aSupInstallFiles[iFile].pszFile[cchProgNameDll], SUPLIB_DLL_SUFF))
+ {
+ /* This only has to be found (once). */
+ if (fDll)
+ rc = supR3HardenedError(VERR_INTERNAL_ERROR, fFatal,
+ "supR3HardenedVerifyProgram: duplicate DLL entry for \"%s\"\n", pszProgName);
+ else
+ rc = supR3HardenedVerifyFileInternal(iFile, fFatal, fLeaveOpen,
+ true /* fVerifyAll - check sign later, only final process need check it on load. */);
+ fDll = true;
+ }
+ else if ( ( g_aSupInstallFiles[iFile].enmType == kSupIFT_Exe
+ || g_aSupInstallFiles[iFile].enmType == kSupIFT_TestExe)
+ && ( cchProgNameExe == cchProgNameDll
+ || !suplibHardenedStrNCmp(pszProgName, g_aSupInstallFiles[iFile].pszFile, cchProgNameExe))
+ && !suplibHardenedStrCmp(&g_aSupInstallFiles[iFile].pszFile[cchProgNameExe], SUPLIB_EXE_SUFF))
+ {
+ /* Here we'll have to check that the specific program is the same as the entry. */
+ if (fExe)
+ rc = supR3HardenedError(VERR_INTERNAL_ERROR, fFatal,
+ "supR3HardenedVerifyProgram: duplicate EXE entry for \"%s\"\n", pszProgName);
+ else
+ rc = supR3HardenedVerifyFileInternal(iFile, fFatal, fLeaveOpen, false /* fVerifyAll */);
+ fExe = true;
+
+ supR3HardenedVerifySameFile(iFile, pszExePath, fFatal);
+ }
+ }
+
+ /*
+ * Check the findings.
+ */
+ if (RT_SUCCESS(rc))
+ {
+ if (!fDll && !fExe)
+ rc = supR3HardenedError(VERR_NOT_FOUND, fFatal,
+ "supR3HardenedVerifyProgram: Couldn't find the program \"%s\"\n", pszProgName);
+ else if (!fExe)
+ rc = supR3HardenedError(VERR_NOT_FOUND, fFatal,
+ "supR3HardenedVerifyProgram: Couldn't find the EXE entry for \"%s\"\n", pszProgName);
+ else if (!fDll)
+ rc = supR3HardenedError(VERR_NOT_FOUND, fFatal,
+ "supR3HardenedVerifyProgram: Couldn't find the DLL entry for \"%s\"\n", pszProgName);
+ }
+ return rc;
+}
+
+
+/**
+ * Verifies all the known files (called from SUPR3HardenedMain).
+ *
+ * @returns VINF_SUCCESS on success.
+ * On verification failure, an error code will be returned when fFatal is clear,
+ * otherwise the program will be terminated.
+ *
+ * @param fFatal Whether validation failures should be treated as
+ * fatal (true) or not (false).
+ * @param pszProgName The program name. This is used to verify that
+ * both the executable and corresponding
+ * DLL/DYLIB/SO are valid.
+ * @param pszExePath The path to the executable.
+ * @param fMainFlags Flags supplied to SUPR3HardenedMain.
+ */
+DECLHIDDEN(int) supR3HardenedVerifyAll(bool fFatal, const char *pszProgName, const char *pszExePath, uint32_t fMainFlags)
+{
+ /*
+ * On windows
+ */
+#if defined(RT_OS_WINDOWS)
+ bool fLeaveOpen = true;
+#else
+ bool fLeaveOpen = false;
+#endif
+
+ /*
+ * The verify all the files.
+ */
+ int rc = VINF_SUCCESS;
+ for (unsigned iFile = 0; iFile < RT_ELEMENTS(g_aSupInstallFiles); iFile++)
+ {
+ int rc2 = supR3HardenedVerifyFileInternal(iFile, fFatal, fLeaveOpen, true /* fVerifyAll */);
+ if (RT_FAILURE(rc2) && RT_SUCCESS(rc))
+ rc = rc2;
+ }
+
+ /*
+ * Verify the program name, that is to say, check that it's in the table
+ * (thus verified above) and verify the signature on platforms where we
+ * sign things.
+ */
+ int rc2 = supR3HardenedVerifyProgram(pszProgName, pszExePath, fFatal, fLeaveOpen, fMainFlags);
+ if (RT_FAILURE(rc2) && RT_SUCCESS(rc))
+ rc2 = rc;
+
+ return rc;
+}
+
+
+/**
+ * Copies the N messages into the error buffer and returns @a rc.
+ *
+ * @returns Returns @a rc
+ * @param rc The return code.
+ * @param pErrInfo The error info structure.
+ * @param cMsgs The number of messages in the ellipsis.
+ * @param ... Message parts.
+ */
+static int supR3HardenedSetErrorN(int rc, PRTERRINFO pErrInfo, unsigned cMsgs, ...)
+{
+ if (pErrInfo)
+ {
+ size_t cbErr = pErrInfo->cbMsg;
+ char *pszErr = pErrInfo->pszMsg;
+
+ va_list va;
+ va_start(va, cMsgs);
+ while (cMsgs-- > 0 && cbErr > 0)
+ {
+ const char *pszMsg = va_arg(va, const char *);
+ size_t cchMsg = RT_VALID_PTR(pszMsg) ? suplibHardenedStrLen(pszMsg) : 0;
+ if (cchMsg >= cbErr)
+ cchMsg = cbErr - 1;
+ suplibHardenedMemCopy(pszErr, pszMsg, cchMsg);
+ pszErr[cchMsg] = '\0';
+ pszErr += cchMsg;
+ cbErr -= cchMsg;
+ }
+ va_end(va);
+
+ pErrInfo->rc = rc;
+ pErrInfo->fFlags |= RTERRINFO_FLAGS_SET;
+ }
+
+ return rc;
+}
+
+
+#if defined(RT_OS_DARWIN) || defined(RT_OS_LINUX)
+/**
+ * Copies the four messages into the error buffer and returns @a rc.
+ *
+ * @returns Returns @a rc
+ * @param rc The return code.
+ * @param pErrInfo The error info structure.
+ * @param pszMsg1 The first message part.
+ * @param pszMsg2 The second message part.
+ * @param pszMsg3 The third message part.
+ * @param pszMsg4 The fourth message part.
+ */
+static int supR3HardenedSetError4(int rc, PRTERRINFO pErrInfo, const char *pszMsg1,
+ const char *pszMsg2, const char *pszMsg3, const char *pszMsg4)
+{
+ return supR3HardenedSetErrorN(rc, pErrInfo, 4, pszMsg1, pszMsg2, pszMsg3, pszMsg4);
+}
+#endif
+
+
+/**
+ * Copies the three messages into the error buffer and returns @a rc.
+ *
+ * @returns Returns @a rc
+ * @param rc The return code.
+ * @param pErrInfo The error info structure.
+ * @param pszMsg1 The first message part.
+ * @param pszMsg2 The second message part.
+ * @param pszMsg3 The third message part.
+ */
+static int supR3HardenedSetError3(int rc, PRTERRINFO pErrInfo, const char *pszMsg1,
+ const char *pszMsg2, const char *pszMsg3)
+{
+ return supR3HardenedSetErrorN(rc, pErrInfo, 3, pszMsg1, pszMsg2, pszMsg3);
+}
+
+
+#ifdef SOME_UNUSED_FUNCTION
+/**
+ * Copies the two messages into the error buffer and returns @a rc.
+ *
+ * @returns Returns @a rc
+ * @param rc The return code.
+ * @param pErrInfo The error info structure.
+ * @param pszMsg1 The first message part.
+ * @param pszMsg2 The second message part.
+ */
+static int supR3HardenedSetError2(int rc, PRTERRINFO pErrInfo, const char *pszMsg1,
+ const char *pszMsg2)
+{
+ return supR3HardenedSetErrorN(rc, pErrInfo, 2, pszMsg1, pszMsg2);
+}
+#endif
+
+
+#ifndef SUP_HARDENED_VERIFY_FOLLOW_SYMLINKS_USE_REALPATH
+# if defined(RT_OS_DARWIN) || defined(RT_OS_LINUX)
+/**
+ * Copies the error message to the error buffer and returns @a rc.
+ *
+ * @returns Returns @a rc
+ * @param rc The return code.
+ * @param pErrInfo The error info structure.
+ * @param pszMsg The message.
+ */
+static int supR3HardenedSetError(int rc, PRTERRINFO pErrInfo, const char *pszMsg)
+{
+ return supR3HardenedSetErrorN(rc, pErrInfo, 1, pszMsg);
+}
+# endif
+#endif
+
+
+/**
+ * Output from a successfull supR3HardenedVerifyPathSanity call.
+ */
+typedef struct SUPR3HARDENEDPATHINFO
+{
+ /** The length of the path in szCopy. */
+ uint16_t cch;
+ /** The number of path components. */
+ uint16_t cComponents;
+ /** Set if the path ends with slash, indicating that it's a directory
+ * reference and not a file reference. The slash has been removed from
+ * the copy. */
+ bool fDirSlash;
+ /** The offset where each path component starts, i.e. the char after the
+ * slash. The array has cComponents + 1 entries, where the final one is
+ * cch + 1 so that one can always terminate the current component by
+ * szPath[aoffComponent[i] - 1] = '\0'. */
+ uint16_t aoffComponents[32+1];
+ /** A normalized copy of the path.
+ * Reserve some extra space so we can be more relaxed about overflow
+ * checks and terminator paddings, especially when recursing. */
+ char szPath[SUPR3HARDENED_MAX_PATH * 2];
+} SUPR3HARDENEDPATHINFO;
+/** Pointer to a parsed path. */
+typedef SUPR3HARDENEDPATHINFO *PSUPR3HARDENEDPATHINFO;
+
+
+/**
+ * Verifies that the path is absolutely sane, it also parses the path.
+ *
+ * A sane path starts at the root (w/ drive letter on DOS derived systems) and
+ * does not have any relative bits (/../) or unnecessary slashes (/bin//ls).
+ * Sane paths are less or equal to SUPR3HARDENED_MAX_PATH bytes in length. UNC
+ * paths are not supported.
+ *
+ * @returns VBox status code.
+ * @param pszPath The path to check.
+ * @param pErrInfo The error info structure.
+ * @param pInfo Where to return a copy of the path along with
+ * parsing information.
+ */
+static int supR3HardenedVerifyPathSanity(const char *pszPath, PRTERRINFO pErrInfo, PSUPR3HARDENEDPATHINFO pInfo)
+{
+ const char *pszSrc = pszPath;
+ char *pszDst = pInfo->szPath;
+
+ /*
+ * Check that it's an absolute path and copy the volume/root specifier.
+ */
+#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
+ if ( !RT_C_IS_ALPHA(pszSrc[0])
+ || pszSrc[1] != ':'
+ || !RTPATH_IS_SLASH(pszSrc[2]))
+ return supR3HardenedSetError3(VERR_SUPLIB_PATH_NOT_ABSOLUTE, pErrInfo, "The path is not absolute: '", pszPath, "'");
+
+ *pszDst++ = RT_C_TO_UPPER(pszSrc[0]);
+ *pszDst++ = ':';
+ *pszDst++ = RTPATH_SLASH;
+ pszSrc += 3;
+
+#else
+ if (!RTPATH_IS_SLASH(pszSrc[0]))
+ return supR3HardenedSetError3(VERR_SUPLIB_PATH_NOT_ABSOLUTE, pErrInfo, "The path is not absolute: '", pszPath, "'");
+
+ *pszDst++ = RTPATH_SLASH;
+ pszSrc += 1;
+#endif
+
+ /*
+ * No path specifying the root or something very shortly thereafter will
+ * be approved of.
+ */
+ if (pszSrc[0] == '\0')
+ return supR3HardenedSetError3(VERR_SUPLIB_PATH_IS_ROOT, pErrInfo, "The path is root: '", pszPath, "'");
+ if ( pszSrc[1] == '\0'
+ || pszSrc[2] == '\0')
+ return supR3HardenedSetError3(VERR_SUPLIB_PATH_TOO_SHORT, pErrInfo, "The path is too short: '", pszPath, "'");
+
+#if RTPATH_STYLE == RTPATH_STR_F_STYLE_UNIX
+ /*
+ * Skip double slashes.
+ */
+ while (RTPATH_IS_SLASH(*pszSrc))
+ pszSrc++;
+#else
+ /*
+ * The root slash should be alone to avoid UNC confusion.
+ */
+ if (RTPATH_IS_SLASH(pszSrc[0]))
+ return supR3HardenedSetError3(VERR_SUPLIB_PATH_NOT_CLEAN, pErrInfo,
+ "The path is not clean of leading double slashes: '", pszPath, "'");
+#endif
+ /*
+ * Check each component. No parent references.
+ */
+ pInfo->cComponents = 0;
+ pInfo->fDirSlash = false;
+ while (pszSrc[0])
+ {
+ /* Sanity checks. */
+ if ( pszSrc[0] == '.'
+ && pszSrc[1] == '.'
+ && RTPATH_IS_SLASH(pszSrc[2]))
+ return supR3HardenedSetError3(VERR_SUPLIB_PATH_NOT_ABSOLUTE, pErrInfo,
+ "The path is not absolute: '", pszPath, "'");
+
+ /* Record the start of the component. */
+ if (pInfo->cComponents >= RT_ELEMENTS(pInfo->aoffComponents) - 1)
+ return supR3HardenedSetError3(VERR_SUPLIB_PATH_TOO_MANY_COMPONENTS, pErrInfo,
+ "The path has too many components: '", pszPath, "'");
+ pInfo->aoffComponents[pInfo->cComponents++] = pszDst - &pInfo->szPath[0];
+
+ /* Traverse to the end of the component, copying it as we go along. */
+ while (pszSrc[0])
+ {
+ if (RTPATH_IS_SLASH(pszSrc[0]))
+ {
+ pszSrc++;
+ if (*pszSrc)
+ *pszDst++ = RTPATH_SLASH;
+ else
+ pInfo->fDirSlash = true;
+ break;
+ }
+ *pszDst++ = *pszSrc++;
+ if ((uintptr_t)(pszDst - &pInfo->szPath[0]) >= SUPR3HARDENED_MAX_PATH)
+ return supR3HardenedSetError3(VERR_SUPLIB_PATH_TOO_LONG, pErrInfo,
+ "The path is too long: '", pszPath, "'");
+ }
+
+ /* Skip double slashes. */
+ while (RTPATH_IS_SLASH(*pszSrc))
+ pszSrc++;
+ }
+
+ /* Terminate the string and enter its length. */
+ pszDst[0] = '\0';
+ pszDst[1] = '\0'; /* for aoffComponents */
+ pInfo->cch = (uint16_t)(pszDst - &pInfo->szPath[0]);
+ pInfo->aoffComponents[pInfo->cComponents] = pInfo->cch + 1;
+
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * The state information collected by supR3HardenedVerifyFsObject.
+ *
+ * This can be used to verify that a directory we've opened for enumeration is
+ * the same as the one that supR3HardenedVerifyFsObject just verified. It can
+ * equally be used to verify a native specfied by the user.
+ */
+typedef struct SUPR3HARDENEDFSOBJSTATE
+{
+#ifdef RT_OS_WINDOWS
+ /** Not implemented for windows yet. */
+ char chTodo;
+#else
+ /** The stat output. */
+ struct stat Stat;
+#endif
+} SUPR3HARDENEDFSOBJSTATE;
+/** Pointer to a file system object state. */
+typedef SUPR3HARDENEDFSOBJSTATE *PSUPR3HARDENEDFSOBJSTATE;
+/** Pointer to a const file system object state. */
+typedef SUPR3HARDENEDFSOBJSTATE const *PCSUPR3HARDENEDFSOBJSTATE;
+
+
+/**
+ * Query information about a file system object by path.
+ *
+ * @returns VBox status code, error buffer filled on failure.
+ * @param pszPath The path to the object.
+ * @param pFsObjState Where to return the state information.
+ * @param pErrInfo The error info structure.
+ */
+static int supR3HardenedQueryFsObjectByPath(char const *pszPath, PSUPR3HARDENEDFSOBJSTATE pFsObjState, PRTERRINFO pErrInfo)
+{
+#if defined(RT_OS_WINDOWS)
+ /** @todo Windows hardening. */
+ pFsObjState->chTodo = 0;
+ RT_NOREF2(pszPath, pErrInfo);
+ return VINF_SUCCESS;
+
+#else
+ /*
+ * Stat the object, do not follow links.
+ */
+ if (lstat(pszPath, &pFsObjState->Stat) != 0)
+ {
+ /* Ignore access errors */
+ if (errno != EACCES)
+ return supR3HardenedSetErrorN(VERR_SUPLIB_STAT_FAILED, pErrInfo,
+ 5, "stat failed with ", strerror(errno), " on: '", pszPath, "'");
+ }
+
+ /*
+ * Read ACLs.
+ */
+ /** @todo */
+
+ return VINF_SUCCESS;
+#endif
+}
+
+
+/**
+ * Query information about a file system object by native handle.
+ *
+ * @returns VBox status code, error buffer filled on failure.
+ * @param hNative The native handle to the object @a pszPath
+ * specifies and this should be verified to be the
+ * same file system object.
+ * @param pFsObjState Where to return the state information.
+ * @param pszPath The path to the object. (For the error message
+ * only.)
+ * @param pErrInfo The error info structure.
+ */
+static int supR3HardenedQueryFsObjectByHandle(RTHCUINTPTR hNative, PSUPR3HARDENEDFSOBJSTATE pFsObjState,
+ char const *pszPath, PRTERRINFO pErrInfo)
+{
+#if defined(RT_OS_WINDOWS)
+ /** @todo Windows hardening. */
+ pFsObjState->chTodo = 0;
+ RT_NOREF3(hNative, pszPath, pErrInfo);
+ return VINF_SUCCESS;
+
+#else
+ /*
+ * Stat the object, do not follow links.
+ */
+ if (fstat((int)hNative, &pFsObjState->Stat) != 0)
+ return supR3HardenedSetErrorN(VERR_SUPLIB_STAT_FAILED, pErrInfo,
+ 5, "fstat failed with ", strerror(errno), " on '", pszPath, "'");
+
+ /*
+ * Read ACLs.
+ */
+ /** @todo */
+
+ return VINF_SUCCESS;
+#endif
+}
+
+
+/**
+ * Verifies that the file system object indicated by the native handle is the
+ * same as the one @a pFsObjState indicates.
+ *
+ * @returns VBox status code, error buffer filled on failure.
+ * @param pFsObjState1 File system object information/state by path.
+ * @param pFsObjState2 File system object information/state by handle.
+ * @param pszPath The path to the object @a pFsObjState
+ * describes. (For the error message.)
+ * @param pErrInfo The error info structure.
+ */
+static int supR3HardenedIsSameFsObject(PCSUPR3HARDENEDFSOBJSTATE pFsObjState1, PCSUPR3HARDENEDFSOBJSTATE pFsObjState2,
+ const char *pszPath, PRTERRINFO pErrInfo)
+{
+#if defined(RT_OS_WINDOWS)
+ /** @todo Windows hardening. */
+ RT_NOREF4(pFsObjState1, pFsObjState2, pszPath, pErrInfo);
+ return VINF_SUCCESS;
+
+#elif defined(RT_OS_OS2)
+ RT_NOREF4(pFsObjState1, pFsObjState2, pszPath, pErrInfo);
+ return VINF_SUCCESS;
+
+#else
+ /*
+ * Compare the ino+dev, then the uid+gid and finally the important mode
+ * bits. Technically the first one should be enough, but we're paranoid.
+ */
+ if ( pFsObjState1->Stat.st_ino != pFsObjState2->Stat.st_ino
+ || pFsObjState1->Stat.st_dev != pFsObjState2->Stat.st_dev)
+ return supR3HardenedSetError3(VERR_SUPLIB_NOT_SAME_OBJECT, pErrInfo,
+ "The native handle is not the same as '", pszPath, "' (ino/dev)");
+ if ( pFsObjState1->Stat.st_uid != pFsObjState2->Stat.st_uid
+ || pFsObjState1->Stat.st_gid != pFsObjState2->Stat.st_gid)
+ return supR3HardenedSetError3(VERR_SUPLIB_NOT_SAME_OBJECT, pErrInfo,
+ "The native handle is not the same as '", pszPath, "' (uid/gid)");
+ if ( (pFsObjState1->Stat.st_mode & (S_IFMT | S_IWUSR | S_IWGRP | S_IWOTH))
+ != (pFsObjState2->Stat.st_mode & (S_IFMT | S_IWUSR | S_IWGRP | S_IWOTH)))
+ return supR3HardenedSetError3(VERR_SUPLIB_NOT_SAME_OBJECT, pErrInfo,
+ "The native handle is not the same as '", pszPath, "' (mode)");
+ return VINF_SUCCESS;
+#endif
+}
+
+
+/**
+ * Verifies a file system object (file or directory).
+ *
+ * @returns VBox status code, error buffer filled on failure.
+ * @param pFsObjState The file system object information/state to be
+ * verified.
+ * @param fDir Whether this is a directory or a file.
+ * @param fRelaxed Whether we can be more relaxed about this
+ * directory (only used for grand parent
+ * directories).
+ * @param fSymlinksAllowed Flag whether symlinks are allowed or not.
+ * If allowed the symlink object is verified not the target.
+ * @param pszPath The path to the object. For error messages and
+ * securing a couple of hacks.
+ * @param pErrInfo The error info structure.
+ */
+static int supR3HardenedVerifyFsObject(PCSUPR3HARDENEDFSOBJSTATE pFsObjState, bool fDir, bool fRelaxed,
+ bool fSymlinksAllowed, const char *pszPath, PRTERRINFO pErrInfo)
+{
+#if defined(RT_OS_WINDOWS)
+ /** @todo Windows hardening. */
+ RT_NOREF(pFsObjState, fDir, fRelaxed, fSymlinksAllowed, pszPath, pErrInfo);
+ return VINF_SUCCESS;
+
+#elif defined(RT_OS_OS2)
+ /* No hardening here - it's a single user system. */
+ RT_NOREF(pFsObjState, fDir, fRelaxed, fSymlinksAllowed, pszPath, pErrInfo);
+ return VINF_SUCCESS;
+
+#else
+ /*
+ * The owner must be root.
+ *
+ * This can be extended to include predefined system users if necessary.
+ */
+ if (pFsObjState->Stat.st_uid != 0)
+ return supR3HardenedSetError3(VERR_SUPLIB_OWNER_NOT_ROOT, pErrInfo, "The owner is not root: '", pszPath, "'");
+
+ /*
+ * The object type must be directory or file. It can be a symbolic link
+ * if explicitely allowed. Otherwise this and other risky stuff is not allowed
+ * (sorry dude, but we're paranoid on purpose here).
+ */
+ if ( !S_ISLNK(pFsObjState->Stat.st_mode)
+ || !fSymlinksAllowed)
+ {
+
+ if ( !S_ISDIR(pFsObjState->Stat.st_mode)
+ && !S_ISREG(pFsObjState->Stat.st_mode))
+ {
+ if (S_ISLNK(pFsObjState->Stat.st_mode))
+ return supR3HardenedSetError3(VERR_SUPLIB_SYMLINKS_ARE_NOT_PERMITTED, pErrInfo,
+ "Symlinks are not permitted: '", pszPath, "'");
+ return supR3HardenedSetError3(VERR_SUPLIB_NOT_DIR_NOT_FILE, pErrInfo,
+ "Not regular file or directory: '", pszPath, "'");
+ }
+ if (fDir != !!S_ISDIR(pFsObjState->Stat.st_mode))
+ {
+ if (S_ISDIR(pFsObjState->Stat.st_mode))
+ return supR3HardenedSetError3(VERR_SUPLIB_IS_DIRECTORY, pErrInfo,
+ "Expected file but found directory: '", pszPath, "'");
+ return supR3HardenedSetError3(VERR_SUPLIB_IS_FILE, pErrInfo,
+ "Expected directory but found file: '", pszPath, "'");
+ }
+ }
+
+ /*
+ * The group does not matter if it does not have write access, if it has
+ * write access it must be group 0 (root/wheel/whatever).
+ *
+ * This can be extended to include predefined system groups or groups that
+ * only root is member of.
+ */
+ if ( (pFsObjState->Stat.st_mode & S_IWGRP)
+ && pFsObjState->Stat.st_gid != 0)
+ {
+# ifdef RT_OS_DARWIN
+ /* HACK ALERT: On Darwin /Applications is root:admin with admin having
+ full access. So, to work around we relax the hardening a bit and
+ permit grand parents and beyond to be group writable by admin. */
+ /** @todo dynamically resolve the admin group? */
+ bool fBad = !fRelaxed || pFsObjState->Stat.st_gid != 80 /*admin*/ || suplibHardenedStrCmp(pszPath, "/Applications");
+
+# elif defined(RT_OS_FREEBSD)
+ /* HACK ALERT: PC-BSD 9 has group-writable /usr/pib directory which is
+ similar to /Applications on OS X (see above).
+ On FreeBSD root is normally the only member of this group, on
+ PC-BSD the default user is a member. */
+ /** @todo dynamically resolve the operator group? */
+ bool fBad = !fRelaxed || pFsObjState->Stat.st_gid != 5 /*operator*/ || suplibHardenedStrCmp(pszPath, "/usr/pbi");
+ NOREF(fRelaxed);
+# elif defined(RT_OS_SOLARIS)
+ /* HACK ALERT: Solaris has group-writable /usr/lib/iconv directory from
+ which the appropriate module is loaded.
+ By default only root and daemon are part of that group.
+ . */
+ /** @todo dynamically resolve the bin group? */
+ bool fBad = !fRelaxed || pFsObjState->Stat.st_gid != 2 /*bin*/ || suplibHardenedStrCmp(pszPath, "/usr/lib/iconv");
+# else
+ NOREF(fRelaxed);
+ bool fBad = true;
+# endif
+ if (fBad)
+ return supR3HardenedSetError3(VERR_SUPLIB_WRITE_NON_SYS_GROUP, pErrInfo,
+ "An unknown (and thus untrusted) group has write access to '", pszPath,
+ "' and we therefore cannot trust the directory content or that of any subdirectory");
+ }
+
+ /*
+ * World must not have write access. There is no relaxing this rule.
+ * Linux exception: Symbolic links are always give permission 0777, there
+ * is no lchmod or lchown APIs. The permissions on parent
+ * directory that contains the symbolic link is what is
+ * decising wrt to modifying it. (Caller is expected not
+ * to allow symbolic links in the first path component.)
+ */
+ if ( (pFsObjState->Stat.st_mode & S_IWOTH)
+# ifdef RT_OS_LINUX
+ && ( !S_ISLNK(pFsObjState->Stat.st_mode)
+ || !fSymlinksAllowed /* paranoia */)
+# endif
+ )
+ return supR3HardenedSetError3(VERR_SUPLIB_WORLD_WRITABLE, pErrInfo,
+ "World writable: '", pszPath, "'");
+
+ /*
+ * Check the ACLs.
+ */
+ /** @todo */
+
+ return VINF_SUCCESS;
+#endif
+}
+
+
+/**
+ * Verifies that the file system object indicated by the native handle is the
+ * same as the one @a pFsObjState indicates.
+ *
+ * @returns VBox status code, error buffer filled on failure.
+ * @param hNative The native handle to the object @a pszPath
+ * specifies and this should be verified to be the
+ * same file system object.
+ * @param pFsObjState The information/state returned by a previous
+ * query call.
+ * @param pszPath The path to the object @a pFsObjState
+ * describes. (For the error message.)
+ * @param pErrInfo The error info structure.
+ */
+static int supR3HardenedVerifySameFsObject(RTHCUINTPTR hNative, PCSUPR3HARDENEDFSOBJSTATE pFsObjState,
+ const char *pszPath, PRTERRINFO pErrInfo)
+{
+ SUPR3HARDENEDFSOBJSTATE FsObjState2;
+ int rc = supR3HardenedQueryFsObjectByHandle(hNative, &FsObjState2, pszPath, pErrInfo);
+ if (RT_SUCCESS(rc))
+ rc = supR3HardenedIsSameFsObject(pFsObjState, &FsObjState2, pszPath, pErrInfo);
+ return rc;
+}
+
+
+/**
+ * Does the recursive directory enumeration.
+ *
+ * @returns VBox status code, error buffer filled on failure.
+ * @param pszDirPath The path buffer containing the subdirectory to
+ * enumerate followed by a slash (this is never
+ * the root slash). The buffer is RTPATH_MAX in
+ * size and anything starting at @a cchDirPath
+ * - 1 and beyond is scratch space.
+ * @param cchDirPath The length of the directory path + slash.
+ * @param pFsObjState Pointer to the file system object state buffer.
+ * On input this will hold the stats for
+ * the directory @a pszDirPath indicates and will
+ * be used to verified that we're opening the same
+ * thing.
+ * @param fRecursive Whether to recurse into subdirectories.
+ * @param pErrInfo The error info structure.
+ */
+static int supR3HardenedVerifyDirRecursive(char *pszDirPath, size_t cchDirPath, PSUPR3HARDENEDFSOBJSTATE pFsObjState,
+ bool fRecursive, PRTERRINFO pErrInfo)
+{
+#if defined(RT_OS_WINDOWS)
+ /** @todo Windows hardening. */
+ RT_NOREF5(pszDirPath, cchDirPath, pFsObjState, fRecursive, pErrInfo);
+ return VINF_SUCCESS;
+
+#elif defined(RT_OS_OS2)
+ /* No hardening here - it's a single user system. */
+ RT_NOREF5(pszDirPath, cchDirPath, pFsObjState, fRecursive, pErrInfo);
+ return VINF_SUCCESS;
+
+#else
+ /*
+ * Open the directory. Now, we could probably eliminate opendir here
+ * and go down on kernel API level (open + getdents for instance), however
+ * that's not very portable and hopefully not necessary.
+ */
+ DIR *pDir = opendir(pszDirPath);
+ if (!pDir)
+ {
+ /* Ignore access errors. */
+ if (errno == EACCES)
+ return VINF_SUCCESS;
+ return supR3HardenedSetErrorN(VERR_SUPLIB_DIR_ENUM_FAILED, pErrInfo,
+ 5, "opendir failed with ", strerror(errno), " on '", pszDirPath, "'");
+ }
+ if (dirfd(pDir) != -1)
+ {
+ int rc = supR3HardenedVerifySameFsObject(dirfd(pDir), pFsObjState, pszDirPath, pErrInfo);
+ if (RT_FAILURE(rc))
+ {
+ closedir(pDir);
+ return rc;
+ }
+ }
+
+ /*
+ * Enumerate the directory, check all the requested bits.
+ */
+ int rc = VINF_SUCCESS;
+ for (;;)
+ {
+ pszDirPath[cchDirPath] = '\0'; /* for error messages. */
+
+ struct dirent Entry;
+ struct dirent *pEntry;
+#if RT_GNUC_PREREQ(4, 6)
+# pragma GCC diagnostic push
+# pragma GCC diagnostic ignored "-Wdeprecated-declarations"
+#endif
+ int iErr = readdir_r(pDir, &Entry, &pEntry);
+#if RT_GNUC_PREREQ(4, 6)
+# pragma GCC diagnostic pop
+#endif
+ if (iErr)
+ {
+ rc = supR3HardenedSetErrorN(VERR_SUPLIB_DIR_ENUM_FAILED, pErrInfo,
+ 5, "readdir_r failed with ", strerror(iErr), " in '", pszDirPath, "'");
+ break;
+ }
+ if (!pEntry)
+ break;
+
+ /*
+ * Check the length and copy it into the path buffer so it can be
+ * stat()'ed.
+ */
+ size_t cchName = suplibHardenedStrLen(pEntry->d_name);
+ if (cchName + cchDirPath > SUPR3HARDENED_MAX_PATH)
+ {
+ rc = supR3HardenedSetErrorN(VERR_SUPLIB_PATH_TOO_LONG, pErrInfo,
+ 4, "Path grew too long during recursion: '", pszDirPath, pEntry->d_name, "'");
+ break;
+ }
+ suplibHardenedMemCopy(&pszDirPath[cchDirPath], pEntry->d_name, cchName + 1);
+
+ /*
+ * Query the information about the entry and verify it.
+ * (We don't bother skipping '.' and '..' at this point, a little bit
+ * of extra checks doesn't hurt and neither requires relaxed handling.)
+ */
+ rc = supR3HardenedQueryFsObjectByPath(pszDirPath, pFsObjState, pErrInfo);
+ if (RT_SUCCESS(rc))
+ break;
+ rc = supR3HardenedVerifyFsObject(pFsObjState, S_ISDIR(pFsObjState->Stat.st_mode), false /*fRelaxed*/,
+ false /*fSymlinksAllowed*/, pszDirPath, pErrInfo);
+ if (RT_FAILURE(rc))
+ break;
+
+ /*
+ * Recurse into subdirectories if requested.
+ */
+ if ( fRecursive
+ && S_ISDIR(pFsObjState->Stat.st_mode)
+ && suplibHardenedStrCmp(pEntry->d_name, ".")
+ && suplibHardenedStrCmp(pEntry->d_name, ".."))
+ {
+ pszDirPath[cchDirPath + cchName] = RTPATH_SLASH;
+ pszDirPath[cchDirPath + cchName + 1] = '\0';
+
+ rc = supR3HardenedVerifyDirRecursive(pszDirPath, cchDirPath + cchName + 1, pFsObjState,
+ fRecursive, pErrInfo);
+ if (RT_FAILURE(rc))
+ break;
+ }
+ }
+
+ closedir(pDir);
+ return rc;
+#endif
+}
+
+
+/**
+ * Worker for SUPR3HardenedVerifyDir.
+ *
+ * @returns See SUPR3HardenedVerifyDir.
+ * @param pszDirPath See SUPR3HardenedVerifyDir.
+ * @param fRecursive See SUPR3HardenedVerifyDir.
+ * @param fCheckFiles See SUPR3HardenedVerifyDir.
+ * @param pErrInfo See SUPR3HardenedVerifyDir.
+ */
+DECLHIDDEN(int) supR3HardenedVerifyDir(const char *pszDirPath, bool fRecursive, bool fCheckFiles, PRTERRINFO pErrInfo)
+{
+ /*
+ * Validate the input path and parse it.
+ */
+ SUPR3HARDENEDPATHINFO Info;
+ int rc = supR3HardenedVerifyPathSanity(pszDirPath, pErrInfo, &Info);
+ if (RT_FAILURE(rc))
+ return rc;
+
+ /*
+ * Verify each component from the root up.
+ */
+ SUPR3HARDENEDFSOBJSTATE FsObjState;
+ uint32_t const cComponents = Info.cComponents;
+ for (uint32_t iComponent = 0; iComponent < cComponents; iComponent++)
+ {
+ bool fRelaxed = iComponent + 2 < cComponents;
+ Info.szPath[Info.aoffComponents[iComponent + 1] - 1] = '\0';
+ rc = supR3HardenedQueryFsObjectByPath(Info.szPath, &FsObjState, pErrInfo);
+ if (RT_SUCCESS(rc))
+ rc = supR3HardenedVerifyFsObject(&FsObjState, true /*fDir*/, fRelaxed,
+ false /*fSymlinksAllowed*/, Info.szPath, pErrInfo);
+ if (RT_FAILURE(rc))
+ return rc;
+ Info.szPath[Info.aoffComponents[iComponent + 1] - 1] = iComponent + 1 != cComponents ? RTPATH_SLASH : '\0';
+ }
+
+ /*
+ * Check files and subdirectories if requested.
+ */
+ if (fCheckFiles || fRecursive)
+ {
+ Info.szPath[Info.cch] = RTPATH_SLASH;
+ Info.szPath[Info.cch + 1] = '\0';
+ return supR3HardenedVerifyDirRecursive(Info.szPath, Info.cch + 1, &FsObjState,
+ fRecursive, pErrInfo);
+ }
+
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * Verfies a file.
+ *
+ * @returns VBox status code, error buffer filled on failure.
+ * @param pszFilename The file to verify.
+ * @param hNativeFile Handle to the file, verify that it's the same
+ * as we ended up with when verifying the path.
+ * RTHCUINTPTR_MAX means NIL here.
+ * @param fMaybe3rdParty Set if the file is could be a supplied by a
+ * third party. Different validation rules may
+ * apply to 3rd party code on some platforms.
+ * @param pErrInfo Where to return extended error information.
+ * Optional.
+ */
+DECLHIDDEN(int) supR3HardenedVerifyFile(const char *pszFilename, RTHCUINTPTR hNativeFile,
+ bool fMaybe3rdParty, PRTERRINFO pErrInfo)
+{
+ /*
+ * Validate the input path and parse it.
+ */
+ SUPR3HARDENEDPATHINFO Info;
+ int rc = supR3HardenedVerifyPathSanity(pszFilename, pErrInfo, &Info);
+ if (RT_FAILURE(rc))
+ return rc;
+ if (Info.fDirSlash)
+ return supR3HardenedSetError3(VERR_SUPLIB_IS_DIRECTORY, pErrInfo,
+ "The file path specifies a directory: '", pszFilename, "'");
+
+ /*
+ * Verify each component from the root up.
+ */
+ SUPR3HARDENEDFSOBJSTATE FsObjState;
+ uint32_t const cComponents = Info.cComponents;
+ for (uint32_t iComponent = 0; iComponent < cComponents; iComponent++)
+ {
+ bool fFinal = iComponent + 1 == cComponents;
+ bool fRelaxed = iComponent + 2 < cComponents;
+ Info.szPath[Info.aoffComponents[iComponent + 1] - 1] = '\0';
+ rc = supR3HardenedQueryFsObjectByPath(Info.szPath, &FsObjState, pErrInfo);
+ if (RT_SUCCESS(rc))
+ rc = supR3HardenedVerifyFsObject(&FsObjState, !fFinal /*fDir*/, fRelaxed,
+ false /*fSymlinksAllowed*/, Info.szPath, pErrInfo);
+ if (RT_FAILURE(rc))
+ return rc;
+ Info.szPath[Info.aoffComponents[iComponent + 1] - 1] = !fFinal ? RTPATH_SLASH : '\0';
+ }
+
+ /*
+ * Verify the file handle against the last component, if specified.
+ */
+ if (hNativeFile != RTHCUINTPTR_MAX)
+ {
+ rc = supR3HardenedVerifySameFsObject(hNativeFile, &FsObjState, Info.szPath, pErrInfo);
+ if (RT_FAILURE(rc))
+ return rc;
+ }
+
+#ifdef RT_OS_WINDOWS
+ /*
+ * The files shall be signed on windows, verify that.
+ */
+ rc = VINF_SUCCESS;
+ HANDLE hVerify;
+ if (hNativeFile == RTHCUINTPTR_MAX)
+ {
+ PRTUTF16 pwszPath;
+ rc = RTStrToUtf16(pszFilename, &pwszPath);
+ if (RT_SUCCESS(rc))
+ {
+ hVerify = CreateFileW(pwszPath, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
+ RTUtf16Free(pwszPath);
+ }
+ else
+ {
+ rc = RTErrInfoSetF(pErrInfo, rc, "Error converting '%s' to UTF-16: %Rrc", pszFilename, rc);
+ hVerify = INVALID_HANDLE_VALUE;
+ }
+ }
+ else
+ {
+ NTSTATUS rcNt = NtDuplicateObject(NtCurrentProcess(), (HANDLE)hNativeFile, NtCurrentProcess(), &hVerify,
+ GENERIC_READ, 0 /*HandleAttributes*/, 0 /*Options*/);
+ if (!NT_SUCCESS(rcNt))
+ hVerify = INVALID_HANDLE_VALUE;
+ }
+ if (hVerify != INVALID_HANDLE_VALUE)
+ {
+# ifdef VBOX_WITH_HARDENING
+ uint32_t fFlags = SUPHNTVI_F_REQUIRE_KERNEL_CODE_SIGNING;
+ if (!fMaybe3rdParty)
+ fFlags = SUPHNTVI_F_REQUIRE_BUILD_CERT;
+ const char *pszSuffix = RTPathSuffix(pszFilename);
+ if ( pszSuffix
+ && pszSuffix[0] == '.'
+ && ( RT_C_TO_LOWER(pszSuffix[1]) == 'r'
+ || RT_C_TO_LOWER(pszSuffix[1]) == 'g')
+ && RT_C_TO_LOWER(pszSuffix[2]) == 'c'
+ && pszSuffix[3] == '\0' )
+ fFlags |= SUPHNTVI_F_RC_IMAGE;
+# ifndef IN_SUP_R3_STATIC /* Not in VBoxCpuReport and friends. */
+ rc = supHardenedWinVerifyImageByHandleNoName(hVerify, fFlags, pErrInfo);
+# endif
+# else
+ RT_NOREF1(fMaybe3rdParty);
+# endif
+ NtClose(hVerify);
+ }
+ else if (RT_SUCCESS(rc))
+ rc = RTErrInfoSetF(pErrInfo, RTErrConvertFromWin32(RtlGetLastWin32Error()),
+ "Error %u trying to open (or duplicate handle for) '%s'", RtlGetLastWin32Error(), pszFilename);
+ if (RT_FAILURE(rc))
+ return rc;
+#else
+ RT_NOREF1(fMaybe3rdParty);
+#endif
+
+ return VINF_SUCCESS;
+}
+
+
+#if defined(RT_OS_DARWIN) || defined(RT_OS_LINUX)
+/**
+ * Verfies a file following symlinks.
+ *
+ * @returns VBox status code, error buffer filled on failure.
+ * @param pszFilename The file to verify.
+ * @param hNativeFile Handle to the file, verify that it's the same
+ * as we ended up with when verifying the path.
+ * RTHCUINTPTR_MAX means NIL here.
+ * @param fMaybe3rdParty Set if the file is could be a supplied by a
+ * third party. Different validation rules may
+ * apply to 3rd party code on some platforms.
+ * @param pErrInfo Where to return extended error information.
+ * Optional.
+ *
+ * @note This is only used on OS X for libraries loaded with dlopen() because
+ * the frameworks use symbolic links to point to the relevant library.
+ *
+ * @sa supR3HardenedVerifyFile
+ */
+DECLHIDDEN(int) supR3HardenedVerifyFileFollowSymlinks(const char *pszFilename, RTHCUINTPTR hNativeFile, bool fMaybe3rdParty,
+ PRTERRINFO pErrInfo)
+{
+ RT_NOREF1(fMaybe3rdParty);
+
+ /*
+ * Validate the input path and parse it.
+ */
+ SUPR3HARDENEDPATHINFO Info;
+ int rc = supR3HardenedVerifyPathSanity(pszFilename, pErrInfo, &Info);
+ if (RT_FAILURE(rc))
+ return rc;
+ if (Info.fDirSlash)
+ return supR3HardenedSetError3(VERR_SUPLIB_IS_DIRECTORY, pErrInfo,
+ "The file path specifies a directory: '", pszFilename, "'");
+
+ /*
+ * Verify each component from the root up.
+ */
+#ifndef SUP_HARDENED_VERIFY_FOLLOW_SYMLINKS_USE_REALPATH
+ uint32_t iLoops = 0;
+#endif
+ SUPR3HARDENEDFSOBJSTATE FsObjState;
+ uint32_t iComponent = 0;
+ while (iComponent < Info.cComponents)
+ {
+ bool fFinal = iComponent + 1 == Info.cComponents;
+ bool fRelaxed = iComponent + 2 < Info.cComponents;
+ Info.szPath[Info.aoffComponents[iComponent + 1] - 1] = '\0';
+ rc = supR3HardenedQueryFsObjectByPath(Info.szPath, &FsObjState, pErrInfo);
+ if (RT_SUCCESS(rc))
+ {
+ /*
+ * In case the component is a symlink expand it and start from the beginning after
+ * verifying it has the proper access rights.
+ * Furthermore only allow symlinks which don't contain any .. or . in the target
+ * (enforced by supR3HardenedVerifyPathSanity).
+ */
+ rc = supR3HardenedVerifyFsObject(&FsObjState, !fFinal /*fDir*/, fRelaxed,
+ true /*fSymlinksAllowed*/, Info.szPath, pErrInfo);
+ if ( RT_SUCCESS(rc)
+ && S_ISLNK(FsObjState.Stat.st_mode))
+ {
+#if SUP_HARDENED_VERIFY_FOLLOW_SYMLINKS_USE_REALPATH /* Another approach using realpath() and verifying the result when encountering a symlink. */
+ char *pszFilenameResolved = realpath(pszFilename, NULL);
+ if (pszFilenameResolved)
+ {
+ rc = supR3HardenedVerifyFile(pszFilenameResolved, hNativeFile, fMaybe3rdParty, pErrInfo);
+ free(pszFilenameResolved);
+ return rc;
+ }
+ else
+ {
+ int iErr = errno;
+ supR3HardenedError(VERR_ACCESS_DENIED, false /*fFatal*/,
+ "supR3HardenedVerifyFileFollowSymlinks: Failed to resolve the real path '%s': %s (%d)\n",
+ pszFilename, strerror(iErr), iErr);
+ return supR3HardenedSetError4(VERR_ACCESS_DENIED, pErrInfo,
+ "realpath failed for '", pszFilename, "': ", strerror(iErr));
+ }
+#else
+ /* Don't loop forever. */
+ iLoops++;
+ if (iLoops < 8)
+ {
+ /*
+ * Construct new path by replacing the current component by the symlink value.
+ * Note! readlink() is a weird API that doesn't necessarily indicates if the
+ * buffer is too small.
+ */
+ char szPath[RTPATH_MAX];
+ size_t const cchBefore = Info.aoffComponents[iComponent]; /* includes slash */
+ size_t const cchAfter = fFinal ? 0 : 1 /*slash*/ + Info.cch - Info.aoffComponents[iComponent + 1];
+ if (sizeof(szPath) > cchBefore + cchAfter + 2)
+ {
+ ssize_t cchTarget = readlink(Info.szPath, szPath, sizeof(szPath) - 1);
+ if (cchTarget > 0)
+ {
+ /* Some serious paranoia against embedded zero terminator and weird return values. */
+ szPath[cchTarget] = '\0';
+ size_t cchLink = strlen(szPath);
+
+ /* Strip trailing dirslashes of non-final link. */
+ if (!fFinal)
+ while (cchLink > 1 and szPath[cchLink - 1] == '/')
+ cchLink--;
+
+ /* Check link value sanity and buffer size. */
+ if (cchLink == 0)
+ return supR3HardenedSetError3(VERR_ACCESS_DENIED, pErrInfo,
+ "Bad readlink return for '", Info.szPath, "'");
+ if (szPath[0] == '/')
+ return supR3HardenedSetError3(VERR_ACCESS_DENIED, pErrInfo,
+ "Absolute symbolic link not allowed: '", szPath, "'");
+ if (cchBefore + cchLink + cchAfter + 1 /*terminator*/ > sizeof(szPath))
+ return supR3HardenedSetError(VERR_SUPLIB_PATH_TOO_LONG, pErrInfo,
+ "Symlinks causing too long path!");
+
+ /* Construct the new path. */
+ if (cchBefore)
+ memmove(&szPath[cchBefore], &szPath[0], cchLink);
+ memcpy(&szPath[0], Info.szPath, cchBefore);
+ if (!cchAfter)
+ szPath[cchBefore + cchLink] = '\0';
+ else
+ {
+ szPath[cchBefore + cchLink] = RTPATH_SLASH;
+ memcpy(&szPath[cchBefore + cchLink + 1],
+ &Info.szPath[Info.aoffComponents[iComponent + 1]],
+ cchAfter); /* cchAfter includes a zero terminator */
+ }
+
+ /* Parse, copy and check the sanity (no '..' or '.') of the altered path. */
+ rc = supR3HardenedVerifyPathSanity(szPath, pErrInfo, &Info);
+ if (RT_FAILURE(rc))
+ return rc;
+ if (Info.fDirSlash)
+ return supR3HardenedSetError3(VERR_SUPLIB_IS_DIRECTORY, pErrInfo,
+ "The file path specifies a directory: '", szPath, "'");
+
+ /* Restart from the current component. */
+ continue;
+ }
+ int iErr = errno;
+ supR3HardenedError(VERR_ACCESS_DENIED, false /*fFatal*/,
+ "supR3HardenedVerifyFileFollowSymlinks: Failed to readlink '%s': %s (%d)\n",
+ Info.szPath, strerror(iErr), iErr);
+ return supR3HardenedSetError4(VERR_ACCESS_DENIED, pErrInfo,
+ "readlink failed for '", Info.szPath, "': ", strerror(iErr));
+ }
+ return supR3HardenedSetError(VERR_SUPLIB_PATH_TOO_LONG, pErrInfo, "Path too long for symlink replacing!");
+ }
+ else
+ return supR3HardenedSetError3(VERR_TOO_MANY_SYMLINKS, pErrInfo,
+ "Too many symbolic links: '", pszFilename, "'");
+#endif
+ }
+ }
+ if (RT_FAILURE(rc))
+ return rc;
+ Info.szPath[Info.aoffComponents[iComponent + 1] - 1] = !fFinal ? RTPATH_SLASH : '\0';
+ iComponent++;
+ }
+
+ /*
+ * Verify the file handle against the last component, if specified.
+ */
+ if (hNativeFile != RTHCUINTPTR_MAX)
+ {
+ rc = supR3HardenedVerifySameFsObject(hNativeFile, &FsObjState, Info.szPath, pErrInfo);
+ if (RT_FAILURE(rc))
+ return rc;
+ }
+
+ return VINF_SUCCESS;
+}
+#endif /* RT_OS_DARWIN || RT_OS_LINUX */
+
+
+/**
+ * Gets the pre-init data for the hand-over to the other version
+ * of this code.
+ *
+ * The reason why we pass this information on is that it contains
+ * open directories and files. Later it may include even more info
+ * (int the verified arrays mostly).
+ *
+ * The receiver is supR3HardenedRecvPreInitData.
+ *
+ * @param pPreInitData Where to store it.
+ */
+DECLHIDDEN(void) supR3HardenedGetPreInitData(PSUPPREINITDATA pPreInitData)
+{
+ pPreInitData->cInstallFiles = RT_ELEMENTS(g_aSupInstallFiles);
+ pPreInitData->paInstallFiles = &g_aSupInstallFiles[0];
+ pPreInitData->paVerifiedFiles = &g_aSupVerifiedFiles[0];
+
+ pPreInitData->cVerifiedDirs = RT_ELEMENTS(g_aSupVerifiedDirs);
+ pPreInitData->paVerifiedDirs = &g_aSupVerifiedDirs[0];
+}
+
+
+/**
+ * Receives the pre-init data from the static executable stub.
+ *
+ * @returns VBox status code. Will not bitch on failure since the
+ * runtime isn't ready for it, so that is left to the exe stub.
+ *
+ * @param pPreInitData The hand-over data.
+ */
+DECLHIDDEN(int) supR3HardenedRecvPreInitData(PCSUPPREINITDATA pPreInitData)
+{
+ /*
+ * Compare the array lengths and the contents of g_aSupInstallFiles.
+ */
+ if ( pPreInitData->cInstallFiles != RT_ELEMENTS(g_aSupInstallFiles)
+ || pPreInitData->cVerifiedDirs != RT_ELEMENTS(g_aSupVerifiedDirs))
+ return VERR_VERSION_MISMATCH;
+ SUPINSTFILE const *paInstallFiles = pPreInitData->paInstallFiles;
+ for (unsigned iFile = 0; iFile < RT_ELEMENTS(g_aSupInstallFiles); iFile++)
+ if ( g_aSupInstallFiles[iFile].enmDir != paInstallFiles[iFile].enmDir
+ || g_aSupInstallFiles[iFile].enmType != paInstallFiles[iFile].enmType
+ || g_aSupInstallFiles[iFile].fOptional != paInstallFiles[iFile].fOptional
+ || suplibHardenedStrCmp(g_aSupInstallFiles[iFile].pszFile, paInstallFiles[iFile].pszFile))
+ return VERR_VERSION_MISMATCH;
+
+ /*
+ * Check that we're not called out of order.
+ * If dynamic linking it screwed up, we may end up here.
+ */
+ if ( !ASMMemIsZero(&g_aSupVerifiedFiles[0], sizeof(g_aSupVerifiedFiles))
+ || !ASMMemIsZero(&g_aSupVerifiedDirs[0], sizeof(g_aSupVerifiedDirs)))
+ return VERR_WRONG_ORDER;
+
+ /*
+ * Copy the verification data over.
+ */
+ suplibHardenedMemCopy(&g_aSupVerifiedFiles[0], pPreInitData->paVerifiedFiles, sizeof(g_aSupVerifiedFiles));
+ suplibHardenedMemCopy(&g_aSupVerifiedDirs[0], pPreInitData->paVerifiedDirs, sizeof(g_aSupVerifiedDirs));
+ return VINF_SUCCESS;
+}
diff --git a/src/VBox/HostDrivers/Support/SUPSvc.cpp b/src/VBox/HostDrivers/Support/SUPSvc.cpp
new file mode 100644
index 00000000..778d8863
--- /dev/null
+++ b/src/VBox/HostDrivers/Support/SUPSvc.cpp
@@ -0,0 +1,456 @@
+/* $Id: SUPSvc.cpp $ */
+/** @file
+ * VirtualBox Support Service - Common Code.
+ */
+
+/*
+ * Copyright (C) 2008-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#define LOG_GROUP LOG_GROUP_SUP
+#include <VBox/log.h>
+#include <iprt/string.h>
+#include <iprt/mem.h>
+#include <iprt/stream.h>
+#include <iprt/getopt.h>
+
+#include "SUPSvcInternal.h"
+
+
+/*********************************************************************************************************************************
+* Structures and Typedefs *
+*********************************************************************************************************************************/
+/**
+ * Service state.
+ */
+typedef enum SUPSVCSERVICESTATE
+{
+ kSupSvcServiceState_Invalid = 0,
+ kSupSvcServiceState_NotCreated,
+ kSupSvcServiceState_Paused,
+ kSupSvcServiceState_Running,
+ kSupSvcServiceState_End
+} SUPSVCSERVICESTATE;
+
+
+/**
+ * Service descriptor.
+ */
+typedef struct SUPSVCSERVICE
+{
+ /** The service name. */
+ const char *pszName;
+ /** The service state. */
+ SUPSVCSERVICESTATE enmState;
+ /** The instance handle returned by pfnCreate. */
+ void *pvInstance;
+
+ /**
+ * Create the service (don't start it).
+ *
+ * @returns VBox status code, log entry is written on failure.
+ * @param ppvInstance Where to store the instance handle.
+ */
+ DECLCALLBACKMEMBER(int, pfnCreate,(void **ppvInstance));
+
+ /**
+ * Start the service.
+ *
+ * @param pvInstance The instance handle.
+ */
+ DECLCALLBACKMEMBER(void, pfnStart,(void *pvInstance));
+
+ /**
+ * Attempt to stop a running service.
+ *
+ * This should fail if there are active clients. A stopped service
+ * can be restarted by calling pfnStart.
+ *
+ * @returns VBox status code, log entry is written on failure.
+ * @param pvInstance The instance handle.
+ */
+ DECLCALLBACKMEMBER(int, pfnTryStop,(void *pvInstance));
+
+ /**
+ * Destroy the service, stopping first it if necessary.
+ *
+ * @param pvInstance The instance handle.
+ * @param fRunning Whether the service is running or not.
+ */
+ DECLCALLBACKMEMBER(void, pfnStopAndDestroy,(void *pvInstance, bool fRunning));
+} SUPSVCSERVICE;
+/** Pointer to a service descriptor. */
+typedef SUPSVCSERVICE *PSUPSVCSERVICE;
+/** Pointer to a const service descriptor. */
+typedef SUPSVCSERVICE const *PCSUPSVCSERVICE;
+
+
+
+/*********************************************************************************************************************************
+* Global Variables *
+*********************************************************************************************************************************/
+static SUPSVCSERVICE g_aServices[] =
+{
+ {
+ "Global",
+ kSupSvcServiceState_NotCreated,
+ NULL,
+ supSvcGlobalCreate,
+ supSvcGlobalStart,
+ supSvcGlobalTryStop,
+ supSvcGlobalStopAndDestroy,
+ }
+#ifdef RT_OS_WINDOWS
+ ,
+ {
+ "Grant",
+ kSupSvcServiceState_NotCreated,
+ NULL,
+ supSvcGrantCreate,
+ supSvcGrantStart,
+ supSvcGrantTryStop,
+ supSvcGrantStopAndDestroy,
+ }
+#endif
+};
+
+
+
+/**
+ * Instantiates and starts the services.
+ *
+ * @returns VBox status code. Done bitching on failure.
+ */
+int supSvcCreateAndStartServices(void)
+{
+ LogFlowFuncEnter();
+
+ /*
+ * Validate that all services are in the NotCreated state.
+ */
+ unsigned i;
+ for (i = 0; i < RT_ELEMENTS(g_aServices); i++)
+ if (g_aServices[i].enmState != kSupSvcServiceState_NotCreated)
+ {
+ supSvcLogError("service %s in state %d, expected state %d (NotCreated)",
+ g_aServices[i].pszName, g_aServices[i].enmState, kSupSvcServiceState_NotCreated);
+ return VERR_WRONG_ORDER;
+ }
+
+ /*
+ * Create all the services, then start them.
+ */
+ int rc = VINF_SUCCESS;
+ for (i = 0; i < RT_ELEMENTS(g_aServices); i++)
+ {
+ void *pvInstance = NULL;
+ int rc = g_aServices[i].pfnCreate(&pvInstance);
+ if (RT_FAILURE(rc))
+ {
+ Log(("supSvcCreateAndStartServices: %s -> %Rrc\n", g_aServices[i].pszName, rc));
+ break;
+ }
+ g_aServices[i].pvInstance = pvInstance;
+ g_aServices[i].enmState = kSupSvcServiceState_Paused;
+ }
+ if (RT_SUCCESS(rc))
+ {
+ for (i = 0; i < RT_ELEMENTS(g_aServices); i++)
+ {
+ g_aServices[i].pfnStart(g_aServices[i].pvInstance);
+ g_aServices[i].enmState = kSupSvcServiceState_Running;
+ }
+ }
+ else
+ {
+ /*
+ * Destroy any services we managed to instantiate.
+ */
+ while (i-- > 0)
+ {
+ g_aServices[i].pfnStopAndDestroy(g_aServices[i].pvInstance, false /* fRunning */);
+ g_aServices[i].pvInstance = NULL;
+ g_aServices[i].enmState = kSupSvcServiceState_NotCreated;
+ }
+ }
+
+ LogFlow(("supSvcCreateAndStartServices: returns %Rrc\n", rc));
+ return rc;
+}
+
+
+/**
+ * Checks if it's possible to stop the services.
+ *
+ * @returns VBox status code, done bitching on failure.
+ */
+int supSvcTryStopServices(void)
+{
+ LogFlowFuncEnter();
+
+ /*
+ * Check that the services are all created and count the running ones.
+ */
+ unsigned i;
+ unsigned cRunning = 0;
+ for (i = 0; i < RT_ELEMENTS(g_aServices); i++)
+ if (g_aServices[i].enmState == kSupSvcServiceState_Running)
+ cRunning++;
+ else if (g_aServices[i].enmState == kSupSvcServiceState_NotCreated)
+ {
+ supSvcLogError("service %s in state %d (NotCreated), expected pause or running",
+ g_aServices[i].pszName, g_aServices[i].enmState, kSupSvcServiceState_NotCreated);
+ return VERR_WRONG_ORDER;
+ }
+ if (!cRunning)
+ return VINF_SUCCESS; /* all stopped, nothing to do. */
+ Assert(cRunning == RT_ELEMENTS(g_aServices)); /* all or nothing */
+
+ /*
+ * Try stop them in reverse of start order.
+ */
+ int rc = VINF_SUCCESS;
+ i = RT_ELEMENTS(g_aServices);
+ while (i-- > 0)
+ {
+ rc = g_aServices[i].pfnTryStop(g_aServices[i].pvInstance);
+ if (RT_FAILURE(rc))
+ {
+ Log(("supSvcTryStopServices: %s -> %Rrc\n", g_aServices[i].pszName, rc));
+ break;
+ }
+ g_aServices[i].enmState = kSupSvcServiceState_Paused;
+ }
+ if (RT_FAILURE(rc))
+ {
+ /* Failed, restart the ones we succeeded in stopping. */
+ while (++i < RT_ELEMENTS(g_aServices))
+ {
+ g_aServices[i].pfnStart(g_aServices[i].pvInstance);
+ g_aServices[i].enmState = kSupSvcServiceState_Running;
+ }
+ }
+ LogFlow(("supSvcTryStopServices: returns %Rrc\n", rc));
+ return rc;
+}
+
+
+/**
+ * Stops and destroys the services.
+ */
+void supSvcStopAndDestroyServices(void)
+{
+ LogFlowFuncEnter();
+
+ /*
+ * Stop and destroy the service in reverse of start order.
+ */
+ unsigned i = RT_ELEMENTS(g_aServices);
+ while (i-- > 0)
+ if (g_aServices[i].enmState != kSupSvcServiceState_NotCreated)
+ {
+ g_aServices[i].pfnStopAndDestroy(g_aServices[i].pvInstance,
+ g_aServices[i].enmState == kSupSvcServiceState_Running);
+ g_aServices[i].pvInstance = NULL;
+ g_aServices[i].enmState = kSupSvcServiceState_NotCreated;
+ }
+
+ LogFlowFuncLeave();
+}
+
+
+
+/**
+ * Logs the message to the appropriate system log.
+ *
+ * In debug builds this will also put it in the debug log.
+ *
+ * @param pszMsg The log string.
+ *
+ * @remarks This may later be replaced by the release logger and callback destination(s).
+ */
+void supSvcLogErrorStr(const char *pszMsg)
+{
+ supSvcOsLogErrorStr(pszMsg);
+ LogRel(("%s\n", pszMsg));
+}
+
+
+/**
+ * Logs the message to the appropriate system log.
+ *
+ * In debug builds this will also put it in the debug log.
+ *
+ * @param pszFormat The log string. No trailing newline.
+ * @param va Format arguments.
+ *
+ * @todo This should later be replaced by the release logger and callback destination(s).
+ */
+void supSvcLogErrorV(const char *pszFormat, va_list va)
+{
+ if (*pszFormat)
+ {
+ char *pszMsg = NULL;
+ if (RTStrAPrintfV(&pszMsg, pszFormat, va) != -1)
+ {
+ supSvcLogErrorStr(pszMsg);
+ RTStrFree(pszMsg);
+ }
+ else
+ supSvcLogErrorStr(pszFormat);
+ }
+}
+
+
+/**
+ * Logs the error message to the appropriate system log.
+ *
+ * In debug builds this will also put it in the debug log.
+ *
+ * @param pszFormat The log string. No trailing newline.
+ * @param ... Format arguments.
+ *
+ * @todo This should later be replaced by the release logger and callback destination(s).
+ */
+void supSvcLogError(const char *pszFormat, ...)
+{
+ va_list va;
+ va_start(va, pszFormat);
+ supSvcLogErrorV(pszFormat, va);
+ va_end(va);
+}
+
+
+/**
+ * Deals with RTGetOpt failure, bitching in the system log.
+ *
+ * @returns 1
+ * @param pszAction The action name.
+ * @param rc The RTGetOpt return value.
+ * @param argc The argument count.
+ * @param argv The argument vector.
+ * @param iArg The argument index.
+ * @param pValue The value returned by RTGetOpt.
+ */
+int supSvcLogGetOptError(const char *pszAction, int rc, int argc, char **argv, int iArg, PCRTOPTIONUNION pValue)
+{
+ supSvcLogError("%s - RTGetOpt failure, %Rrc (%d): %s",
+ pszAction, rc, rc, iArg < argc ? argv[iArg] : "<null>");
+ return 1;
+}
+
+
+/**
+ * Bitch about too many arguments (after RTGetOpt stops) in the system log.
+ *
+ * @returns 1
+ * @param pszAction The action name.
+ * @param argc The argument count.
+ * @param argv The argument vector.
+ * @param iArg The argument index.
+ */
+int supSvcLogTooManyArgsError(const char *pszAction, int argc, char **argv, int iArg)
+{
+ Assert(iArg < argc);
+ supSvcLogError("%s - Too many arguments: %s", pszAction, argv[iArg]);
+ for ( ; iArg < argc; iArg++)
+ LogRel(("arg#%i: %s\n", iArg, argv[iArg]));
+ return 1;
+}
+
+
+/**
+ * Prints an error message to the screen.
+ *
+ * @param pszFormat The message format string.
+ * @param va Format arguments.
+ */
+void supSvcDisplayErrorV(const char *pszFormat, va_list va)
+{
+ RTStrmPrintf(g_pStdErr, "VBoxSupSvc error: ");
+ RTStrmPrintfV(g_pStdErr, pszFormat, va);
+ Log(("supSvcDisplayErrorV: %s", pszFormat)); /** @todo format it! */
+}
+
+
+/**
+ * Prints an error message to the screen.
+ *
+ * @param pszFormat The message format string.
+ * @param ... Format arguments.
+ */
+void supSvcDisplayError(const char *pszFormat, ...)
+{
+ va_list va;
+ va_start(va, pszFormat);
+ supSvcDisplayErrorV(pszFormat, va);
+ va_end(va);
+}
+
+
+/**
+ * Deals with RTGetOpt failure.
+ *
+ * @returns 1
+ * @param pszAction The action name.
+ * @param rc The RTGetOpt return value.
+ * @param argc The argument count.
+ * @param argv The argument vector.
+ * @param iArg The argument index.
+ * @param pValue The value returned by RTGetOpt.
+ */
+int supSvcDisplayGetOptError(const char *pszAction, int rc, int argc, char **argv, int iArg, PCRTOPTIONUNION pValue)
+{
+ supSvcDisplayError("%s - RTGetOpt failure, %Rrc (%d): %s\n",
+ pszAction, rc, rc, iArg < argc ? argv[iArg] : "<null>");
+ return 1;
+}
+
+
+/**
+ * Bitch about too many arguments (after RTGetOpt stops).
+ *
+ * @returns 1
+ * @param pszAction The action name.
+ * @param argc The argument count.
+ * @param argv The argument vector.
+ * @param iArg The argument index.
+ */
+int supSvcDisplayTooManyArgsError(const char *pszAction, int argc, char **argv, int iArg)
+{
+ Assert(iArg < argc);
+ supSvcDisplayError("%s - Too many arguments: %s\n", pszAction, argv[iArg]);
+ return 1;
+}
+
diff --git a/src/VBox/HostDrivers/Support/SUPSvcGlobal.cpp b/src/VBox/HostDrivers/Support/SUPSvcGlobal.cpp
new file mode 100644
index 00000000..62b526d4
--- /dev/null
+++ b/src/VBox/HostDrivers/Support/SUPSvcGlobal.cpp
@@ -0,0 +1,79 @@
+/* $Id: SUPSvcGlobal.cpp $ */
+/** @file
+ * VirtualBox Support Service - The Global Service.
+ */
+
+/*
+ * Copyright (C) 2008-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include "SUPSvcInternal.h"
+
+#include <iprt/errcore.h>
+#include <iprt/assert.h>
+
+
+/** @copydoc SUPSVCSERVICE::pfnCreate */
+DECLCALLBACK(int) supSvcGlobalCreate(void **ppvInstance)
+{
+ *ppvInstance = (void *)1;
+ return VINF_SUCCESS;
+}
+
+
+/** @copydoc SUPSVCSERVICE::pfnStart */
+DECLCALLBACK(void) supSvcGlobalStart(void *pvInstance)
+{
+ Assert(pvInstance == (void *)1);
+ NOREF(pvInstance);
+}
+
+
+/** @copydoc SUPSVCSERVICE::pfnTryStop */
+DECLCALLBACK(int) supSvcGlobalTryStop(void *pvInstance)
+{
+ Assert(pvInstance == (void *)1);
+ NOREF(pvInstance);
+ return VINF_SUCCESS;
+}
+
+
+/** @copydoc SUPSVCSERVICE::pfnStopAndDestroy */
+DECLCALLBACK(void) supSvcGlobalStopAndDestroy(void *pvInstance, bool fRunning)
+{
+ Assert(pvInstance == (void *)1);
+ NOREF(pvInstance);
+ NOREF(fRunning);
+}
+
diff --git a/src/VBox/HostDrivers/Support/SUPSvcGrant.cpp b/src/VBox/HostDrivers/Support/SUPSvcGrant.cpp
new file mode 100644
index 00000000..80fa05ed
--- /dev/null
+++ b/src/VBox/HostDrivers/Support/SUPSvcGrant.cpp
@@ -0,0 +1,1012 @@
+/* $Id: SUPSvcGrant.cpp $ */
+/** @file
+ * VirtualBox Support Service - The Grant Service.
+ */
+
+/*
+ * Copyright (C) 2008-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#define LOG_GROUP LOG_GROUP_SUP
+#include "SUPSvcInternal.h"
+
+#include <VBox/log.h>
+#include <iprt/asm.h>
+#include <iprt/errcore.h>
+#include <iprt/assert.h>
+#include <iprt/critsect.h>
+#include <iprt/mem.h>
+#include <iprt/semaphore.h>
+#include <iprt/thread.h>
+#include <iprt/time.h>
+#include <iprt/localipc.h>
+
+
+/*********************************************************************************************************************************
+* Structures and Typedefs *
+*********************************************************************************************************************************/
+/** Pointer to a client instance. */
+typedef struct SUPSVCGRANTSESSION *PSUPSVCGRANTSESSION;
+/** Pointer to a Grant service instance. */
+typedef struct SUPSVCGRANT *PSUPSVCGRANT;
+
+
+/**
+ * Grant service session data.
+ */
+typedef struct SUPSVCGRANTSESSION
+{
+ /** Pointer to the next client in the list. */
+ PSUPSVCGRANTSESSION pNext;
+ /** Pointer to the previous client in the list. */
+ PSUPSVCGRANTSESSION pPrev;
+ /** Pointer to the parent (the service instance). */
+ PSUPSVCGRANT volatile pParent;
+ /** The local ipc client handle. */
+ RTLOCALIPCSESSION volatile hSession;
+ /** Indicate that the thread should terminate ASAP. */
+ bool volatile fTerminate;
+ /** The thread handle. */
+ RTTHREAD hThread;
+
+} SUPSVCGRANTSESSION;
+
+
+/**
+ * State grant service machine.
+ */
+typedef enum SUPSVCGRANTSTATE
+{
+ /** The invalid zero entry. */
+ kSupSvcGrantState_Invalid = 0,
+ /** Creating - the thread is being started.
+ * Next: Paused or Butchered. */
+ kSupSvcGrantState_Creating,
+ /** Paused - the thread is blocked on it's user event semaphore.
+ * Next: Resuming, Terminating or Butchered.
+ * Prev: Creating, Pausing */
+ kSupSvcGrantState_Paused,
+ /** Resuming - the thread is being unblocked and ushered into RTLocalIpcServiceListen.
+ * Next: Listen or Butchered.
+ * Prev: Paused */
+ kSupSvcGrantState_Resuming,
+ /** Listen - the thread is in RTLocalIpcServerListen or setting up an incoming session.
+ * Next: Pausing or Butchered.
+ * Prev: Resuming */
+ kSupSvcGrantState_Listen,
+ /** Pausing - Cancelling the listen and dropping any incoming sessions.
+ * Next: Paused or Butchered.
+ * Prev: Listen */
+ kSupSvcGrantState_Pausing,
+ /** Butchered - The thread has quit because something when terribly wrong.
+ * Next: Destroyed
+ * Prev: Any. */
+ kSupSvcGrantState_Butchered,
+ /** Pausing - Cancelling the listen and dropping any incoming sessions.
+ * Next: Destroyed
+ * Prev: Paused */
+ kSupSvcGrantState_Terminating,
+ /** Destroyed - the instance is invalid.
+ * Prev: Butchered or Terminating */
+ kSupSvcGrantState_Destroyed,
+ /** The end of valid state values. */
+ kSupSvcGrantState_End,
+ /** The usual 32-bit blowup hack. */
+ kSupSvcGrantState_32BitHack = 0x7fffffff
+} SUPSVCGRANTSTATE;
+
+
+/**
+ * Grant service instance data.
+ */
+typedef struct SUPSVCGRANT
+{
+ /** The local ipc server handle. */
+ RTLOCALIPCSERVER hServer;
+
+ /** Critical section serializing access to the session list, the state,
+ * the response event, the session event, and the thread event. */
+ RTCRITSECT CritSect;
+ /** The service thread will signal this event when it has changed to
+ * the 'paused' or 'running' state. */
+ RTSEMEVENT hResponseEvent;
+ /** Event that's signaled on session termination. */
+ RTSEMEVENT hSessionEvent;
+ /** The handle to the service thread. */
+ RTTHREAD hThread;
+ /** Head of the session list. */
+ PSUPSVCGRANTSESSION volatile pSessionHead;
+ /** The service state. */
+ SUPSVCGRANTSTATE volatile enmState;
+
+ /** Critical section serializing access to the SUPR3HardenedVerify APIs. */
+ RTCRITSECT VerifyCritSect;
+} SUPSVCGRANT;
+
+
+/*********************************************************************************************************************************
+* Internal Functions *
+*********************************************************************************************************************************/
+static const char *supSvcGrantStateName(SUPSVCGRANTSTATE enmState);
+
+
+
+
+/**
+ * Services a client session.
+ *
+ * @returns VINF_SUCCESS.
+ *
+ * @param hThread The thread handle.
+ * @param pvSession Pointer to the session instance data.
+ */
+static DECLCALLBACK(int) supSvcGrantSessionThread(RTTHREAD hThread, void *pvSession)
+{
+ PSUPSVCGRANTSESSION pThis = (PSUPSVCGRANTSESSION)pvSession;
+ RTLOCALIPCSESSION hSession = pThis->hSession;
+ Log(("supSvcGrantSessionThread(%p):\n", pThis));
+
+ /*
+ * Process client requests until it quits or we're cancelled on termination.
+ */
+ while (!ASMAtomicUoReadBool(&pThis->fTerminate))
+ {
+ RTThreadSleep(1000);
+ /** @todo */
+ }
+
+ /*
+ * Clean up the session.
+ */
+ PSUPSVCGRANT pParent = ASMAtomicReadPtrT(&pThis->pParent, PSUPSVCGRANT);
+ if (pParent)
+ RTCritSectEnter(&pParent->CritSect);
+ else
+ Log(("supSvcGrantSessionThread(%p): No parent\n", pThis));
+
+ ASMAtomicXchgHandle(&pThis->hSession, NIL_RTLOCALIPCSESSION, &hSession);
+ if (hSession != NIL_RTLOCALIPCSESSION)
+ RTLocalIpcSessionClose(hSession);
+ else
+ Log(("supSvcGrantSessionThread(%p): No session handle\n", pThis));
+
+ if (pParent)
+ {
+ RTSemEventSignal(pParent->hSessionEvent);
+ RTCritSectLeave(&pParent->CritSect);
+ }
+ Log(("supSvcGrantSessionThread(%p): exits\n"));
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * Cleans up a session.
+ *
+ * This is called while inside the grant service critical section.
+ *
+ * @param pThis The session to destroy.
+ * @param pParent The parent.
+ */
+static void supSvcGrantSessionDestroy(PSUPSVCGRANTSESSION pThis, PSUPSVCGRANT pParent)
+{
+ /*
+ * Unlink it.
+ */
+ if (pThis->pNext)
+ {
+ Assert(pThis->pNext->pPrev == pThis);
+ pThis->pNext->pPrev = pThis->pPrev;
+ }
+
+ if (pThis->pPrev)
+ {
+ Assert(pThis->pPrev->pNext == pThis);
+ pThis->pPrev->pNext = pThis->pNext;
+ }
+ else if (pParent->pSessionHead == pThis)
+ pParent->pSessionHead = pThis->pNext;
+
+ /*
+ * Free the resources associated with it.
+ */
+ pThis->hThread = NIL_RTTHREAD;
+ pThis->pNext = NULL;
+ pThis->pPrev = NULL;
+
+ RTLOCALIPCSESSION hSession;
+ ASMAtomicXchgHandle(&pThis->hSession, NIL_RTLOCALIPCSESSION, &hSession);
+ if (hSession != NIL_RTLOCALIPCSESSION)
+ RTLocalIpcSessionClose(hSession);
+
+ RTMemFree(pThis);
+}
+
+
+/**
+ * Cleans up zombie sessions, locked.
+ *
+ * @param pThis Pointer to the grant service instance data.
+ */
+static void supSvcGrantCleanUpSessionsLocked(PSUPSVCGRANT pThis)
+{
+ /*
+ * Iterate until be make it all the way thru the list.
+ *
+ * Only use the thread state as and indicator on whether we can destroy
+ * the session or not.
+ */
+ PSUPSVCGRANTSESSION pCur;
+ do
+ {
+ for (pCur = pThis->pSessionHead; pCur; pCur = pCur->pNext)
+ {
+ int rc = RTThreadWait(pCur->hThread, 0, NULL);
+ if (RT_SUCCESS(rc))
+ {
+ supSvcGrantSessionDestroy(pCur, pThis);
+ break;
+ }
+
+ Assert(rc == VERR_TIMEOUT);
+ Assert(pCur->hThread != NIL_RTTHREAD);
+ Assert(pCur->pNext != pThis->pSessionHead);
+ }
+ } while (pCur);
+}
+
+
+/**
+ * Cleans up zombie sessions.
+ *
+ * @returns VINF_SUCCESS, VBox error code on internal error.
+ *
+ * @param pThis Pointer to the grant service instance data.
+ * @param fOwnCritSect Whether we own the crit sect already. The state is preserved.
+ */
+static int supSvcGrantCleanUpSessions(PSUPSVCGRANT pThis, bool fOwnCritSect)
+{
+ int rc = RTCritSectEnter(&pThis->CritSect);
+ if (RT_FAILURE(rc))
+ {
+ supSvcLogError("supSvcGrantCleanUpSessions: RTCritSectEnter returns %Rrc", rc);
+ return rc;
+ }
+
+ supSvcGrantCleanUpSessionsLocked(pThis);
+
+ RTCritSectLeave(&pThis->CritSect);
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * Gets the state name.
+ *
+ * @returns The state name string (read only).
+ * @param enmState The state.
+ */
+static const char *supSvcGrantStateName(SUPSVCGRANTSTATE enmState)
+{
+ switch (enmState)
+ {
+ case kSupSvcGrantState_Invalid: return "Invalid";
+ case kSupSvcGrantState_Creating: return "Creating";
+ case kSupSvcGrantState_Paused: return "Paused";
+ case kSupSvcGrantState_Resuming: return "Resuming";
+ case kSupSvcGrantState_Listen: return "Listen";
+ case kSupSvcGrantState_Pausing: return "Pausing";
+ case kSupSvcGrantState_Butchered: return "Butchered";
+ case kSupSvcGrantState_Terminating: return "Terminating";
+ case kSupSvcGrantState_Destroyed: return "Destroyed";
+ default: return "?Unknown?";
+ }
+}
+
+
+/**
+ * Attempts to flip into the butchered state.
+ *
+ * @returns rc.
+ * @param pThis The instance data.
+ * @param fOwnCritSect Whether we own the crit sect already.
+ * @param pszFailed What failed.
+ * @param rc What to return (lazy bird).
+ */
+static int supSvcGrantThreadButchered(PSUPSVCGRANT pThis, bool fOwnCritSect, const char *pszFailed, int rc)
+{
+ int rc2 = VINF_SUCCESS;
+ if (!fOwnCritSect)
+ rc2 = RTCritSectEnter(&pThis->CritSect);
+ if (RT_SUCCESS(rc2))
+ {
+ supSvcLogError("supSvcGrantThread(%s): Butchered; %Rrc: %s",
+ supSvcGrantStateName(pThis->enmState), rc, pszFailed);
+ pThis->enmState = kSupSvcGrantState_Butchered;
+
+ RTCritSectLeave(&pThis->CritSect);
+ }
+ return rc;
+}
+
+
+/**
+ * Creates a new session.
+ *
+ * @returns VINF_SUCCESS on success, VBox error code on internal error.
+ *
+ * @param pThis Pointer to the grant service instance data.
+ * @param hSession The client session handle.
+ */
+static int supSvcGrantThreadCreateSession(PSUPSVCGRANT pThis, RTLOCALIPCSESSION hSession)
+{
+ /*
+ * Allocate and initialize a new session instance before entering the critsect.
+ */
+ PSUPSVCGRANTSESSION pSession = (PSUPSVCGRANTSESSION)RTMemAlloc(sizeof(*pSession));
+ if (!pSession)
+ {
+ supSvcLogError("supSvcGrantThreadListen: failed to allocate session");
+ return VINF_SUCCESS; /* not fatal? */
+ }
+ pSession->pPrev = NULL;
+ pSession->pNext = NULL;
+ pSession->pParent = pThis;
+ pSession->hSession = hSession;
+ pSession->fTerminate = false;
+ pSession->hThread = NIL_RTTHREAD;
+
+ /*
+ * Enter the critsect, check the state, link it and fire off the session thread.
+ */
+ int rc = RTCritSectEnter(&pThis->CritSect);
+ if (RT_SUCCESS(rc))
+ {
+ /* check the state */
+ SUPSVCGRANTSTATE enmState = pThis->enmState;
+ if (enmState == kSupSvcGrantState_Listen)
+ {
+ /* link it */
+ pSession->pNext = pThis->pSessionHead;
+ if (pThis->pSessionHead)
+ pThis->pSessionHead->pPrev = pSession;
+ pThis->pSessionHead = pSession;
+
+ /* fire up the thread */
+ Log(("supSvcGrantThreadListen: starting session %p\n", pSession));
+ rc = RTThreadCreate(&pSession->hThread, supSvcGrantSessionThread, pSession, 0,
+ RTTHREADTYPE_DEFAULT, RTTHREADFLAGS_WAITABLE, "SESSION");
+ if (RT_SUCCESS(rc))
+ {
+ rc = RTCritSectLeave(&pThis->CritSect);
+ if (RT_FAILURE(rc))
+ return supSvcGrantThreadButchered(pThis, false /* fOwnCritSect */, "RTCritSectLeave", rc);
+
+ /*
+ * Successfully handled the client.
+ */
+ return VINF_SUCCESS;
+ }
+
+ /* bail out */
+ supSvcLogError("supSvcGrantThreadListen: RTThreadCreate returns %Rrc", rc);
+ }
+ else
+ Log(("supSvcGrantThreadListen: dropping connection, state %s\n", supSvcGrantStateName(enmState)));
+
+ RTCritSectLeave(&pThis->CritSect);
+ rc = VINF_SUCCESS;
+ }
+ else
+ supSvcGrantThreadButchered(pThis, false /* fOwnCritSect */, "RTCritSectEnter", rc);
+ RTLocalIpcSessionClose(hSession);
+ RTMemFree(pSession);
+ return rc;
+}
+
+
+/**
+ * Listen for a client session and kicks off the service thread for it.
+ *
+ * @returns VINF_SUCCESS on normal state change, failure if something gets screwed up.
+ *
+ * @param pThis Pointer to the grant service instance data.
+ */
+static int supSvcGrantThreadListen(PSUPSVCGRANT pThis)
+{
+ /*
+ * Wait for a client to connect and create a new session.
+ */
+ RTLOCALIPCSESSION hClientSession = NIL_RTLOCALIPCSESSION;
+ int rc = RTLocalIpcServerListen(pThis->hServer, &hClientSession);
+ if (RT_FAILURE(rc))
+ {
+ if (rc == VERR_CANCELLED)
+ LogFlow(("supSvcGrantThreadListen: cancelled\n"));
+ else if (rc == VERR_TRY_AGAIN)
+ /* for testing */;
+ else
+ return supSvcGrantThreadButchered(pThis, false /* fOwnCritSect */, "RTLocalIpcServerListen", rc);
+ return VINF_SUCCESS;
+ }
+
+ return supSvcGrantThreadCreateSession(pThis, hClientSession);
+}
+
+
+/**
+ * Grant service thread.
+ *
+ * This thread is the one listening for clients and kicks off
+ * the session threads and stuff.
+ *
+ * @returns VINF_SUCCESS on normal exit, VBox error status on failure.
+ * @param hThread The thread handle.
+ * @param pvThis Pointer to the grant service instance data.
+ */
+static DECLCALLBACK(int) supSvcGrantThread(RTTHREAD hThread, void *pvThis)
+{
+ PSUPSVCGRANT pThis = (PSUPSVCGRANT)pvThis;
+
+ /*
+ * The state loop.
+ */
+ for (;;)
+ {
+ /*
+ * Switch on the current state (requires critsect).
+ */
+ int rc = RTCritSectEnter(&pThis->CritSect);
+ if (RT_FAILURE(rc))
+ {
+ supSvcLogError("supSvcGrantThread - RTCritSectEnter returns %Rrc", rc);
+ return rc;
+ }
+
+ SUPSVCGRANTSTATE enmState = pThis->enmState;
+ LogFlow(("supSvcGrantThread: switching %s\n", supSvcGrantStateName(enmState)));
+ switch (enmState)
+ {
+ case kSupSvcGrantState_Creating:
+ case kSupSvcGrantState_Pausing:
+ pThis->enmState = kSupSvcGrantState_Paused;
+ rc = RTSemEventSignal(pThis->hResponseEvent);
+ if (RT_FAILURE(rc))
+ return supSvcGrantThreadButchered(pThis, true /* fOwnCritSect*/, "RTSemEventSignal", rc);
+ RT_FALL_THRU();
+
+ case kSupSvcGrantState_Paused:
+ RTCritSectLeave(&pThis->CritSect);
+
+ rc = RTThreadUserWait(hThread, 60*1000); /* wake up once in a while (paranoia) */
+ if (RT_FAILURE(rc) && rc != VERR_TIMEOUT)
+ return supSvcGrantThreadButchered(pThis, false /* fOwnCritSect*/, "RTThreadUserWait", rc);
+ break;
+
+ case kSupSvcGrantState_Resuming:
+ pThis->enmState = kSupSvcGrantState_Listen;
+ rc = RTSemEventSignal(pThis->hResponseEvent);
+ if (RT_FAILURE(rc))
+ return supSvcGrantThreadButchered(pThis, true /* fOwnCritSect*/, "RTSemEventSignal", rc);
+ RT_FALL_THRU();
+
+ case kSupSvcGrantState_Listen:
+ RTCritSectLeave(&pThis->CritSect);
+ rc = supSvcGrantThreadListen(pThis);
+ if (RT_FAILURE(rc))
+ {
+ Log(("supSvcGrantThread: supSvcGrantDoListening returns %Rrc, exiting\n", rc));
+ return rc;
+ }
+ break;
+
+ case kSupSvcGrantState_Terminating:
+ RTCritSectLeave(&pThis->CritSect);
+ Log(("supSvcGrantThread: Done\n"));
+ return VINF_SUCCESS;
+
+ case kSupSvcGrantState_Butchered:
+ default:
+ return supSvcGrantThreadButchered(pThis, true /* fOwnCritSect*/, "Bad state", VERR_INTERNAL_ERROR);
+ }
+
+ /*
+ * Massage the session list between clients and states.
+ */
+ rc = supSvcGrantCleanUpSessions(pThis, false /* fOwnCritSect */);
+ if (RT_FAILURE(rc))
+ return supSvcGrantThreadButchered(pThis, false /* fOwnCritSect */, "supSvcGrantCleanUpSessions", rc);
+ }
+}
+
+
+/**
+ * Waits for the service thread to respond to a state change.
+ *
+ * @returns VINF_SUCCESS on success, VERR_TIMEOUT if it doesn't respond in time, other error code on internal error.
+ *
+ * @param pThis Pointer to the grant service instance data.
+ * @param enmCurState The current state.
+ * @param enmNewState The new state we're waiting for it to enter.
+ */
+static int supSvcGrantWait(PSUPSVCGRANT pThis, SUPSVCGRANTSTATE enmCurState, SUPSVCGRANTSTATE enmNewState)
+{
+ LogFlow(("supSvcGrantWait(,%s,%s): enter\n",
+ supSvcGrantStateName(enmCurState), supSvcGrantStateName(enmNewState)));
+
+ /*
+ * Wait a short while for the response event to be set.
+ */
+ RTSemEventWait(pThis->hResponseEvent, 1000);
+ int rc = RTCritSectEnter(&pThis->CritSect);
+ if (RT_SUCCESS(rc))
+ {
+ if (pThis->enmState == enmNewState)
+ {
+ RTCritSectLeave(&pThis->CritSect);
+ rc = VINF_SUCCESS;
+ }
+ else if (pThis->enmState == enmCurState)
+ {
+ /*
+ * Wait good while longer.
+ */
+ RTCritSectLeave(&pThis->CritSect);
+ rc = RTSemEventWait(pThis->hResponseEvent, 59*1000); /* 59 sec */
+ if (RT_SUCCESS(rc) || rc == VERR_TIMEOUT)
+ {
+ rc = RTCritSectEnter(&pThis->CritSect);
+ if (RT_SUCCESS(rc))
+ {
+ /*
+ * Check the state whether we've succeeded.
+ */
+ SUPSVCGRANTSTATE enmState = pThis->enmState;
+ if (enmState == enmNewState)
+ rc = VINF_SUCCESS;
+ else if (enmState == enmCurState)
+ {
+ supSvcLogError("supSvcGrantWait(,%s,%s) - the thread doesn't respond in a timely manner, failing.",
+ supSvcGrantStateName(enmCurState), supSvcGrantStateName(enmNewState));
+ rc = VERR_TIMEOUT;
+ }
+ else
+ {
+ supSvcLogError("supSvcGrantWait(,%s,%s) - wrong state %s!", supSvcGrantStateName(enmCurState),
+ supSvcGrantStateName(enmNewState), supSvcGrantStateName(enmState));
+ AssertMsgFailed(("%s\n", supSvcGrantStateName(enmState)));
+ rc = VERR_INTERNAL_ERROR;
+ }
+
+ RTCritSectLeave(&pThis->CritSect);
+ }
+ else
+ supSvcLogError("supSvcGrantWait(,%s,%s) - RTCritSectEnter returns %Rrc",
+ supSvcGrantStateName(enmCurState), supSvcGrantStateName(enmNewState));
+ }
+ else
+ supSvcLogError("supSvcGrantWait(,%s,%s) - RTSemEventWait returns %Rrc",
+ supSvcGrantStateName(enmCurState), supSvcGrantStateName(enmNewState));
+ }
+ else
+ {
+ supSvcLogError("supSvcGrantWait(,%s,%s) - wrong state %s!", supSvcGrantStateName(enmCurState),
+ supSvcGrantStateName(enmNewState), supSvcGrantStateName(pThis->enmState));
+ AssertMsgFailed(("%s\n", supSvcGrantStateName(pThis->enmState)));
+ RTCritSectLeave(&pThis->CritSect);
+ rc = VERR_INTERNAL_ERROR;
+ }
+ }
+ else
+ supSvcLogError("supSvcGrantWait(,%s,%s) - RTCritSectEnter returns %Rrc",
+ supSvcGrantStateName(enmCurState), supSvcGrantStateName(enmNewState));
+
+ Log(("supSvcGrantWait(,%s,%s): returns %Rrc\n",
+ supSvcGrantStateName(enmCurState), supSvcGrantStateName(enmNewState), rc));
+ return rc;
+}
+
+
+/** @copydoc SUPSVCSERVICE::pfnCreate */
+DECLCALLBACK(int) supSvcGrantCreate(void **ppvInstance)
+{
+ LogFlowFuncEnter();
+
+ /*
+ * Allocate and initialize the session data.
+ */
+ PSUPSVCGRANT pThis = (PSUPSVCGRANT)RTMemAlloc(sizeof(*pThis));
+ if (!pThis)
+ {
+ supSvcLogError("supSvcGrantCreate - no memory");
+ return VERR_NO_MEMORY;
+ }
+ bool fFreeIt = true;
+ pThis->pSessionHead = NULL;
+ pThis->enmState = kSupSvcGrantState_Creating;
+ int rc = RTCritSectInit(&pThis->VerifyCritSect);
+ if (RT_SUCCESS(rc))
+ {
+ rc = RTCritSectInit(&pThis->CritSect);
+ if (RT_SUCCESS(rc))
+ {
+ rc = RTSemEventCreate(&pThis->hResponseEvent);
+ if (RT_SUCCESS(rc))
+ {
+ rc = RTSemEventCreate(&pThis->hSessionEvent);
+ if (RT_SUCCESS(rc))
+ {
+ /*
+ * Create the local IPC instance and then finally fire up the thread.
+ */
+ rc = RTLocalIpcServerCreate(&pThis->hServer, SUPSVC_GRANT_SERVICE_NAME, RTLOCALIPC_FLAGS_MULTI_SESSION);
+ if (RT_SUCCESS(rc))
+ {
+ rc = RTThreadCreate(&pThis->hThread, supSvcGrantThread, pThis, 0, RTTHREADTYPE_DEFAULT, RTTHREADFLAGS_WAITABLE, "GRANT");
+ if (RT_SUCCESS(rc))
+ {
+ rc = supSvcGrantWait(pThis, kSupSvcGrantState_Creating, kSupSvcGrantState_Paused);
+ if (RT_SUCCESS(rc))
+ {
+ /*
+ * Successfully created the grant service!
+ */
+ Log(("supSvcGrantCreate: returns VINF_SUCCESS (pThis=%p)\n", pThis));
+ *ppvInstance = pThis;
+ return VINF_SUCCESS;
+ }
+
+ /*
+ * The thread FAILED to start in a timely manner!
+ */
+ RTCritSectEnter(&pThis->CritSect);
+ pThis->enmState = kSupSvcGrantState_Terminating;
+ RTCritSectLeave(&pThis->CritSect);
+
+ RTThreadUserSignal(pThis->hThread);
+
+ int cTries = 10;
+ int rc2 = RTThreadWait(pThis->hThread, 20000, NULL);
+ if (RT_FAILURE(rc2))
+ {
+ /* poke it a few more times before giving up. */
+ while (--cTries > 0)
+ {
+ RTThreadUserSignal(pThis->hThread);
+ RTLocalIpcServerCancel(pThis->hServer);
+ if (RTThreadWait(pThis->hThread, 1000, NULL) != VERR_TIMEOUT)
+ break;
+ }
+ }
+ fFreeIt = cTries <= 0;
+ }
+ else
+ supSvcLogError("supSvcGrantCreate - RTThreadCreate returns %Rrc", rc);
+ RTLocalIpcServerDestroy(pThis->hServer);
+ pThis->hServer = NIL_RTLOCALIPCSERVER;
+ }
+ else
+ supSvcLogError("supSvcGrantCreate - RTLocalIpcServiceCreate returns %Rrc", rc);
+ RTSemEventDestroy(pThis->hSessionEvent);
+ pThis->hSessionEvent = NIL_RTSEMEVENT;
+ }
+ else
+ supSvcLogError("supSvcGrantCreate - RTSemEventCreate returns %Rrc", rc);
+ RTSemEventDestroy(pThis->hResponseEvent);
+ pThis->hResponseEvent = NIL_RTSEMEVENT;
+ }
+ else
+ supSvcLogError("supSvcGrantCreate - RTSemEventCreate returns %Rrc", rc);
+ RTCritSectDelete(&pThis->CritSect);
+ }
+ else
+ supSvcLogError("supSvcGrantCreate - RTCritSectInit returns %Rrc", rc);
+ RTCritSectDelete(&pThis->VerifyCritSect);
+ }
+ else
+ supSvcLogError("supSvcGrantCreate - RTCritSectInit returns %Rrc", rc);
+ if (fFreeIt)
+ RTMemFree(pThis);
+ Log(("supSvcGrantCreate: returns %Rrc\n", rc));
+ return rc;
+}
+
+
+/** @copydoc SUPSVCSERVICE::pfnStart */
+DECLCALLBACK(void) supSvcGrantStart(void *pvInstance)
+{
+ PSUPSVCGRANT pThis = (PSUPSVCGRANT)pvInstance;
+
+ /*
+ * Change the state and signal the thread.
+ */
+ int rc = RTCritSectEnter(&pThis->CritSect);
+ if (RT_SUCCESS(rc))
+ {
+ bool fInCritSect = true;
+ SUPSVCGRANTSTATE enmState = pThis->enmState;
+ if (enmState == kSupSvcGrantState_Paused)
+ {
+ pThis->enmState = kSupSvcGrantState_Resuming;
+ rc = RTThreadUserSignal(pThis->hThread);
+ if (RT_SUCCESS(rc))
+ {
+ /*
+ * Wait for the bugger to respond (no need to bitch here).
+ */
+ RTCritSectLeave(&pThis->CritSect);
+ supSvcGrantWait(pThis, kSupSvcGrantState_Resuming, kSupSvcGrantState_Listen);
+ fInCritSect = false;
+ }
+ }
+ else
+ supSvcLogError("supSvcGrantStart - Incorrect state %s!", supSvcGrantStateName(enmState));
+ if (fInCritSect)
+ RTCritSectLeave(&pThis->CritSect);
+ }
+ else
+ {
+ supSvcLogError("supSvcGrantStart - RTCritSectEnter returns %Rrc!", rc);
+ AssertRCReturnVoid(rc);
+ }
+}
+
+
+/** @copydoc SUPSVCSERVICE::pfnTryStop */
+DECLCALLBACK(int) supSvcGrantTryStop(void *pvInstance)
+{
+ PSUPSVCGRANT pThis = (PSUPSVCGRANT)pvInstance;
+
+ /*
+ * Don't give up immediately.
+ */
+ uint64_t u64StartTS = RTTimeMilliTS();
+ int rc;
+ for (;;)
+ {
+ /*
+ * First check the state to make sure the thing is actually running.
+ * If the critsect is butchered, just pretend success.
+ */
+ rc = RTCritSectEnter(&pThis->CritSect);
+ if (RT_FAILURE(rc))
+ {
+ supSvcLogError("supSvcGrantTryStop - RTCritSectEnter returns %Rrc", rc);
+ AssertRC(rc);
+ return VINF_SUCCESS;
+ }
+ SUPSVCGRANTSTATE enmState = pThis->enmState;
+ if (enmState != kSupSvcGrantState_Listen)
+ {
+ supSvcLogError("supSvcGrantTryStop - Not running, state: %s", supSvcGrantStateName(enmState));
+ RTCritSectLeave(&pThis->CritSect);
+ return VINF_SUCCESS;
+ }
+
+ /*
+ * If there are no clients, usher the thread into the paused state.
+ */
+ supSvcGrantCleanUpSessionsLocked(pThis);
+ if (!pThis->pSessionHead)
+ {
+ rc = RTThreadUserReset(pThis->hThread);
+ pThis->enmState = kSupSvcGrantState_Pausing;
+ int rc2 = RTLocalIpcServerCancel(pThis->hServer);
+ int rc3 = RTCritSectLeave(&pThis->CritSect);
+ if (RT_SUCCESS(rc) && RT_SUCCESS(rc2) && RT_SUCCESS(rc3))
+ supSvcGrantWait(pThis, kSupSvcGrantState_Pausing, kSupSvcGrantState_Paused);
+ else
+ {
+ if (RT_FAILURE(rc))
+ supSvcLogError("supSvcGrantTryStop - RTThreadUserReset returns %Rrc", rc);
+ if (RT_FAILURE(rc2))
+ supSvcLogError("supSvcGrantTryStop - RTLocalIpcServerCancel returns %Rrc", rc);
+ if (RT_FAILURE(rc3))
+ supSvcLogError("supSvcGrantTryStop - RTCritSectLeave returns %Rrc", rc);
+ }
+ return VINF_SUCCESS;
+ }
+
+ /*
+ * Check the time limit, otherwise wait for a client event.
+ */
+ uint64_t u64Elapsed = RTTimeMilliTS() - u64StartTS;
+ if (u64Elapsed >= 60*1000) /* 1 min */
+ {
+ unsigned cSessions = 0;
+ for (PSUPSVCGRANTSESSION pCur = pThis->pSessionHead; pCur; pCur = pCur->pNext)
+ cSessions++;
+ RTCritSectLeave(&pThis->CritSect);
+
+ supSvcLogError("supSvcGrantTryStop - %u active sessions after waiting %u ms", cSessions, (unsigned)u64Elapsed);
+ return VERR_TRY_AGAIN;
+ }
+
+ rc = RTCritSectLeave(&pThis->CritSect);
+ if (RT_FAILURE(rc))
+ {
+ supSvcLogError("supSvcGrantTryStop - RTCritSectLeave returns %Rrc", rc);
+ return VINF_SUCCESS;
+ }
+
+ rc = RTSemEventWait(pThis->hSessionEvent, 60*1000 - u64Elapsed);
+ if (RT_FAILURE(rc) && rc != VERR_TIMEOUT)
+ {
+ supSvcLogError("supSvcGrantTryStop - RTSemEventWait returns %Rrc", rc);
+ return VINF_SUCCESS;
+ }
+ }
+}
+
+
+/** @copydoc SUPSVCSERVICE::pfnStopAndDestroy */
+DECLCALLBACK(void) supSvcGrantStopAndDestroy(void *pvInstance, bool fRunning)
+{
+ PSUPSVCGRANT pThis = (PSUPSVCGRANT)pvInstance;
+ int rc;
+
+ /*
+ * Attempt to stop the service, cancelling blocked server and client calls.
+ */
+ RTCritSectEnter(&pThis->CritSect);
+
+ SUPSVCGRANTSTATE enmState = pThis->enmState;
+ AssertMsg(fRunning == (pThis->enmState == kSupSvcGrantState_Listen),
+ ("%RTbool %s\n", fRunning, supSvcGrantStateName(enmState)));
+
+ if (enmState == kSupSvcGrantState_Listen)
+ {
+ RTThreadUserReset(pThis->hThread);
+ pThis->enmState = kSupSvcGrantState_Paused;
+ for (PSUPSVCGRANTSESSION pCur = pThis->pSessionHead; pCur; pCur = pCur->pNext)
+ ASMAtomicWriteBool(&pCur->fTerminate, true);
+
+ /* try cancel local ipc operations that might be pending */
+ RTLocalIpcServerCancel(pThis->hServer);
+ for (PSUPSVCGRANTSESSION pCur = pThis->pSessionHead; pCur; pCur = pCur->pNext)
+ {
+ RTLOCALIPCSESSION hSession;
+ ASMAtomicReadHandle(&pCur->hSession, &hSession);
+ if (hSession != NIL_RTLOCALIPCSESSION)
+ RTLocalIpcSessionCancel(hSession);
+ }
+
+ /*
+ * Wait for the thread to respond (outside the crit sect).
+ */
+ RTCritSectLeave(&pThis->CritSect);
+ supSvcGrantWait(pThis, kSupSvcGrantState_Pausing, kSupSvcGrantState_Paused);
+ RTCritSectEnter(&pThis->CritSect);
+
+ /*
+ * Wait for any lingering sessions to exit.
+ */
+ supSvcGrantCleanUpSessionsLocked(pThis);
+ if (pThis->pSessionHead)
+ {
+ uint64_t u64StartTS = RTTimeMilliTS();
+ do
+ {
+ /* Destroy the sessions since cancelling didn't do the trick. */
+ for (PSUPSVCGRANTSESSION pCur = pThis->pSessionHead; pCur; pCur = pCur->pNext)
+ {
+ RTLOCALIPCSESSION hSession;
+ ASMAtomicXchgHandle(&pCur->hSession, NIL_RTLOCALIPCSESSION, &hSession);
+ if (hSession != NIL_RTLOCALIPCSESSION)
+ {
+ rc = RTLocalIpcSessionClose(hSession);
+ AssertRC(rc);
+ if (RT_FAILURE(rc))
+ supSvcLogError("supSvcGrantStopAndDestroy: RTLocalIpcSessionClose(%p) returns %Rrc",
+ (uintptr_t)hSession, rc);
+ }
+ }
+
+ /* Check the time. */
+ uint64_t u64Elapsed = RTTimeMilliTS() - u64StartTS;
+ if (u64Elapsed >= 60*1000) /* 1 min */
+ break;
+
+ /* wait */
+ RTCritSectLeave(&pThis->CritSect);
+ rc = RTSemEventWait(pThis->hSessionEvent, 60*1000 - u64Elapsed);
+ RTCritSectEnter(&pThis->CritSect);
+ if (RT_FAILURE(rc) && rc != VERR_TIMEOUT)
+ break;
+
+ /* cleanup and check again */
+ supSvcGrantCleanUpSessionsLocked(pThis);
+ } while (pThis->pSessionHead);
+ }
+ }
+
+ /*
+ * Tell the service thread to terminate and wait for it to do so.
+ */
+ pThis->enmState = kSupSvcGrantState_Terminating;
+ RTLOCALIPCSERVER hServer;
+ ASMAtomicXchgHandle(&pThis->hServer, NIL_RTLOCALIPCSERVER, &hServer);
+ RTThreadUserSignal(pThis->hThread);
+
+ RTCritSectLeave(&pThis->CritSect);
+
+ rc = RTThreadWait(pThis->hThread, 20*1000, NULL);
+ if (RT_FAILURE(rc) && rc == VERR_TIMEOUT)
+ {
+ RTThreadUserSignal(pThis->hThread);
+ RTLocalIpcServerDestroy(hServer);
+ hServer = NIL_RTLOCALIPCSERVER;
+
+ rc = RTThreadWait(pThis->hThread, 40*1000, NULL);
+ if (RT_FAILURE(rc))
+ supSvcLogError("supSvcGrantStopAndDestroy - RTThreadWait(40 sec) returns %Rrc", rc);
+ }
+ else if (RT_FAILURE(rc))
+ supSvcLogError("supSvcGrantStopAndDestroy - RTThreadWait(20 sec) returns %Rrc", rc);
+ pThis->hThread = NIL_RTTHREAD;
+
+ /*
+ * Kill the parent pointers of any lingering sessions.
+ */
+ RTCritSectEnter(&pThis->CritSect);
+ pThis->enmState = kSupSvcGrantState_Destroyed;
+
+ supSvcGrantCleanUpSessionsLocked(pThis);
+ unsigned cSessions = 0;
+ for (PSUPSVCGRANTSESSION pCur = pThis->pSessionHead; pCur; pCur = pCur->pNext)
+ ASMAtomicWriteNullPtr(&pCur->pParent);
+
+ RTCritSectLeave(&pThis->CritSect);
+ if (cSessions)
+ supSvcLogError("supSvcGrantStopAndDestroy: %d session failed to terminate!", cSessions);
+
+ /*
+ * Free the resource.
+ */
+ RTLocalIpcServerDestroy(hServer);
+
+ RTSemEventDestroy(pThis->hResponseEvent);
+ pThis->hResponseEvent = NIL_RTSEMEVENT;
+
+ RTSemEventDestroy(pThis->hSessionEvent);
+ pThis->hSessionEvent = NIL_RTSEMEVENT;
+
+ RTCritSectDelete(&pThis->VerifyCritSect);
+ RTCritSectDelete(&pThis->CritSect);
+
+ RTMemFree(pThis);
+
+ Log(("supSvcGrantStopAndDestroy: done (rc=%Rrc)\n", rc));
+}
+
diff --git a/src/VBox/HostDrivers/Support/SUPSvcInternal.h b/src/VBox/HostDrivers/Support/SUPSvcInternal.h
new file mode 100644
index 00000000..88782b86
--- /dev/null
+++ b/src/VBox/HostDrivers/Support/SUPSvcInternal.h
@@ -0,0 +1,104 @@
+/* $Id: SUPSvcInternal.h $ */
+/** @file
+ * VirtualBox Support Service - Internal header.
+ */
+
+/*
+ * Copyright (C) 2008-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+#ifndef VBOX_INCLUDED_SRC_Support_SUPSvcInternal_h
+#define VBOX_INCLUDED_SRC_Support_SUPSvcInternal_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+#include <VBox/cdefs.h>
+#include <VBox/types.h>
+#include <iprt/stdarg.h>
+#include <iprt/getopt.h>
+
+RT_C_DECLS_BEGIN
+
+/** @name Common Helpers
+ * @{ */
+void supSvcLogErrorStr(const char *pszMsg);
+void supSvcLogErrorV(const char *pszFormat, va_list va);
+void supSvcLogError(const char *pszFormat, ...);
+int supSvcLogGetOptError(const char *pszAction, int rc, int argc, char **argv, int iArg, PCRTOPTIONUNION pValue);
+int supSvcLogTooManyArgsError(const char *pszAction, int argc, char **argv, int iArg);
+void supSvcDisplayErrorV(const char *pszFormat, va_list va);
+void supSvcDisplayError(const char *pszFormat, ...);
+int supSvcDisplayGetOptError(const char *pszAction, int rc, int argc, char **argv, int iArg, PCRTOPTIONUNION pValue);
+int supSvcDisplayTooManyArgsError(const char *pszAction, int argc, char **argv, int iArg);
+/** @} */
+
+
+/** @name OS Backend
+ * @{ */
+/**
+ * Logs the message to the appropriate system log.
+ *
+ * @param pszMsg The log string.
+ */
+void supSvcOsLogErrorStr(const char *pszMsg);
+/** @} */
+
+
+/** @name The Service Manager
+ * @{ */
+void supSvcStopAndDestroyServices(void);
+int supSvcTryStopServices(void);
+int supSvcCreateAndStartServices(void);
+/** @} */
+
+
+/** @name The Grant Service
+ * @{ */
+#define SUPSVC_GRANT_SERVICE_NAME "VirtualBoxGrantSvc"
+DECLCALLBACK(int) supSvcGrantCreate(void **ppvInstance);
+DECLCALLBACK(void) supSvcGrantStart(void *pvInstance);
+DECLCALLBACK(int) supSvcGrantTryStop(void *pvInstance);
+DECLCALLBACK(void) supSvcGrantStopAndDestroy(void *pvInstance, bool fRunning);
+/** @} */
+
+
+/** @name The Global Service
+ * @{ */
+DECLCALLBACK(int) supSvcGlobalCreate(void **ppvInstance);
+DECLCALLBACK(void) supSvcGlobalStart(void *pvInstance);
+DECLCALLBACK(int) supSvcGlobalTryStop(void *pvInstance);
+DECLCALLBACK(void) supSvcGlobalStopAndDestroy(void *pvInstance, bool fRunning);
+/** @} */
+
+RT_C_DECLS_END
+
+#endif /* !VBOX_INCLUDED_SRC_Support_SUPSvcInternal_h */
+
diff --git a/src/VBox/HostDrivers/Support/bldSUPSignedDummy.cpp b/src/VBox/HostDrivers/Support/bldSUPSignedDummy.cpp
new file mode 100644
index 00000000..8f780a1c
--- /dev/null
+++ b/src/VBox/HostDrivers/Support/bldSUPSignedDummy.cpp
@@ -0,0 +1,42 @@
+/* $Id: bldSUPSignedDummy.cpp $ */
+/** @file
+ * VirtualBox Support Library - Dummy program for extracting signing certificate.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+int main()
+{
+ return 0;
+}
+
diff --git a/src/VBox/HostDrivers/Support/darwin/Info.plist b/src/VBox/HostDrivers/Support/darwin/Info.plist
new file mode 100644
index 00000000..e010a700
--- /dev/null
+++ b/src/VBox/HostDrivers/Support/darwin/Info.plist
@@ -0,0 +1,46 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+ <key>CFBundleDevelopmentRegion</key> <string>English</string>
+ <key>CFBundleExecutable</key> <string>VBoxDrv</string>
+ <key>CFBundleIdentifier</key> <string>org.virtualbox.kext.VBoxDrv</string>
+ <key>CFBundleInfoDictionaryVersion</key> <string>6.0</string>
+ <key>CFBundleName</key> <string>VBoxDrv</string>
+ <key>CFBundlePackageType</key> <string>KEXT</string>
+ <key>CFBundleSignature</key> <string>????</string>
+ <key>CFBundleGetInfoString</key> <string>@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>VBoxDrv</key>
+ <dict>
+ <key>CFBundleIdentifier</key> <string>org.virtualbox.kext.VBoxDrv</string>
+ <key>IOClass</key> <string>org_virtualbox_SupDrv</string>
+ <key>IOMatchCategory</key> <string>org_virtualbox_SupDrv</string>
+ <key>IOProviderClass</key> <string>IOResources</string>
+ <key>IOResourceMatch</key> <string>IOKit</string>
+ <key>IOUserClientClass</key> <string>org_virtualbox_SupDrvClient</string>
+ </dict>
+ </dict>
+ <key>OSBundleLibraries</key>
+ <dict>
+ <key>com.apple.kpi.bsd</key> <string>9.0.0</string>
+ <key>com.apple.kpi.mach</key> <string>9.0.0</string>
+ <key>com.apple.kpi.libkern</key> <string>9.0.0</string>
+ <key>com.apple.kpi.unsupported</key> <string>9.0.0</string>
+ <key>com.apple.kpi.iokit</key> <string>9.0.0</string>
+ </dict>
+ <key>OSBundleLibraries_x86_64</key>
+ <dict>
+ <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/HostDrivers/Support/darwin/Makefile.kup b/src/VBox/HostDrivers/Support/darwin/Makefile.kup
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/src/VBox/HostDrivers/Support/darwin/Makefile.kup
diff --git a/src/VBox/HostDrivers/Support/darwin/SUPDrv-darwin.cpp b/src/VBox/HostDrivers/Support/darwin/SUPDrv-darwin.cpp
new file mode 100644
index 00000000..35f6e8ba
--- /dev/null
+++ b/src/VBox/HostDrivers/Support/darwin/SUPDrv-darwin.cpp
@@ -0,0 +1,2410 @@
+/* $Id: SUPDrv-darwin.cpp $ */
+/** @file
+ * VirtualBox Support Driver - Darwin Specific Code.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#define LOG_GROUP LOG_GROUP_SUP_DRV
+#include "../../../Runtime/r0drv/darwin/the-darwin-kernel.h"
+
+#include "../SUPDrvInternal.h"
+#include <VBox/version.h>
+#include <iprt/assert.h>
+#include <iprt/asm.h>
+#include <iprt/asm-amd64-x86.h>
+#include <iprt/ctype.h>
+#include <iprt/dbg.h>
+#include <iprt/initterm.h>
+#include <iprt/file.h>
+#include <iprt/ldr.h>
+#include <iprt/mem.h>
+#include <iprt/power.h>
+#include <iprt/process.h>
+#include <iprt/spinlock.h>
+#include <iprt/semaphore.h>
+#include <iprt/x86.h>
+#include <iprt/crypto/applecodesign.h>
+#include <iprt/crypto/store.h>
+#include <iprt/crypto/pkcs7.h>
+#include <iprt/crypto/x509.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/IODeviceTreeSupport.h>
+#if MAC_OS_X_VERSION_MIN_REQUIRED < 101100
+# include <IOKit/usb/IOUSBHIDDriver.h>
+#endif
+#include <IOKit/bluetooth/IOBluetoothHIDDriver.h>
+#include <IOKit/bluetooth/IOBluetoothHIDDriverTypes.h>
+
+#ifdef VBOX_WITH_HOST_VMX
+# include <libkern/version.h>
+RT_C_DECLS_BEGIN
+# include <i386/vmx.h>
+RT_C_DECLS_END
+#endif
+
+
+/*********************************************************************************************************************************
+* Defined Constants And Macros *
+*********************************************************************************************************************************/
+
+/** The system device node name. */
+#define DEVICE_NAME_SYS "vboxdrv"
+/** The user device node name. */
+#define DEVICE_NAME_USR "vboxdrvu"
+
+
+/** @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 VBoxDrvDarwinStart(struct kmod_info *pKModInfo, void *pvData);
+static kern_return_t VBoxDrvDarwinStop(struct kmod_info *pKModInfo, void *pvData);
+#ifdef VBOX_WITH_DARWIN_R0_DARWIN_IMAGE_VERIFICATION
+static int supdrvDarwinInitCertStores(PSUPDRVDEVEXT pDevExt);
+static void supdrvDarwinDestroyCertStores(PSUPDRVDEVEXT pDevExt);
+#endif
+
+static int VBoxDrvDarwinOpen(dev_t Dev, int fFlags, int fDevType, struct proc *pProcess);
+static int VBoxDrvDarwinClose(dev_t Dev, int fFlags, int fDevType, struct proc *pProcess);
+static int VBoxDrvDarwinIOCtl(dev_t Dev, u_long iCmd, caddr_t pData, int fFlags, struct proc *pProcess);
+#ifndef VBOX_WITHOUT_EFLAGS_AC_SET_IN_VBOXDRV
+static int VBoxDrvDarwinIOCtlSMAP(dev_t Dev, u_long iCmd, caddr_t pData, int fFlags, struct proc *pProcess);
+#endif
+static int VBoxDrvDarwinIOCtlSlow(PSUPDRVSESSION pSession, u_long iCmd, caddr_t pData, struct proc *pProcess);
+
+static int VBoxDrvDarwinErr2DarwinErr(int rc);
+
+static IOReturn VBoxDrvDarwinSleepHandler(void *pvTarget, void *pvRefCon, UInt32 uMessageType, IOService *pProvider, void *pvMessageArgument, vm_size_t argSize);
+RT_C_DECLS_END
+
+static int vboxdrvDarwinResolveSymbols(void);
+static bool vboxdrvDarwinCpuHasSMAP(void);
+
+
+/*********************************************************************************************************************************
+* Structures and Typedefs *
+*********************************************************************************************************************************/
+/**
+ * The service class.
+ * This is just a formality really.
+ */
+class org_virtualbox_SupDrv : public IOService
+{
+ OSDeclareDefaultStructors(org_virtualbox_SupDrv);
+
+public:
+ virtual bool init(OSDictionary *pDictionary = 0);
+ virtual void free(void);
+ virtual bool start(IOService *pProvider);
+ virtual void stop(IOService *pProvider);
+ virtual IOService *probe(IOService *pProvider, SInt32 *pi32Score);
+ virtual bool terminate(IOOptionBits fOptions);
+
+ RTR0MEMEF_NEW_AND_DELETE_OPERATORS_IOKIT();
+
+private:
+ /** Guard against the parent class growing and us using outdated headers. */
+ uint8_t m_abSafetyPadding[256];
+};
+
+OSDefineMetaClassAndStructors(org_virtualbox_SupDrv, IOService);
+
+
+/**
+ * An attempt at getting that clientDied() notification.
+ * I don't think it'll work as I cannot figure out where/what creates the correct
+ * port right.
+ */
+class org_virtualbox_SupDrvClient : public IOUserClient
+{
+ OSDeclareDefaultStructors(org_virtualbox_SupDrvClient);
+
+private:
+ /** Guard against the parent class growing and us using outdated headers. */
+ uint8_t m_abSafetyPadding[256];
+
+ PSUPDRVSESSION m_pSession; /**< The session. */
+ task_t m_Task; /**< The client task. */
+ org_virtualbox_SupDrv *m_pProvider; /**< The service provider. */
+
+public:
+ virtual bool initWithTask(task_t OwningTask, void *pvSecurityId, UInt32 u32Type);
+ virtual bool start(IOService *pProvider);
+ static void sessionClose(RTPROCESS Process);
+ virtual IOReturn clientClose(void);
+ virtual IOReturn clientDied(void);
+ virtual bool terminate(IOOptionBits fOptions = 0);
+ virtual bool finalize(IOOptionBits fOptions);
+ virtual void stop(IOService *pProvider);
+
+ RTR0MEMEF_NEW_AND_DELETE_OPERATORS_IOKIT();
+};
+
+OSDefineMetaClassAndStructors(org_virtualbox_SupDrvClient, 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(VBoxDrv, VBOX_VERSION_STRING, _start, _stop)
+DECL_HIDDEN_DATA(kmod_start_func_t *) _realmain = VBoxDrvDarwinStart;
+DECL_HIDDEN_DATA(kmod_stop_func_t *) _antimain = VBoxDrvDarwinStop;
+DECL_HIDDEN_DATA(int) _kext_apple_cc = __APPLE_CC__;
+RT_C_DECLS_END
+
+
+/**
+ * Device extention & session data association structure.
+ */
+static SUPDRVDEVEXT g_DevExt;
+
+/**
+ * The character device switch table for the driver.
+ */
+static struct cdevsw g_DevCW =
+{
+ /** @todo g++ doesn't like this syntax - it worked with gcc before renaming to .cpp. */
+ /*.d_open = */VBoxDrvDarwinOpen,
+ /*.d_close = */VBoxDrvDarwinClose,
+ /*.d_read = */eno_rdwrt,
+ /*.d_write = */eno_rdwrt,
+ /*.d_ioctl = */VBoxDrvDarwinIOCtl,
+ /*.d_stop = */eno_stop,
+ /*.d_reset = */eno_reset,
+ /*.d_ttys = */NULL,
+ /*.d_select= */eno_select,
+ /*.d_mmap = */eno_mmap,
+ /*.d_strategy = */eno_strat,
+ /*.d_getc = */(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 for the system device. */
+static void *g_hDevFsDeviceSys = NULL;
+/** Registered devfs device handle for the user device. */
+static void *g_hDevFsDeviceUsr = NULL;
+
+/** Spinlock protecting g_apSessionHashTab. */
+static RTSPINLOCK g_Spinlock = NIL_RTSPINLOCK;
+/** Hash table */
+static PSUPDRVSESSION g_apSessionHashTab[19];
+/** Calculates the index into g_apSessionHashTab.*/
+#define SESSION_HASH(pid) ((pid) % RT_ELEMENTS(g_apSessionHashTab))
+/** The number of open sessions. */
+static int32_t volatile g_cSessions = 0;
+/** The notifier handle for the sleep callback handler. */
+static IONotifier *g_pSleepNotifier = NULL;
+
+/** Pointer to vmx_suspend(). */
+static PFNRT g_pfnVmxSuspend = NULL;
+/** Pointer to vmx_resume(). */
+static PFNRT g_pfnVmxResume = NULL;
+/** Pointer to vmx_use_count. */
+static int volatile *g_pVmxUseCount = NULL;
+
+#ifdef SUPDRV_WITH_MSR_PROBER
+/** Pointer to rdmsr_carefully if found. Returns 0 on success. */
+static int (*g_pfnRdMsrCarefully)(uint32_t uMsr, uint32_t *puLow, uint32_t *puHigh) = NULL;
+/** Pointer to rdmsr64_carefully if found. Returns 0 on success. */
+static int (*g_pfnRdMsr64Carefully)(uint32_t uMsr, uint64_t *uValue) = NULL;
+/** Pointer to wrmsr[64]_carefully if found. Returns 0 on success. */
+static int (*g_pfnWrMsr64Carefully)(uint32_t uMsr, uint64_t uValue) = NULL;
+#endif
+
+/** SUPKERNELFEATURES_XXX */
+static uint32_t g_fKernelFeatures = 0;
+
+/**
+ * Start the kernel module.
+ */
+static kern_return_t VBoxDrvDarwinStart(struct kmod_info *pKModInfo, void *pvData)
+{
+ RT_NOREF(pKModInfo, pvData);
+#ifdef DEBUG
+ printf("VBoxDrvDarwinStart\n");
+#endif
+
+ /*
+ * Initialize IPRT.
+ */
+ int rc = RTR0Init(0);
+ if (RT_SUCCESS(rc))
+ {
+ /*
+ * Initialize the device extension.
+ */
+ rc = supdrvInitDevExt(&g_DevExt, sizeof(SUPDRVSESSION));
+ if (RT_SUCCESS(rc))
+ {
+#ifdef VBOX_WITH_DARWIN_R0_DARWIN_IMAGE_VERIFICATION
+ supdrvDarwinInitCertStores(&g_DevExt);
+#endif
+
+ /*
+ * Initialize the session hash table.
+ */
+ memset(g_apSessionHashTab, 0, sizeof(g_apSessionHashTab)); /* paranoia */
+ rc = RTSpinlockCreate(&g_Spinlock, RTSPINLOCK_FLAGS_INTERRUPT_SAFE, "VBoxDrvDarwin");
+ if (RT_SUCCESS(rc))
+ {
+ if (vboxdrvDarwinCpuHasSMAP())
+ {
+ g_fKernelFeatures |= SUPKERNELFEATURES_SMAP;
+#ifndef VBOX_WITHOUT_EFLAGS_AC_SET_IN_VBOXDRV
+ LogRel(("disabling SMAP for VBoxDrvDarwinIOCtl\n"));
+ g_DevCW.d_ioctl = VBoxDrvDarwinIOCtlSMAP;
+#endif
+ }
+
+ /*
+ * Resolve some extra kernel symbols.
+ */
+ rc = vboxdrvDarwinResolveSymbols();
+ if (RT_SUCCESS(rc))
+ {
+
+ /*
+ * Registering ourselves as a character device.
+ */
+ g_iMajorDeviceNo = cdevsw_add(-1, &g_DevCW);
+ if (g_iMajorDeviceNo >= 0)
+ {
+#ifdef VBOX_WITH_HARDENING
+ g_hDevFsDeviceSys = devfs_make_node(makedev(g_iMajorDeviceNo, 0), DEVFS_CHAR,
+ UID_ROOT, GID_WHEEL, 0600, DEVICE_NAME_SYS);
+#else
+ g_hDevFsDeviceSys = devfs_make_node(makedev(g_iMajorDeviceNo, 0), DEVFS_CHAR,
+ UID_ROOT, GID_WHEEL, 0666, DEVICE_NAME_SYS);
+#endif
+ if (g_hDevFsDeviceSys)
+ {
+ g_hDevFsDeviceUsr = devfs_make_node(makedev(g_iMajorDeviceNo, 1), DEVFS_CHAR,
+ UID_ROOT, GID_WHEEL, 0666, DEVICE_NAME_USR);
+ if (g_hDevFsDeviceUsr)
+ {
+ LogRel(("VBoxDrv: version " VBOX_VERSION_STRING " r%d; IOCtl version %#x; IDC version %#x; dev major=%d\n",
+ VBOX_SVN_REV, SUPDRV_IOC_VERSION, SUPDRV_IDC_VERSION, g_iMajorDeviceNo));
+
+ /* Register a sleep/wakeup notification callback */
+ g_pSleepNotifier = registerPrioritySleepWakeInterest(&VBoxDrvDarwinSleepHandler, &g_DevExt, NULL);
+ if (g_pSleepNotifier == NULL)
+ LogRel(("VBoxDrv: register for sleep/wakeup events failed\n"));
+
+ return KMOD_RETURN_SUCCESS;
+ }
+
+ LogRel(("VBoxDrv: devfs_make_node(makedev(%d,1),,,,%s) failed\n", g_iMajorDeviceNo, DEVICE_NAME_USR));
+ devfs_remove(g_hDevFsDeviceSys);
+ g_hDevFsDeviceSys = NULL;
+ }
+ else
+ LogRel(("VBoxDrv: devfs_make_node(makedev(%d,0),,,,%s) failed\n", g_iMajorDeviceNo, DEVICE_NAME_SYS));
+
+ cdevsw_remove(g_iMajorDeviceNo, &g_DevCW);
+ g_iMajorDeviceNo = -1;
+ }
+ else
+ LogRel(("VBoxDrv: cdevsw_add failed (%d)\n", g_iMajorDeviceNo));
+ }
+#ifdef VBOX_WITH_DARWIN_R0_DARWIN_IMAGE_VERIFICATION
+ supdrvDarwinDestroyCertStores(&g_DevExt);
+#endif
+ RTSpinlockDestroy(g_Spinlock);
+ g_Spinlock = NIL_RTSPINLOCK;
+ }
+ else
+ LogRel(("VBoxDrv: RTSpinlockCreate failed (rc=%d)\n", rc));
+ supdrvDeleteDevExt(&g_DevExt);
+ }
+ else
+ printf("VBoxDrv: failed to initialize device extension (rc=%d)\n", rc);
+ RTR0TermForced();
+ }
+ else
+ printf("VBoxDrv: failed to initialize IPRT (rc=%d)\n", rc);
+
+ memset(&g_DevExt, 0, sizeof(g_DevExt));
+ return KMOD_RETURN_FAILURE;
+}
+
+
+/**
+ * Resolves kernel symbols we need and some we just would like to have.
+ */
+static int vboxdrvDarwinResolveSymbols(void)
+{
+ RTDBGKRNLINFO hKrnlInfo;
+ int rc = RTR0DbgKrnlInfoOpen(&hKrnlInfo, 0);
+ if (RT_SUCCESS(rc))
+ {
+ /*
+ * The VMX stuff - required with raw-mode (in theory for 64-bit on
+ * 32-bit too, but we never did that on darwin).
+ */
+ int rc1 = RTR0DbgKrnlInfoQuerySymbol(hKrnlInfo, NULL, "vmx_resume", (void **)&g_pfnVmxResume);
+ int rc2 = RTR0DbgKrnlInfoQuerySymbol(hKrnlInfo, NULL, "vmx_suspend", (void **)&g_pfnVmxSuspend);
+ int rc3 = RTR0DbgKrnlInfoQuerySymbol(hKrnlInfo, NULL, "vmx_use_count", (void **)&g_pVmxUseCount);
+ if (RT_SUCCESS(rc1) && RT_SUCCESS(rc2) && RT_SUCCESS(rc3))
+ {
+ LogRel(("VBoxDrv: vmx_resume=%p vmx_suspend=%p vmx_use_count=%p (%d) cr4=%#x\n",
+ g_pfnVmxResume, g_pfnVmxSuspend, g_pVmxUseCount, *g_pVmxUseCount, ASMGetCR4() ));
+ }
+ else
+ {
+ LogRel(("VBoxDrv: failed to resolve vmx stuff: vmx_resume=%Rrc vmx_suspend=%Rrc vmx_use_count=%Rrc", rc1, rc2, rc3));
+ g_pfnVmxResume = NULL;
+ g_pfnVmxSuspend = NULL;
+ g_pVmxUseCount = NULL;
+#ifdef VBOX_WITH_RAW_MODE
+ rc = VERR_SYMBOL_NOT_FOUND;
+#endif
+ }
+
+ if (RT_SUCCESS(rc))
+ {
+#ifdef SUPDRV_WITH_MSR_PROBER
+ /*
+ * MSR prober stuff - optional!
+ */
+ rc2 = RTR0DbgKrnlInfoQuerySymbol(hKrnlInfo, NULL, "rdmsr_carefully", (void **)&g_pfnRdMsrCarefully);
+ if (RT_FAILURE(rc2))
+ g_pfnRdMsrCarefully = NULL;
+ rc2 = RTR0DbgKrnlInfoQuerySymbol(hKrnlInfo, NULL, "rdmsr64_carefully", (void **)&g_pfnRdMsr64Carefully);
+ if (RT_FAILURE(rc2))
+ g_pfnRdMsr64Carefully = NULL;
+# ifdef RT_ARCH_AMD64 /* Missing 64 in name, so if implemented on 32-bit it could have different signature. */
+ rc2 = RTR0DbgKrnlInfoQuerySymbol(hKrnlInfo, NULL, "wrmsr_carefully", (void **)&g_pfnWrMsr64Carefully);
+ if (RT_FAILURE(rc2))
+# endif
+ g_pfnWrMsr64Carefully = NULL;
+
+ LogRel(("VBoxDrv: g_pfnRdMsrCarefully=%p g_pfnRdMsr64Carefully=%p g_pfnWrMsr64Carefully=%p\n",
+ g_pfnRdMsrCarefully, g_pfnRdMsr64Carefully, g_pfnWrMsr64Carefully));
+
+#endif /* SUPDRV_WITH_MSR_PROBER */
+ }
+
+ RTR0DbgKrnlInfoRelease(hKrnlInfo);
+ }
+ else
+ LogRel(("VBoxDrv: Failed to open kernel symbols, rc=%Rrc\n", rc));
+ return rc;
+}
+
+
+#ifdef VBOX_WITH_DARWIN_R0_DARWIN_IMAGE_VERIFICATION
+
+/**
+ * Initalizes the certificate stores (code signing) in the device extension.
+ */
+static int supdrvDarwinInitCertStores(PSUPDRVDEVEXT pDevExt)
+{
+ pDevExt->hAdditionalStore = NIL_RTCRSTORE;
+
+ pDevExt->hRootStore = NIL_RTCRSTORE;
+ int rc = RTCrStoreCreateInMem(&pDevExt->hRootStore, g_cSUPTrustedTAs + 1);
+ if (RT_SUCCESS(rc))
+ {
+ for (uint32_t i = 0; i < g_cSUPTrustedTAs; i++)
+ {
+ int rc2 = RTCrStoreCertAddEncoded(pDevExt->hRootStore, RTCRCERTCTX_F_ENC_TAF_DER,
+ g_aSUPTrustedTAs[i].pch, g_aSUPTrustedTAs[i].cb, NULL);
+ if (RT_FAILURE(rc2) && RT_SUCCESS(rc))
+ {
+ printf("VBoxDrv: Error loading g_aSUPTrustedTAs[%u]: %d\n", i, rc);
+ rc = rc2;
+ }
+ }
+
+ /* We implicitly trust the build certificate. */
+ int rc2 = RTCrStoreCertAddEncoded(pDevExt->hRootStore, RTCRCERTCTX_F_ENC_X509_DER,
+ g_abSUPBuildCert, g_cbSUPBuildCert, NULL);
+ if (RT_FAILURE(rc2) && RT_SUCCESS(rc))
+ {
+ printf("VBoxDrv: Error loading g_cbSUPBuildCert: %d\n", rc);
+ rc = rc2;
+ }
+ }
+ return rc;
+}
+
+
+/**
+ * Releases the certificate stores in the device extension.
+ */
+static void supdrvDarwinDestroyCertStores(PSUPDRVDEVEXT pDevExt)
+{
+ if (pDevExt->hRootStore != NIL_RTCRSTORE)
+ {
+ uint32_t cRefs = RTCrStoreRelease(pDevExt->hRootStore);
+ Assert(cRefs == 0); RT_NOREF(cRefs);
+ pDevExt->hRootStore = NIL_RTCRSTORE;
+ }
+ if (pDevExt->hAdditionalStore != NIL_RTCRSTORE)
+ {
+ uint32_t cRefs = RTCrStoreRelease(pDevExt->hAdditionalStore);
+ Assert(cRefs == 0); RT_NOREF(cRefs);
+ pDevExt->hAdditionalStore = NIL_RTCRSTORE;
+ }
+}
+
+#endif /* VBOX_WITH_DARWIN_R0_DARWIN_IMAGE_VERIFICATION */
+
+/**
+ * Stop the kernel module.
+ */
+static kern_return_t VBoxDrvDarwinStop(struct kmod_info *pKModInfo, void *pvData)
+{
+ RT_NOREF(pKModInfo, pvData);
+ int rc;
+ LogFlow(("VBoxDrvDarwinStop\n"));
+
+ /** @todo I've got a nagging feeling that we'll have to keep track of users and refuse
+ * unloading if we're busy. Investigate and implement this! */
+
+ /*
+ * Undo the work done during start (in reverse order).
+ */
+ if (g_pSleepNotifier)
+ {
+ g_pSleepNotifier->remove();
+ g_pSleepNotifier = NULL;
+ }
+
+ devfs_remove(g_hDevFsDeviceUsr);
+ g_hDevFsDeviceUsr = NULL;
+
+ devfs_remove(g_hDevFsDeviceSys);
+ g_hDevFsDeviceSys = NULL;
+
+ rc = cdevsw_remove(g_iMajorDeviceNo, &g_DevCW);
+ Assert(rc == g_iMajorDeviceNo);
+ g_iMajorDeviceNo = -1;
+
+ supdrvDeleteDevExt(&g_DevExt);
+
+ rc = RTSpinlockDestroy(g_Spinlock);
+ AssertRC(rc);
+ g_Spinlock = NIL_RTSPINLOCK;
+
+#ifdef VBOX_WITH_DARWIN_R0_DARWIN_IMAGE_VERIFICATION
+ supdrvDarwinDestroyCertStores(&g_DevExt);
+#endif
+
+ RTR0TermForced();
+
+ memset(&g_DevExt, 0, sizeof(g_DevExt));
+#ifdef DEBUG
+ printf("VBoxDrvDarwinStop - done\n");
+#endif
+ return KMOD_RETURN_SUCCESS;
+}
+
+
+/**
+ * Device open. Called on open /dev/vboxdrv
+ *
+ * @param Dev The device number.
+ * @param fFlags ???.
+ * @param fDevType ???.
+ * @param pProcess The process issuing this request.
+ */
+static int VBoxDrvDarwinOpen(dev_t Dev, int fFlags, int fDevType, struct proc *pProcess)
+{
+ RT_NOREF(fFlags, fDevType);
+#ifdef DEBUG_DARWIN_GIP
+ char szName[128];
+ szName[0] = '\0';
+ proc_name(proc_pid(pProcess), szName, sizeof(szName));
+ Log(("VBoxDrvDarwinOpen: pid=%d '%s'\n", proc_pid(pProcess), szName));
+#endif
+
+ /*
+ * 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_SupDrvClient, fail
+ * if no such session, and mark it as opened. We set the uid & gid
+ * here too, since that is more straight forward at this point.
+ */
+ const bool fUnrestricted = minor(Dev) == 0;
+ int rc = VINF_SUCCESS;
+ PSUPDRVSESSION 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->fUnrestricted = fUnrestricted;
+ pSession->Uid = Uid;
+ pSession->Gid = Gid;
+ }
+ 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;
+
+#ifdef DEBUG_DARWIN_GIP
+ OSDBGPRINT(("VBoxDrvDarwinOpen: pid=%d '%s' pSession=%p rc=%d\n", proc_pid(pProcess), szName, pSession, rc));
+#else
+ Log(("VBoxDrvDarwinOpen: g_DevExt=%p pSession=%p rc=%d pid=%d\n", &g_DevExt, pSession, rc, proc_pid(pProcess)));
+#endif
+ return VBoxDrvDarwinErr2DarwinErr(rc);
+}
+
+
+/**
+ * Close device.
+ */
+static int VBoxDrvDarwinClose(dev_t Dev, int fFlags, int fDevType, struct proc *pProcess)
+{
+ RT_NOREF(Dev, fFlags, fDevType, pProcess);
+ Log(("VBoxDrvDarwinClose: pid=%d\n", (int)RTProcSelf()));
+ Assert(proc_pid(pProcess) == (int)RTProcSelf());
+
+ /*
+ * Hand the session closing to org_virtualbox_SupDrvClient.
+ */
+ org_virtualbox_SupDrvClient::sessionClose(RTProcSelf());
+ return 0;
+}
+
+
+/**
+ * Device I/O Control entry point.
+ *
+ * @returns Darwin for slow IOCtls and VBox status code for the fast ones.
+ * @param Dev The device number (major+minor).
+ * @param iCmd The IOCtl command.
+ * @param pData Pointer to the data (if any it's a SUPDRVIOCTLDATA (kernel copy)).
+ * @param fFlags Flag saying we're a character device (like we didn't know already).
+ * @param pProcess The process issuing this request.
+ */
+static int VBoxDrvDarwinIOCtl(dev_t Dev, u_long iCmd, caddr_t pData, int fFlags, struct proc *pProcess)
+{
+ RT_NOREF(fFlags);
+ const bool fUnrestricted = minor(Dev) == 0;
+ const RTPROCESS Process = proc_pid(pProcess);
+ const unsigned iHash = SESSION_HASH(Process);
+ PSUPDRVSESSION pSession;
+
+#ifdef VBOX_WITH_EFLAGS_AC_SET_IN_VBOXDRV
+ /*
+ * Refuse all I/O control calls if we've ever detected EFLAGS.AC being cleared.
+ *
+ * This isn't a problem, as there is absolutely nothing in the kernel context that
+ * depend on user context triggering cleanups. That would be pretty wild, right?
+ */
+ if (RT_UNLIKELY(g_DevExt.cBadContextCalls > 0))
+ {
+ SUPR0Printf("VBoxDrvDarwinIOCtl: EFLAGS.AC=0 detected %u times, refusing all I/O controls!\n", g_DevExt.cBadContextCalls);
+ return EDEVERR;
+ }
+#endif
+
+ /*
+ * Find the session.
+ */
+ RTSpinlockAcquire(g_Spinlock);
+
+ pSession = g_apSessionHashTab[iHash];
+ while (pSession && (pSession->Process != Process || pSession->fUnrestricted != fUnrestricted || !pSession->fOpened))
+ pSession = pSession->pNextHash;
+
+ if (RT_LIKELY(pSession))
+ supdrvSessionRetain(pSession);
+
+ RTSpinlockRelease(g_Spinlock);
+ if (RT_UNLIKELY(!pSession))
+ {
+ OSDBGPRINT(("VBoxDrvDarwinIOCtl: WHAT?!? pSession == NULL! This must be a mistake... pid=%d iCmd=%#lx\n",
+ (int)Process, iCmd));
+ return EINVAL;
+ }
+
+ /*
+ * Deal with the two high-speed IOCtl that takes it's arguments from
+ * the session and iCmd, and only returns a VBox status code.
+ */
+ int rc;
+ AssertCompile((SUP_IOCTL_FAST_DO_FIRST & 0xff) == (SUP_IOCTL_FLAG | 64));
+ if ( (uintptr_t)(iCmd - SUP_IOCTL_FAST_DO_FIRST) < (uintptr_t)32
+ && fUnrestricted)
+ rc = supdrvIOCtlFast(iCmd - SUP_IOCTL_FAST_DO_FIRST, *(uint32_t *)pData, &g_DevExt, pSession);
+ else
+ rc = VBoxDrvDarwinIOCtlSlow(pSession, iCmd, pData, pProcess);
+
+ supdrvSessionRelease(pSession);
+ return rc;
+}
+
+
+#ifndef VBOX_WITHOUT_EFLAGS_AC_SET_IN_VBOXDRV
+/**
+ * Alternative Device I/O Control entry point on hosts with SMAP support.
+ *
+ * @returns Darwin for slow IOCtls and VBox status code for the fast ones.
+ * @param Dev The device number (major+minor).
+ * @param iCmd The IOCtl command.
+ * @param pData Pointer to the data (if any it's a SUPDRVIOCTLDATA (kernel copy)).
+ * @param fFlags Flag saying we're a character device (like we didn't know already).
+ * @param pProcess The process issuing this request.
+ */
+static int VBoxDrvDarwinIOCtlSMAP(dev_t Dev, u_long iCmd, caddr_t pData, int fFlags, struct proc *pProcess)
+{
+ /*
+ * Allow VBox R0 code to touch R3 memory. Setting the AC bit disables the
+ * SMAP check.
+ */
+ RTCCUINTREG fSavedEfl = ASMAddFlags(X86_EFL_AC);
+
+ int rc = VBoxDrvDarwinIOCtl(Dev, iCmd, pData, fFlags, pProcess);
+
+# if defined(VBOX_STRICT) || defined(VBOX_WITH_EFLAGS_AC_SET_IN_VBOXDRV)
+ /*
+ * Before we restore AC and the rest of EFLAGS, check if the IOCtl handler code
+ * accidentially modified it or some other important flag.
+ */
+ if (RT_UNLIKELY( (ASMGetFlags() & (X86_EFL_AC | X86_EFL_IF | X86_EFL_DF | X86_EFL_IOPL))
+ != ((fSavedEfl & (X86_EFL_AC | X86_EFL_IF | X86_EFL_DF | X86_EFL_IOPL)) | X86_EFL_AC) ))
+ {
+ char szTmp[48];
+ RTStrPrintf(szTmp, sizeof(szTmp), "iCmd=%#x: %#x->%#x!", iCmd, (uint32_t)fSavedEfl, (uint32_t)ASMGetFlags());
+ supdrvBadContext(&g_DevExt, "SUPDrv-darwin.cpp", __LINE__, szTmp);
+ }
+# endif
+
+ ASMSetFlags(fSavedEfl);
+ return rc;
+}
+#endif /* VBOX_WITHOUT_EFLAGS_AC_SET_IN_VBOXDRV */
+
+
+/**
+ * Worker for VBoxDrvDarwinIOCtl that takes the slow IOCtl functions.
+ *
+ * @returns Darwin errno.
+ *
+ * @param pSession The session.
+ * @param iCmd The IOCtl command.
+ * @param pData Pointer to the kernel copy of the SUPDRVIOCTLDATA buffer.
+ * @param pProcess The calling process.
+ */
+static int VBoxDrvDarwinIOCtlSlow(PSUPDRVSESSION pSession, u_long iCmd, caddr_t pData, struct proc *pProcess)
+{
+ RT_NOREF(pProcess);
+ LogFlow(("VBoxDrvDarwinIOCtlSlow: pSession=%p iCmd=%p pData=%p pProcess=%p\n", pSession, iCmd, pData, pProcess));
+
+
+ /*
+ * Buffered or unbuffered?
+ */
+ PSUPREQHDR pHdr;
+ user_addr_t pUser = 0;
+ void *pvPageBuf = NULL;
+ uint32_t cbReq = IOCPARM_LEN(iCmd);
+ if ((IOC_DIRMASK & iCmd) == IOC_INOUT)
+ {
+ pHdr = (PSUPREQHDR)pData;
+ if (RT_UNLIKELY(cbReq < sizeof(*pHdr)))
+ {
+ OSDBGPRINT(("VBoxDrvDarwinIOCtlSlow: cbReq=%#x < %#x; iCmd=%#lx\n", cbReq, (int)sizeof(*pHdr), iCmd));
+ return EINVAL;
+ }
+ if (RT_UNLIKELY((pHdr->fFlags & SUPREQHDR_FLAGS_MAGIC_MASK) != SUPREQHDR_FLAGS_MAGIC))
+ {
+ OSDBGPRINT(("VBoxDrvDarwinIOCtlSlow: bad magic fFlags=%#x; iCmd=%#lx\n", pHdr->fFlags, iCmd));
+ return EINVAL;
+ }
+ if (RT_UNLIKELY( RT_MAX(pHdr->cbIn, pHdr->cbOut) != cbReq
+ || pHdr->cbIn < sizeof(*pHdr)
+ || pHdr->cbOut < sizeof(*pHdr)))
+ {
+ OSDBGPRINT(("VBoxDrvDarwinIOCtlSlow: max(%#x,%#x) != %#x; iCmd=%#lx\n", pHdr->cbIn, pHdr->cbOut, cbReq, iCmd));
+ return EINVAL;
+ }
+ }
+ else if ((IOC_DIRMASK & iCmd) == IOC_VOID && !cbReq)
+ {
+ /*
+ * Get the header and figure out how much we're gonna have to read.
+ */
+ IPRT_DARWIN_SAVE_EFL_AC();
+ SUPREQHDR Hdr;
+ pUser = (user_addr_t)*(void **)pData;
+ int rc = copyin(pUser, &Hdr, sizeof(Hdr));
+ if (RT_UNLIKELY(rc))
+ {
+ OSDBGPRINT(("VBoxDrvDarwinIOCtlSlow: copyin(%llx,Hdr,) -> %#x; iCmd=%#lx\n", (unsigned long long)pUser, rc, iCmd));
+ IPRT_DARWIN_RESTORE_EFL_AC();
+ return rc;
+ }
+ if (RT_UNLIKELY((Hdr.fFlags & SUPREQHDR_FLAGS_MAGIC_MASK) != SUPREQHDR_FLAGS_MAGIC))
+ {
+ OSDBGPRINT(("VBoxDrvDarwinIOCtlSlow: bad magic fFlags=%#x; iCmd=%#lx\n", Hdr.fFlags, iCmd));
+ IPRT_DARWIN_RESTORE_EFL_AC();
+ return EINVAL;
+ }
+ cbReq = RT_MAX(Hdr.cbIn, Hdr.cbOut);
+ if (RT_UNLIKELY( Hdr.cbIn < sizeof(Hdr)
+ || Hdr.cbOut < sizeof(Hdr)
+ || cbReq > _1M*16))
+ {
+ OSDBGPRINT(("VBoxDrvDarwinIOCtlSlow: max(%#x,%#x); iCmd=%#lx\n", Hdr.cbIn, Hdr.cbOut, iCmd));
+ IPRT_DARWIN_RESTORE_EFL_AC();
+ return EINVAL;
+ }
+
+ /*
+ * Allocate buffer and copy in the data.
+ */
+ pHdr = (PSUPREQHDR)RTMemTmpAlloc(cbReq);
+ if (!pHdr)
+ pvPageBuf = pHdr = (PSUPREQHDR)IOMallocAligned(RT_ALIGN_Z(cbReq, PAGE_SIZE), 8);
+ if (RT_UNLIKELY(!pHdr))
+ {
+ OSDBGPRINT(("VBoxDrvDarwinIOCtlSlow: failed to allocate buffer of %d bytes; iCmd=%#lx\n", cbReq, iCmd));
+ IPRT_DARWIN_RESTORE_EFL_AC();
+ return ENOMEM;
+ }
+ rc = copyin(pUser, pHdr, Hdr.cbIn);
+ if (RT_UNLIKELY(rc))
+ {
+ OSDBGPRINT(("VBoxDrvDarwinIOCtlSlow: copyin(%llx,%p,%#x) -> %#x; iCmd=%#lx\n",
+ (unsigned long long)pUser, pHdr, Hdr.cbIn, rc, iCmd));
+ if (pvPageBuf)
+ IOFreeAligned(pvPageBuf, RT_ALIGN_Z(cbReq, PAGE_SIZE));
+ else
+ RTMemTmpFree(pHdr);
+ IPRT_DARWIN_RESTORE_EFL_AC();
+ return rc;
+ }
+ if (Hdr.cbIn < cbReq)
+ RT_BZERO((uint8_t *)pHdr + Hdr.cbIn, cbReq - Hdr.cbIn);
+ IPRT_DARWIN_RESTORE_EFL_AC();
+ }
+ else
+ {
+ Log(("VBoxDrvDarwinIOCtlSlow: huh? cbReq=%#x iCmd=%#lx\n", cbReq, iCmd));
+ return EINVAL;
+ }
+
+ /*
+ * Process the IOCtl.
+ */
+ int rc = supdrvIOCtl(iCmd, &g_DevExt, pSession, pHdr, cbReq);
+ if (RT_LIKELY(!rc))
+ {
+ /*
+ * If not buffered, copy back the buffer before returning.
+ */
+ if (pUser)
+ {
+ IPRT_DARWIN_SAVE_EFL_AC();
+ uint32_t cbOut = pHdr->cbOut;
+ if (cbOut > cbReq)
+ {
+ OSDBGPRINT(("VBoxDrvDarwinIOCtlSlow: too much output! %#x > %#x; uCmd=%#lx!\n", cbOut, cbReq, iCmd));
+ cbOut = cbReq;
+ }
+ rc = copyout(pHdr, pUser, cbOut);
+ if (RT_UNLIKELY(rc))
+ OSDBGPRINT(("VBoxDrvDarwinIOCtlSlow: copyout(%p,%llx,%#x) -> %d; uCmd=%#lx!\n",
+ pHdr, (unsigned long long)pUser, cbOut, rc, iCmd));
+
+ /* cleanup */
+ if (pvPageBuf)
+ IOFreeAligned(pvPageBuf, RT_ALIGN_Z(cbReq, PAGE_SIZE));
+ else
+ RTMemTmpFree(pHdr);
+ IPRT_DARWIN_RESTORE_EFL_AC();
+ }
+ }
+ else
+ {
+ /*
+ * The request failed, just clean up.
+ */
+ if (pUser)
+ {
+ if (pvPageBuf)
+ {
+ IPRT_DARWIN_SAVE_EFL_AC();
+ IOFreeAligned(pvPageBuf, RT_ALIGN_Z(cbReq, PAGE_SIZE));
+ IPRT_DARWIN_RESTORE_EFL_AC();
+ }
+ else
+ RTMemTmpFree(pHdr);
+ }
+
+ Log(("VBoxDrvDarwinIOCtlSlow: pid=%d iCmd=%lx pData=%p failed, rc=%d\n", proc_pid(pProcess), iCmd, (void *)pData, rc));
+ rc = EINVAL;
+ }
+
+ Log2(("VBoxDrvDarwinIOCtlSlow: returns %d\n", rc));
+ return rc;
+}
+
+
+/**
+ * The SUPDRV IDC entry point.
+ *
+ * @returns VBox status code, see supdrvIDC.
+ * @param uReq The request code.
+ * @param pReq The request.
+ */
+DECLEXPORT(int) VBOXCALL SUPDrvDarwinIDC(uint32_t uReq, PSUPDRVIDCREQHDR pReq)
+{
+ PSUPDRVSESSION pSession;
+
+ /*
+ * Some quick validations.
+ */
+ if (RT_UNLIKELY(!RT_VALID_PTR(pReq)))
+ return VERR_INVALID_POINTER;
+
+ pSession = pReq->pSession;
+ if (pSession)
+ {
+ if (RT_UNLIKELY(!RT_VALID_PTR(pSession)))
+ return VERR_INVALID_PARAMETER;
+ if (RT_UNLIKELY(pSession->pDevExt != &g_DevExt))
+ return VERR_INVALID_PARAMETER;
+ }
+ else if (RT_UNLIKELY(uReq != SUPDRV_IDC_REQ_CONNECT))
+ return VERR_INVALID_PARAMETER;
+
+ /*
+ * Do the job.
+ */
+ return supdrvIDC(uReq, &g_DevExt, pSession, pReq);
+}
+
+
+void VBOXCALL supdrvOSCleanupSession(PSUPDRVDEVEXT pDevExt, PSUPDRVSESSION pSession)
+{
+ NOREF(pDevExt);
+ NOREF(pSession);
+}
+
+
+void VBOXCALL supdrvOSSessionHashTabInserted(PSUPDRVDEVEXT pDevExt, PSUPDRVSESSION pSession, void *pvUser)
+{
+ NOREF(pDevExt); NOREF(pSession); NOREF(pvUser);
+}
+
+
+void VBOXCALL supdrvOSSessionHashTabRemoved(PSUPDRVDEVEXT pDevExt, PSUPDRVSESSION pSession, void *pvUser)
+{
+ NOREF(pDevExt); NOREF(pSession); NOREF(pvUser);
+}
+
+
+/**
+ * Initializes any OS specific object creator fields.
+ */
+void VBOXCALL supdrvOSObjInitCreator(PSUPDRVOBJ pObj, PSUPDRVSESSION pSession)
+{
+ NOREF(pObj);
+ NOREF(pSession);
+}
+
+
+/**
+ * Checks if the session can access the object.
+ *
+ * @returns true if a decision has been made.
+ * @returns false if the default access policy should be applied.
+ *
+ * @param pObj The object in question.
+ * @param pSession The session wanting to access the object.
+ * @param pszObjName The object name, can be NULL.
+ * @param prc Where to store the result when returning true.
+ */
+bool VBOXCALL supdrvOSObjCanAccess(PSUPDRVOBJ pObj, PSUPDRVSESSION pSession, const char *pszObjName, int *prc)
+{
+ NOREF(pObj);
+ NOREF(pSession);
+ NOREF(pszObjName);
+ NOREF(prc);
+ return false;
+}
+
+/**
+ * Callback for blah blah blah.
+ */
+IOReturn VBoxDrvDarwinSleepHandler(void * /* pvTarget */, void *pvRefCon, UInt32 uMessageType,
+ IOService *pProvider, void *pvMsgArg, vm_size_t cbMsgArg)
+{
+ RT_NOREF(pProvider, pvMsgArg, cbMsgArg);
+ LogFlow(("VBoxDrv: 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;
+}
+
+
+#ifdef VBOX_WITH_HOST_VMX
+/**
+ * For cleaning up the mess we left behind on Yosemite with 4.3.28 and earlier.
+ *
+ * We ASSUME VT-x is supported by the CPU.
+ *
+ * @param idCpu Unused.
+ * @param pvUser1 Unused.
+ * @param pvUser2 Unused.
+ */
+static DECLCALLBACK(void) vboxdrvDarwinVmxEnableFix(RTCPUID idCpu, void *pvUser1, void *pvUser2)
+{
+ RT_NOREF(idCpu, pvUser1, pvUser2);
+ RTCCUINTREG uCr4 = ASMGetCR4();
+ if (!(uCr4 & X86_CR4_VMXE))
+ {
+ uCr4 |= X86_CR4_VMXE;
+ ASMSetCR4(uCr4);
+ }
+}
+#endif
+
+
+/**
+ * @copydoc SUPR0EnableVTx
+ */
+int VBOXCALL supdrvOSEnableVTx(bool fEnable)
+{
+#ifdef VBOX_WITH_HOST_VMX
+ int rc;
+ if ( version_major >= 10 /* 10 = 10.6.x = Snow Leopard */
+# ifdef VBOX_WITH_RAW_MODE
+ && g_pfnVmxSuspend
+ && g_pfnVmxResume
+ && g_pVmxUseCount
+# endif
+ )
+ {
+ IPRT_DARWIN_SAVE_EFL_AC();
+ if (fEnable)
+ {
+ /*
+ * We screwed up on Yosemite and didn't notice that we weren't
+ * calling host_vmxon. CR4.VMXE may therefore have been disabled
+ * by us. So, first time around we make sure it's set so we won't
+ * crash in the pre-4.3.28/5.0RC1 upgrade scenario.
+ * See @bugref{7907}.
+ */
+ static bool volatile g_fDoneCleanup = false;
+ if (!g_fDoneCleanup)
+ {
+ if (version_major == 14 /* 14 = 10.10 = yosemite */)
+ {
+ uint32_t fCaps;
+ rc = supdrvQueryVTCapsInternal(&fCaps);
+ if (RT_SUCCESS(rc))
+ {
+ if (fCaps & SUPVTCAPS_VT_X)
+ rc = RTMpOnAll(vboxdrvDarwinVmxEnableFix, NULL, NULL);
+ else
+ rc = VERR_VMX_NO_VMX;
+ }
+ if (RT_FAILURE(rc))
+ {
+ IPRT_DARWIN_RESTORE_EFL_AC();
+ return rc;
+ }
+ }
+ g_fDoneCleanup = true;
+ }
+
+ /*
+ * Call the kernel.
+ */
+ AssertLogRelMsg(!g_pVmxUseCount || *g_pVmxUseCount >= 0,
+ ("vmx_use_count=%d (@ %p, expected it to be a positive number\n",
+ *g_pVmxUseCount, g_pVmxUseCount));
+
+ rc = host_vmxon(false /* exclusive */);
+ if (rc == VMX_OK)
+ rc = VINF_SUCCESS;
+ else if (rc == VMX_UNSUPPORTED)
+ rc = VERR_VMX_NO_VMX;
+ else if (rc == VMX_INUSE)
+ rc = VERR_VMX_IN_VMX_ROOT_MODE;
+ else /* shouldn't happen, but just in case. */
+ {
+ LogRel(("host_vmxon returned %d\n", rc));
+ rc = VERR_UNRESOLVED_ERROR;
+ }
+ LogRel(("VBoxDrv: host_vmxon -> vmx_use_count=%d rc=%Rrc\n", *g_pVmxUseCount, rc));
+ }
+ else
+ {
+ AssertLogRelMsgReturn(!g_pVmxUseCount || *g_pVmxUseCount >= 1,
+ ("vmx_use_count=%d (@ %p, expected it to be a non-zero positive number\n",
+ *g_pVmxUseCount, g_pVmxUseCount),
+ VERR_WRONG_ORDER);
+ host_vmxoff();
+ rc = VINF_SUCCESS;
+ LogRel(("VBoxDrv: host_vmxoff -> vmx_use_count=%d\n", *g_pVmxUseCount));
+ }
+ IPRT_DARWIN_RESTORE_EFL_AC();
+ }
+ else
+ {
+ /* In 10.5.x the host_vmxon is severely broken! Don't use it, it will
+ frequnetly panic the host. */
+ rc = VERR_NOT_SUPPORTED;
+ }
+ return rc;
+#else
+ return VERR_NOT_SUPPORTED;
+#endif
+}
+
+
+/**
+ * @copydoc SUPR0SuspendVTxOnCpu
+ */
+bool VBOXCALL supdrvOSSuspendVTxOnCpu(void)
+{
+#ifdef VBOX_WITH_HOST_VMX
+ /*
+ * Consult the VMX usage counter, don't try suspend if not enabled.
+ *
+ * Note! The host_vmxon/off code is still race prone since, but this is
+ * currently the best we can do without always enable VMX when
+ * loading the driver.
+ */
+ if ( g_pVmxUseCount
+ && *g_pVmxUseCount > 0)
+ {
+ IPRT_DARWIN_SAVE_EFL_AC();
+ g_pfnVmxSuspend();
+ IPRT_DARWIN_RESTORE_EFL_AC();
+ return true;
+ }
+ return false;
+#else
+ return false;
+#endif
+}
+
+
+/**
+ * @copydoc SUPR0ResumeVTxOnCpu
+ */
+void VBOXCALL supdrvOSResumeVTxOnCpu(bool fSuspended)
+{
+#ifdef VBOX_WITH_HOST_VMX
+ /*
+ * Don't consult the counter here, the state knows better.
+ * We're executing with interrupts disabled and anyone racing us with
+ * disabling VT-x will be waiting in the rendezvous code.
+ */
+ if ( fSuspended
+ && g_pfnVmxResume)
+ {
+ IPRT_DARWIN_SAVE_EFL_AC();
+ g_pfnVmxResume();
+ IPRT_DARWIN_RESTORE_EFL_AC();
+ }
+ else
+ Assert(!fSuspended);
+#else
+ Assert(!fSuspended);
+#endif
+}
+
+
+bool VBOXCALL supdrvOSGetForcedAsyncTscMode(PSUPDRVDEVEXT pDevExt)
+{
+ NOREF(pDevExt);
+ return false;
+}
+
+
+bool VBOXCALL supdrvOSAreCpusOfflinedOnSuspend(void)
+{
+ /** @todo verify this. */
+ return false;
+}
+
+
+bool VBOXCALL supdrvOSAreTscDeltasInSync(void)
+{
+ return false;
+}
+
+
+#ifdef VBOX_WITH_DARWIN_R0_DARWIN_IMAGE_VERIFICATION
+
+/**
+ * @callback_method_impl{FNRTLDRIMPORT}
+ */
+static DECLCALLBACK(int) supdrvDarwinLdrOpenImportCallback(RTLDRMOD hLdrMod, const char *pszModule, const char *pszSymbol,
+ unsigned uSymbol, PRTLDRADDR pValue, void *pvUser)
+{
+ PSUPDRVDEVEXT pDevExt = (PSUPDRVDEVEXT)pvUser;
+
+ /*
+ * First consult the VMMR0 module if there is one fully loaded.
+ * This is necessary as VMMR0 may overload assertion and logger symbols.
+ */
+ if (pDevExt->pvVMMR0)
+ for (PSUPDRVLDRIMAGE pImage = pDevExt->pLdrImages; pImage; pImage = pImage->pNext)
+ if (pImage->pvImage == pDevExt->pvVMMR0)
+ {
+ if ( pImage->uState == SUP_IOCTL_LDR_LOAD
+ && pImage->hLdrMod != NIL_RTLDRMOD)
+ {
+ int rc = RTLdrGetSymbolEx(pImage->hLdrMod, pImage->pvImage, (uintptr_t)pImage->pvImage,
+ UINT32_MAX, pszSymbol, pValue);
+ if (RT_SUCCESS(rc))
+ return VINF_SUCCESS;
+ }
+ break;
+ }
+
+ /*
+ * Then we consult the SUPDrv export table.
+ */
+ uintptr_t uValue = 0;
+ int rc = supdrvLdrGetExportedSymbol(pszSymbol, &uValue);
+ if (RT_SUCCESS(rc))
+ {
+ *pValue = uValue;
+ return VINF_SUCCESS;
+ }
+
+ /*
+ * Failed.
+ */
+ printf("VBoxDrv: Unable to resolve symbol '%s'.\n", pszSymbol);
+ RT_NOREF(hLdrMod, pszModule, uSymbol);
+ return VERR_SYMBOL_NOT_FOUND;
+}
+
+
+/**
+ * @callback_method_impl{FNRTCRPKCS7VERIFYCERTCALLBACK,
+ * Verify that the signing certificate is sane.}
+ */
+static DECLCALLBACK(int) supdrvDarwinLdrOpenVerifyCertificatCallback(PCRTCRX509CERTIFICATE pCert, RTCRX509CERTPATHS hCertPaths,
+ uint32_t fFlags, void *pvUser, PRTERRINFO pErrInfo)
+{
+ RT_NOREF(pvUser); //PSUPDRVDEVEXT pDevExt = (PSUPDRVDEVEXT)pvUser;
+# ifdef DEBUG_bird
+ printf("supdrvDarwinLdrOpenVerifyCertificatCallback: pCert=%p hCertPaths=%p\n", (void *)pCert, (void *)hCertPaths);
+# endif
+
+# if 0
+ /*
+ * Test signing certificates normally doesn't have all the necessary
+ * features required below. So, treat them as special cases.
+ */
+ if ( hCertPaths == NIL_RTCRX509CERTPATHS
+ && RTCrX509Name_Compare(&pCert->TbsCertificate.Issuer, &pCert->TbsCertificate.Subject) == 0)
+ {
+ RTMsgInfo("Test signed.\n");
+ return VINF_SUCCESS;
+ }
+# endif
+
+ /*
+ * Standard code signing capabilites required.
+ */
+ int rc = RTCrPkcs7VerifyCertCallbackCodeSigning(pCert, hCertPaths, fFlags, NULL, pErrInfo);
+ if ( RT_SUCCESS(rc)
+ && (fFlags & RTCRPKCS7VCC_F_SIGNED_DATA))
+ {
+ uint32_t cDevIdApp = 0;
+ uint32_t cDevIdKext = 0;
+ uint32_t cDevIdMacDev = 0;
+ for (uint32_t i = 0; i < pCert->TbsCertificate.T3.Extensions.cItems; i++)
+ {
+ PCRTCRX509EXTENSION pExt = pCert->TbsCertificate.T3.Extensions.papItems[i];
+ if (RTAsn1ObjId_CompareWithString(&pExt->ExtnId, RTCR_APPLE_CS_DEVID_APPLICATION_OID) == 0)
+ {
+ cDevIdApp++;
+ if (!pExt->Critical.fValue)
+ rc = RTErrInfoSetF(pErrInfo, VERR_GENERAL_FAILURE,
+ "Dev ID Application certificate extension is not flagged critical");
+ }
+ else if (RTAsn1ObjId_CompareWithString(&pExt->ExtnId, RTCR_APPLE_CS_DEVID_KEXT_OID) == 0)
+ {
+ cDevIdKext++;
+ if (!pExt->Critical.fValue)
+ rc = RTErrInfoSetF(pErrInfo, VERR_GENERAL_FAILURE,
+ "Dev ID kext certificate extension is not flagged critical");
+ }
+ else if (RTAsn1ObjId_CompareWithString(&pExt->ExtnId, RTCR_APPLE_CS_DEVID_MAC_SW_DEV_OID) == 0)
+ {
+ cDevIdMacDev++;
+ if (!pExt->Critical.fValue)
+ rc = RTErrInfoSetF(pErrInfo, VERR_GENERAL_FAILURE,
+ "Dev ID MAC SW dev certificate extension is not flagged critical");
+ }
+ }
+# ifdef VBOX_WITH_DARWIN_R0_TEST_SIGN
+ /*
+ * Mac application software development certs do not have the usually required extensions.
+ */
+ if (cDevIdMacDev)
+ {
+ cDevIdApp++;
+ cDevIdKext++;
+ }
+# endif
+ if (cDevIdApp == 0)
+ rc = RTErrInfoSetF(pErrInfo, VERR_GENERAL_FAILURE,
+ "Certificate is missing the 'Dev ID Application' extension");
+ if (cDevIdKext == 0)
+ rc = RTErrInfoSetF(pErrInfo, VERR_GENERAL_FAILURE,
+ "Certificate is missing the 'Dev ID kext' extension");
+ }
+
+ return rc;
+}
+
+
+/**
+ * @callback_method_impl{FNRTLDRVALIDATESIGNEDDATA}
+ */
+static DECLCALLBACK(int) supdrvDarwinLdrOpenVerifyCallback(RTLDRMOD hLdrMod, PCRTLDRSIGNATUREINFO pInfo,
+ PRTERRINFO pErrInfo, void *pvUser)
+{
+ PSUPDRVDEVEXT pDevExt = (PSUPDRVDEVEXT)pvUser;
+ RT_NOREF_PV(hLdrMod);
+
+ switch (pInfo->enmType)
+ {
+ case RTLDRSIGNATURETYPE_PKCS7_SIGNED_DATA:
+ if (pInfo->pvExternalData)
+ {
+ PCRTCRPKCS7CONTENTINFO pContentInfo = (PCRTCRPKCS7CONTENTINFO)pInfo->pvSignature;
+ RTTIMESPEC ValidationTime;
+ RTTimeNow(&ValidationTime);
+
+ return RTCrPkcs7VerifySignedDataWithExternalData(pContentInfo,
+ RTCRPKCS7VERIFY_SD_F_COUNTER_SIGNATURE_SIGNING_TIME_ONLY
+ | RTCRPKCS7VERIFY_SD_F_ALWAYS_USE_SIGNING_TIME_IF_PRESENT
+ | RTCRPKCS7VERIFY_SD_F_ALWAYS_USE_MS_TIMESTAMP_IF_PRESENT,
+ pDevExt->hAdditionalStore, pDevExt->hRootStore, &ValidationTime,
+ supdrvDarwinLdrOpenVerifyCertificatCallback, pDevExt,
+ pInfo->pvExternalData, pInfo->cbExternalData, pErrInfo);
+ }
+ return RTErrInfoSetF(pErrInfo, VERR_NOT_SUPPORTED, "Expected external data with signature!");
+
+ default:
+ return RTErrInfoSetF(pErrInfo, VERR_NOT_SUPPORTED, "Unsupported signature type: %d", pInfo->enmType);
+ }
+}
+
+#endif /* VBOX_WITH_DARWIN_R0_DARWIN_IMAGE_VERIFICATION */
+
+int VBOXCALL supdrvOSLdrOpen(PSUPDRVDEVEXT pDevExt, PSUPDRVLDRIMAGE pImage, const char *pszFilename)
+{
+#ifdef VBOX_WITH_DARWIN_R0_DARWIN_IMAGE_VERIFICATION
+ /*
+ * Initialize our members.
+ */
+ pImage->hLdrMod = NIL_RTLDRMOD;
+ pImage->hMemAlloc = NIL_RTR0MEMOBJ;
+
+ /*
+ * We have to double buffer the file to be avoid a potential race between
+ * validation and actual image loading. This could be eliminated later by
+ * baking the image validation into the RTLdrGetBits operation.
+ *
+ * Note! After calling RTLdrOpenInMemory, pvFile is owned by the loader and will be
+ * freed via the RTFileReadAllFree callback when the loader module is closed.
+ */
+ void *pvFile = NULL;
+ size_t cbFile = 0;
+ int rc = RTFileReadAllEx(pszFilename, 0, _32M, RTFILE_RDALL_O_DENY_WRITE, &pvFile, &cbFile);
+ if (RT_SUCCESS(rc))
+ {
+ PRTERRINFOSTATIC pErrInfo = (PRTERRINFOSTATIC)RTMemTmpAlloc(sizeof(RTERRINFOSTATIC));
+ RTLDRMOD hLdrMod = NIL_RTLDRMOD;
+ rc = RTLdrOpenInMemory(pszFilename, 0 /*fFlags*/, RTLDRARCH_HOST, cbFile,
+ NULL /*pfnRead*/, RTFileReadAllFree, pvFile,
+ &hLdrMod, pErrInfo ? RTErrInfoInitStatic(pErrInfo) : NULL);
+ if (RT_SUCCESS(rc))
+ {
+ /*
+ * Validate the image.
+ */
+ rc = RTLdrVerifySignature(hLdrMod, supdrvDarwinLdrOpenVerifyCallback, pDevExt,
+ pErrInfo ? RTErrInfoInitStatic(pErrInfo) : NULL);
+ if (RT_SUCCESS(rc))
+ {
+ /*
+ * Allocate memory for the object and load it into it.
+ */
+ size_t cbImage = RTLdrSize(hLdrMod);
+ if (cbImage == pImage->cbImageBits)
+ {
+ RTR0MEMOBJ hMemAlloc;
+ rc = RTR0MemObjAllocPage(&hMemAlloc, cbImage, true /*fExecutable*/);
+ if (RT_SUCCESS(rc))
+ {
+ void *pvImageBits = RTR0MemObjAddress(hMemAlloc);
+ rc = RTLdrGetBits(hLdrMod, pvImageBits, (uintptr_t)pvImageBits,
+ supdrvDarwinLdrOpenImportCallback, pDevExt);
+ if (RT_SUCCESS(rc))
+ {
+ /*
+ * Commit.
+ */
+ pImage->hMemAlloc = hMemAlloc;
+ pImage->hLdrMod = hLdrMod;
+ pImage->pvImage = pvImageBits;
+ RTMemTmpFree(pErrInfo);
+ /** @todo Call RTLdrDone. */
+ kprintf("VBoxDrv: Loaded %s at %p\n", pImage->szName, pvImageBits);
+ return VINF_SUCCESS;
+ }
+
+ RTR0MemObjFree(hMemAlloc, true /*fFreeMappings*/);
+ }
+ else
+ printf("VBoxDrv: Failed to allocate %u bytes for %s: %d\n", (unsigned)cbImage, pszFilename, rc);
+ }
+ else
+ {
+ printf("VBoxDrv: Image size mismatch for %s: %#x, ring-3 says %#x\n",
+ pszFilename, (unsigned)cbImage, (unsigned)pImage->cbImageBits);
+ rc = VERR_LDR_MISMATCH_NATIVE;
+ }
+ }
+ else if (pErrInfo && RTErrInfoIsSet(&pErrInfo->Core))
+ printf("VBoxDrv: RTLdrVerifySignature(%s) failed: %d - %s\n", pszFilename, rc, pErrInfo->Core.pszMsg);
+ else
+ printf("VBoxDrv: RTLdrVerifySignature(%s) failed: %d\n", pszFilename, rc);
+ RTLdrClose(hLdrMod);
+ }
+ else if (pErrInfo && RTErrInfoIsSet(&pErrInfo->Core))
+ printf("VBoxDrv: RTLdrOpenInMemory(%s) failed: %d - %s\n", pszFilename, rc, pErrInfo->Core.pszMsg);
+ else
+ printf("VBoxDrv: RTLdrOpenInMemory(%s) failed: %d\n", pszFilename, rc);
+ RTMemTmpFree(pErrInfo);
+ }
+ return rc;
+#else /* !VBOX_WITH_DARWIN_R0_DARWIN_IMAGE_VERIFICATION */
+ NOREF(pDevExt); NOREF(pImage); NOREF(pszFilename);
+ return VERR_NOT_SUPPORTED;
+#endif /* !VBOX_WITH_DARWIN_R0_DARWIN_IMAGE_VERIFICATION */
+}
+
+
+#ifdef VBOX_WITH_DARWIN_R0_DARWIN_IMAGE_VERIFICATION
+/**
+ * @callback_method_impl{FNRTLDRENUMSYMS,
+ * Worker for supdrvOSLdrValidatePointer.
+ */
+static DECLCALLBACK(int) supdrvDarwinLdrValidatePointerCallback(RTLDRMOD hLdrMod, const char *pszSymbol, unsigned uSymbol,
+ RTLDRADDR Value, void *pvUser)
+{
+ RT_NOREF(hLdrMod, pszSymbol, uSymbol);
+ if (Value == (uintptr_t)pvUser)
+ return VINF_CALLBACK_RETURN;
+ return VINF_SUCCESS;
+}
+#endif
+
+
+int VBOXCALL supdrvOSLdrValidatePointer(PSUPDRVDEVEXT pDevExt, PSUPDRVLDRIMAGE pImage, void *pv,
+ const uint8_t *pbImageBits, const char *pszSymbol)
+{
+#ifdef VBOX_WITH_DARWIN_R0_DARWIN_IMAGE_VERIFICATION
+ AssertReturn(pImage->hLdrMod != NIL_RTLDRMOD, VERR_INVALID_STATE);
+
+ /*
+ * If we've got a symbol name, just to a lookup and compare addresses.
+ */
+ int rc;
+ if (RT_C_IS_UPPER(*pszSymbol))
+ {
+ RTLDRADDR uValueFound;
+ rc = RTLdrGetSymbolEx(pImage->hLdrMod, pImage->pvImage, (uintptr_t)pImage->pvImage, UINT32_MAX, pszSymbol, &uValueFound);
+ if (RT_SUCCESS(rc))
+ {
+ if (uValueFound == (uintptr_t)pv)
+ rc = VINF_SUCCESS;
+ else
+ {
+ SUPR0Printf("SUPDrv: Different exports found for %s in %s: %RTptr, expected %p\n",
+ pszSymbol, pImage->szName, (RTUINTPTR)uValueFound, pv);
+ rc = VERR_LDR_BAD_FIXUP;
+ }
+ }
+ else
+ SUPR0Printf("SUPDrv: No export named %s (%p) in %s!\n", pszSymbol, pv, pImage->szName);
+ }
+ /*
+ * Otherwise do a symbol enumeration and look for the entrypoint.
+ */
+ else
+ {
+ rc = RTLdrEnumSymbols(pImage->hLdrMod, 0 /*fFlags*/, pImage->pvImage, (uintptr_t)pImage->pvImage,
+ supdrvDarwinLdrValidatePointerCallback, pv);
+ if (rc == VINF_CALLBACK_RETURN)
+ rc = VINF_SUCCESS;
+ else if (RT_SUCCESS(rc))
+ {
+ SUPR0Printf("SUPDrv: No export with address %p (%s) in %s!\n", pv, pszSymbol, pImage->szName);
+ rc = VERR_NOT_FOUND;
+ }
+ else
+ SUPR0Printf("SUPDrv: RTLdrEnumSymbols failed on %s: %Rrc\n", pImage->szName, rc);
+ }
+ RT_NOREF(pDevExt, pbImageBits);
+ return rc;
+#else
+ NOREF(pDevExt); NOREF(pImage); NOREF(pv); NOREF(pbImageBits); NOREF(pszSymbol);
+ return VERR_NOT_SUPPORTED;
+#endif
+}
+
+
+int VBOXCALL supdrvOSLdrQuerySymbol(PSUPDRVDEVEXT pDevExt, PSUPDRVLDRIMAGE pImage,
+ const char *pszSymbol, size_t cchSymbol, void **ppvSymbol)
+{
+#ifdef VBOX_WITH_DARWIN_R0_DARWIN_IMAGE_VERIFICATION
+ /*
+ * Just hand the problem to RTLdrGetSymbolEx.
+ */
+ RTLDRADDR uValueFound;
+ int rc = RTLdrGetSymbolEx(pImage->hLdrMod, pImage->pvImage, (uintptr_t)pImage->pvImage, UINT32_MAX, pszSymbol, &uValueFound);
+ if (RT_SUCCESS(rc))
+ {
+ *ppvSymbol = (void *)(uintptr_t)uValueFound;
+ return VINF_SUCCESS;
+ }
+ RT_NOREF(pDevExt, cchSymbol);
+ return rc;
+
+#else
+ RT_NOREF(pDevExt, pImage, pszSymbol, cchSymbol, ppvSymbol);
+ return VERR_WRONG_ORDER;
+#endif
+}
+
+
+int VBOXCALL supdrvOSLdrLoad(PSUPDRVDEVEXT pDevExt, PSUPDRVLDRIMAGE pImage, const uint8_t *pbImageBits, PSUPLDRLOAD pReq)
+{
+#ifdef VBOX_WITH_DARWIN_R0_DARWIN_IMAGE_VERIFICATION
+ /* State paranoia. */
+ AssertReturn(pImage->hLdrMod != NIL_RTLDRMOD, VERR_INVALID_STATE);
+ AssertReturn(pImage->hMemAlloc != NIL_RTR0MEMOBJ, VERR_INVALID_STATE);
+ AssertReturn(pImage->pvImage, VERR_INVALID_STATE);
+
+ /*
+ * We should get an identical match with ring-3 here, so the code here is
+ * trivial in comparision to SUPDrv-win.cpp.
+ */
+ if (!memcmp(pImage->pvImage, pbImageBits, pImage->cbImageBits))
+ return VINF_SUCCESS;
+
+ /*
+ * Try show what when wrong (code is copied from supdrvNtCompare).
+ */
+ uint32_t cbLeft = pImage->cbImageBits;
+ const uint8_t *pbNativeBits = (const uint8_t *)pImage->pvImage;
+ for (size_t off = 0; cbLeft > 0; off++, cbLeft--)
+ if (pbNativeBits[off] != pbImageBits[off])
+ {
+ /* Note! We need to copy image bits into a temporary stack buffer here as we'd
+ otherwise risk overwriting them while formatting the error message. */
+ uint8_t abBytes[64];
+ memcpy(abBytes, &pbImageBits[off], RT_MIN(64, cbLeft));
+ supdrvLdrLoadError(VERR_LDR_MISMATCH_NATIVE, pReq,
+ "Mismatch at %#x (%p) of %s loaded at %p:\n"
+ "ring-0: %.*Rhxs\n"
+ "ring-3: %.*Rhxs",
+ off, &pbNativeBits[off], pImage->szName, pImage->pvImage,
+ RT_MIN(64, cbLeft), &pbNativeBits[off],
+ RT_MIN(64, cbLeft), &abBytes[0]);
+ printf("SUPDrv: %s\n", pReq->u.Out.szError);
+ break;
+ }
+
+ RT_NOREF(pDevExt);
+ return VERR_LDR_MISMATCH_NATIVE;
+
+#else
+ NOREF(pDevExt); NOREF(pImage); NOREF(pbImageBits); NOREF(pReq);
+ return VERR_NOT_SUPPORTED;
+#endif
+}
+
+
+void VBOXCALL supdrvOSLdrUnload(PSUPDRVDEVEXT pDevExt, PSUPDRVLDRIMAGE pImage)
+{
+#ifdef VBOX_WITH_DARWIN_R0_DARWIN_IMAGE_VERIFICATION
+ if (pImage->hLdrMod != NIL_RTLDRMOD)
+ {
+ int rc = RTLdrClose(pImage->hLdrMod);
+ AssertRC(rc);
+ pImage->hLdrMod = NIL_RTLDRMOD;
+ }
+ if (pImage->hMemAlloc != NIL_RTR0MEMOBJ)
+ {
+ RTR0MemObjFree(pImage->hMemAlloc, true /*fFreeMappings*/);
+ pImage->hMemAlloc = NIL_RTR0MEMOBJ;
+ }
+ NOREF(pDevExt);
+#else
+ NOREF(pDevExt); NOREF(pImage);
+#endif
+}
+
+
+void VBOXCALL supdrvOSLdrNotifyLoaded(PSUPDRVDEVEXT pDevExt, PSUPDRVLDRIMAGE pImage)
+{
+ NOREF(pDevExt); NOREF(pImage);
+}
+
+
+void VBOXCALL supdrvOSLdrNotifyOpened(PSUPDRVDEVEXT pDevExt, PSUPDRVLDRIMAGE pImage, const char *pszFilename)
+{
+#if 1
+ NOREF(pDevExt); NOREF(pImage); NOREF(pszFilename);
+#else
+ /*
+ * Try store the image load address in NVRAM so we can retrived it on panic.
+ * Note! This only works if you're root! - Acutally, it doesn't work at all at the moment. FIXME!
+ */
+ IORegistryEntry *pEntry = IORegistryEntry::fromPath("/options", gIODTPlane);
+ if (pEntry)
+ {
+ char szVar[80];
+ RTStrPrintf(szVar, sizeof(szVar), "vboximage"/*-%s*/, pImage->szName);
+ char szValue[48];
+ RTStrPrintf(szValue, sizeof(szValue), "%#llx,%#llx", (uint64_t)(uintptr_t)pImage->pvImage,
+ (uint64_t)(uintptr_t)pImage->pvImage + pImage->cbImageBits - 1);
+ bool fRc = pEntry->setProperty(szVar, szValue); NOREF(fRc);
+ pEntry->release();
+ SUPR0Printf("fRc=%d '%s'='%s'\n", fRc, szVar, szValue);
+ }
+ /*else
+ SUPR0Printf("failed to find /options in gIODTPlane\n");*/
+#endif
+}
+
+
+void VBOXCALL supdrvOSLdrNotifyUnloaded(PSUPDRVDEVEXT pDevExt, PSUPDRVLDRIMAGE pImage)
+{
+ NOREF(pDevExt); NOREF(pImage);
+}
+
+
+void VBOXCALL supdrvOSLdrRetainWrapperModule(PSUPDRVDEVEXT pDevExt, PSUPDRVLDRIMAGE pImage)
+{
+ RT_NOREF(pDevExt, pImage);
+ AssertFailed();
+}
+
+
+void VBOXCALL supdrvOSLdrReleaseWrapperModule(PSUPDRVDEVEXT pDevExt, PSUPDRVLDRIMAGE pImage)
+{
+ RT_NOREF(pDevExt, pImage);
+ AssertFailed();
+}
+
+
+#ifdef SUPDRV_WITH_MSR_PROBER
+
+typedef struct SUPDRVDARWINMSRARGS
+{
+ RTUINT64U uValue;
+ uint32_t uMsr;
+ int rc;
+} SUPDRVDARWINMSRARGS, *PSUPDRVDARWINMSRARGS;
+
+/**
+ * On CPU worker for supdrvOSMsrProberRead.
+ *
+ * @param idCpu Ignored.
+ * @param pvUser1 Pointer to a SUPDRVDARWINMSRARGS.
+ * @param pvUser2 Ignored.
+ */
+static DECLCALLBACK(void) supdrvDarwinMsrProberReadOnCpu(RTCPUID idCpu, void *pvUser1, void *pvUser2)
+{
+ PSUPDRVDARWINMSRARGS pArgs = (PSUPDRVDARWINMSRARGS)pvUser1;
+ if (g_pfnRdMsr64Carefully)
+ pArgs->rc = g_pfnRdMsr64Carefully(pArgs->uMsr, &pArgs->uValue.u);
+ else if (g_pfnRdMsrCarefully)
+ pArgs->rc = g_pfnRdMsrCarefully(pArgs->uMsr, &pArgs->uValue.s.Lo, &pArgs->uValue.s.Hi);
+ else
+ pArgs->rc = 2;
+ NOREF(idCpu); NOREF(pvUser2);
+}
+
+
+int VBOXCALL supdrvOSMsrProberRead(uint32_t uMsr, RTCPUID idCpu, uint64_t *puValue)
+{
+ if (!g_pfnRdMsr64Carefully && !g_pfnRdMsrCarefully)
+ return VERR_NOT_SUPPORTED;
+
+ SUPDRVDARWINMSRARGS Args;
+ Args.uMsr = uMsr;
+ Args.uValue.u = 0;
+ Args.rc = -1;
+
+ if (idCpu == NIL_RTCPUID)
+ {
+ IPRT_DARWIN_SAVE_EFL_AC();
+ supdrvDarwinMsrProberReadOnCpu(idCpu, &Args, NULL);
+ IPRT_DARWIN_RESTORE_EFL_AC();
+ }
+ else
+ {
+ int rc = RTMpOnSpecific(idCpu, supdrvDarwinMsrProberReadOnCpu, &Args, NULL);
+ if (RT_FAILURE(rc))
+ return rc;
+ }
+
+ if (Args.rc)
+ return VERR_ACCESS_DENIED;
+ *puValue = Args.uValue.u;
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * On CPU worker for supdrvOSMsrProberWrite.
+ *
+ * @param idCpu Ignored.
+ * @param pvUser1 Pointer to a SUPDRVDARWINMSRARGS.
+ * @param pvUser2 Ignored.
+ */
+static DECLCALLBACK(void) supdrvDarwinMsrProberWriteOnCpu(RTCPUID idCpu, void *pvUser1, void *pvUser2)
+{
+ PSUPDRVDARWINMSRARGS pArgs = (PSUPDRVDARWINMSRARGS)pvUser1;
+ if (g_pfnWrMsr64Carefully)
+ pArgs->rc = g_pfnWrMsr64Carefully(pArgs->uMsr, pArgs->uValue.u);
+ else
+ pArgs->rc = 2;
+ NOREF(idCpu); NOREF(pvUser2);
+}
+
+
+int VBOXCALL supdrvOSMsrProberWrite(uint32_t uMsr, RTCPUID idCpu, uint64_t uValue)
+{
+ if (!g_pfnWrMsr64Carefully)
+ return VERR_NOT_SUPPORTED;
+
+ SUPDRVDARWINMSRARGS Args;
+ Args.uMsr = uMsr;
+ Args.uValue.u = uValue;
+ Args.rc = -1;
+
+ if (idCpu == NIL_RTCPUID)
+ {
+ IPRT_DARWIN_SAVE_EFL_AC();
+ supdrvDarwinMsrProberWriteOnCpu(idCpu, &Args, NULL);
+ IPRT_DARWIN_RESTORE_EFL_AC();
+ }
+ else
+ {
+ int rc = RTMpOnSpecific(idCpu, supdrvDarwinMsrProberWriteOnCpu, &Args, NULL);
+ if (RT_FAILURE(rc))
+ return rc;
+ }
+
+ if (Args.rc)
+ return VERR_ACCESS_DENIED;
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * Worker for supdrvOSMsrProberModify.
+ */
+static DECLCALLBACK(void) supdrvDarwinMsrProberModifyOnCpu(RTCPUID idCpu, void *pvUser1, void *pvUser2)
+{
+ RT_NOREF(idCpu, pvUser2);
+ PSUPMSRPROBER pReq = (PSUPMSRPROBER)pvUser1;
+ register uint32_t uMsr = pReq->u.In.uMsr;
+ bool const fFaster = pReq->u.In.enmOp == SUPMSRPROBEROP_MODIFY_FASTER;
+ uint64_t uBefore;
+ uint64_t uWritten;
+ uint64_t uAfter;
+ int rcBefore, rcWrite, rcAfter, rcRestore;
+ RTCCUINTREG fOldFlags;
+
+ /* Initialize result variables. */
+ uBefore = uWritten = uAfter = 0;
+ rcWrite = rcAfter = rcRestore = -1;
+
+ /*
+ * Do the job.
+ */
+ fOldFlags = ASMIntDisableFlags();
+ ASMCompilerBarrier(); /* paranoia */
+ if (!fFaster)
+ ASMWriteBackAndInvalidateCaches();
+
+ rcBefore = g_pfnRdMsr64Carefully(uMsr, &uBefore);
+ if (rcBefore >= 0)
+ {
+ register uint64_t uRestore = uBefore;
+ uWritten = uRestore;
+ uWritten &= pReq->u.In.uArgs.Modify.fAndMask;
+ uWritten |= pReq->u.In.uArgs.Modify.fOrMask;
+
+ rcWrite = g_pfnWrMsr64Carefully(uMsr, uWritten);
+ rcAfter = g_pfnRdMsr64Carefully(uMsr, &uAfter);
+ rcRestore = g_pfnWrMsr64Carefully(uMsr, uRestore);
+
+ if (!fFaster)
+ {
+ ASMWriteBackAndInvalidateCaches();
+ ASMReloadCR3();
+ ASMNopPause();
+ }
+ }
+
+ ASMCompilerBarrier(); /* paranoia */
+ ASMSetFlags(fOldFlags);
+
+ /*
+ * Write out the results.
+ */
+ pReq->u.Out.uResults.Modify.uBefore = uBefore;
+ pReq->u.Out.uResults.Modify.uWritten = uWritten;
+ pReq->u.Out.uResults.Modify.uAfter = uAfter;
+ pReq->u.Out.uResults.Modify.fBeforeGp = rcBefore != 0;
+ pReq->u.Out.uResults.Modify.fModifyGp = rcWrite != 0;
+ pReq->u.Out.uResults.Modify.fAfterGp = rcAfter != 0;
+ pReq->u.Out.uResults.Modify.fRestoreGp = rcRestore != 0;
+ RT_ZERO(pReq->u.Out.uResults.Modify.afReserved);
+}
+
+
+int VBOXCALL supdrvOSMsrProberModify(RTCPUID idCpu, PSUPMSRPROBER pReq)
+{
+ if (!g_pfnWrMsr64Carefully || !g_pfnRdMsr64Carefully)
+ return VERR_NOT_SUPPORTED;
+ if (idCpu == NIL_RTCPUID)
+ {
+ IPRT_DARWIN_SAVE_EFL_AC();
+ supdrvDarwinMsrProberModifyOnCpu(idCpu, pReq, NULL);
+ IPRT_DARWIN_RESTORE_EFL_AC();
+ return VINF_SUCCESS;
+ }
+ return RTMpOnSpecific(idCpu, supdrvDarwinMsrProberModifyOnCpu, pReq, NULL);
+}
+
+#endif /* SUPDRV_WITH_MSR_PROBER */
+
+/**
+ * Resume Bluetooth keyboard.
+ * If there is no Bluetooth keyboard device connected to the system we just ignore this.
+ */
+static void supdrvDarwinResumeBluetoothKbd(void)
+{
+ OSDictionary *pDictionary = IOService::serviceMatching("AppleBluetoothHIDKeyboard");
+ if (pDictionary)
+ {
+ OSIterator *pIter;
+ IOBluetoothHIDDriver *pDriver;
+
+ pIter = IOService::getMatchingServices(pDictionary);
+ if (pIter)
+ {
+ while ((pDriver = (IOBluetoothHIDDriver *)pIter->getNextObject()))
+ if (pDriver->isKeyboard())
+ (void)pDriver->hidControl(IOBTHID_CONTROL_EXIT_SUSPEND);
+
+ pIter->release();
+ }
+ pDictionary->release();
+ }
+}
+
+/**
+ * Resume built-in keyboard on MacBook Air and Pro hosts.
+ * If there is no built-in keyboard device attached to the system we just ignore this.
+ */
+static void supdrvDarwinResumeBuiltinKbd(void)
+{
+ /** @todo macbook pro 16 w/ 10.15.5 as the "Apple Internal Keyboard /
+ * Trackpad" hooked up to "HID Relay" / "AppleUserUSBHostHIDDevice"
+ * and "AppleUserUSBHostHIDDevice" among other things, but not
+ * "AppleUSBTCKeyboard". This change is probably older than 10.15,
+ * given that IOUSBHIDDriver not is present in the 10.11 SDK. */
+#if MAC_OS_X_VERSION_MIN_REQUIRED < 101100
+ /*
+ * AppleUSBTCKeyboard KEXT is responsible for built-in keyboard management.
+ * We resume keyboard by accessing to its IOService.
+ */
+ OSDictionary *pDictionary = IOService::serviceMatching("AppleUSBTCKeyboard");
+ if (pDictionary)
+ {
+ OSIterator *pIter;
+ IOUSBHIDDriver *pDriver;
+
+ pIter = IOService::getMatchingServices(pDictionary);
+ if (pIter)
+ {
+ while ((pDriver = (IOUSBHIDDriver *)pIter->getNextObject()))
+ if (pDriver->IsPortSuspended())
+ pDriver->SuspendPort(false, 0);
+
+ pIter->release();
+ }
+ pDictionary->release();
+ }
+#endif
+}
+
+
+/**
+ * Resume suspended keyboard devices (if any).
+ */
+int VBOXCALL supdrvDarwinResumeSuspendedKbds(void)
+{
+ IPRT_DARWIN_SAVE_EFL_AC();
+ supdrvDarwinResumeBuiltinKbd();
+ supdrvDarwinResumeBluetoothKbd();
+ IPRT_DARWIN_RESTORE_EFL_AC();
+ return 0;
+}
+
+
+/**
+ * Converts an IPRT error code to a darwin error code.
+ *
+ * @returns corresponding darwin error code.
+ * @param rc IPRT status code.
+ */
+static int VBoxDrvDarwinErr2DarwinErr(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;
+}
+
+
+/**
+ * Check if the CPU has SMAP support.
+ */
+static bool vboxdrvDarwinCpuHasSMAP(void)
+{
+ uint32_t uMaxId, uEAX, uEBX, uECX, uEDX;
+ ASMCpuId(0, &uMaxId, &uEBX, &uECX, &uEDX);
+ if ( RTX86IsValidStdRange(uMaxId)
+ && uMaxId >= 0x00000007)
+ {
+ ASMCpuId_Idx_ECX(0x00000007, 0, &uEAX, &uEBX, &uECX, &uEDX);
+ if (uEBX & X86_CPUID_STEXT_FEATURE_EBX_SMAP)
+ return true;
+ }
+#ifdef VBOX_WITH_EFLAGS_AC_SET_IN_VBOXDRV
+ return true;
+#else
+ return false;
+#endif
+}
+
+
+RTDECL(int) SUPR0PrintfV(const char *pszFormat, va_list va)
+{
+ IPRT_DARWIN_SAVE_EFL_AC();
+
+ char szMsg[512];
+ RTStrPrintfV(szMsg, sizeof(szMsg) - 1, pszFormat, va);
+ szMsg[sizeof(szMsg) - 1] = '\0';
+
+ printf("%s", szMsg);
+ kprintf("%s", szMsg);
+
+ IPRT_DARWIN_RESTORE_EFL_AC();
+ return 0;
+}
+
+
+SUPR0DECL(uint32_t) SUPR0GetKernelFeatures(void)
+{
+ return g_fKernelFeatures;
+}
+
+
+SUPR0DECL(bool) SUPR0FpuBegin(bool fCtxHook)
+{
+ RT_NOREF(fCtxHook);
+ return false;
+}
+
+
+SUPR0DECL(void) SUPR0FpuEnd(bool fCtxHook)
+{
+ RT_NOREF(fCtxHook);
+}
+
+/*
+ *
+ * org_virtualbox_SupDrv
+ *
+ * - IOService diff resync -
+ * - IOService diff resync -
+ * - IOService diff resync -
+ *
+ */
+
+
+/**
+ * Initialize the object.
+ */
+bool org_virtualbox_SupDrv::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_SupDrv::free(void)
+{
+ LogFlow(("IOService::free([%p])\n", this));
+ IOService::free();
+}
+
+
+/**
+ * Check if it's ok to start this service.
+ * It's always ok by us, so it's up to IOService to decide really.
+ */
+IOService *org_virtualbox_SupDrv::probe(IOService *pProvider, SInt32 *pi32Score)
+{
+ LogFlow(("IOService::probe([%p])\n", this));
+ return IOService::probe(pProvider, pi32Score);
+}
+
+
+/**
+ * Start this service.
+ */
+bool org_virtualbox_SupDrv::start(IOService *pProvider)
+{
+ LogFlow(("org_virtualbox_SupDrv::start([%p])\n", this));
+
+ if (IOService::start(pProvider))
+ {
+ /* register the service. */
+ registerService();
+ return true;
+ }
+ return false;
+}
+
+
+/**
+ * Stop this service.
+ */
+void org_virtualbox_SupDrv::stop(IOService *pProvider)
+{
+ LogFlow(("org_virtualbox_SupDrv::stop([%p], %p)\n", this, pProvider));
+ IOService::stop(pProvider);
+}
+
+
+/**
+ * Termination request.
+ *
+ * @return true if we're ok with shutting down now, false if we're not.
+ * @param fOptions Flags.
+ */
+bool org_virtualbox_SupDrv::terminate(IOOptionBits fOptions)
+{
+ bool fRc;
+ LogFlow(("org_virtualbox_SupDrv::terminate: reference_count=%d g_cSessions=%d (fOptions=%#x)\n",
+ KMOD_INFO_NAME.reference_count, ASMAtomicUoReadS32(&g_cSessions), fOptions));
+ if ( KMOD_INFO_NAME.reference_count != 0
+ || ASMAtomicUoReadS32(&g_cSessions))
+ fRc = false;
+ else
+ fRc = IOService::terminate(fOptions);
+ LogFlow(("org_virtualbox_SupDrv::terminate: returns %d\n", fRc));
+ return fRc;
+}
+
+
+/*
+ *
+ * org_virtualbox_SupDrvClient
+ *
+ */
+
+
+/**
+ * Initializer called when the client opens the service.
+ */
+bool org_virtualbox_SupDrvClient::initWithTask(task_t OwningTask, void *pvSecurityId, UInt32 u32Type)
+{
+ LogFlow(("org_virtualbox_SupDrvClient::initWithTask([%p], %#x, %p, %#x) (cur pid=%d proc=%p)\n",
+ this, OwningTask, pvSecurityId, u32Type, RTProcSelf(), RTR0ProcHandleSelf()));
+ AssertMsg((RTR0PROCESS)OwningTask == RTR0ProcHandleSelf(), ("%p %p\n", OwningTask, RTR0ProcHandleSelf()));
+
+ if (!OwningTask)
+ return false;
+
+ if (u32Type != SUP_DARWIN_IOSERVICE_COOKIE)
+ {
+ VBOX_RETRIEVE_CUR_PROC_NAME(szProcName);
+ LogRelMax(10,("org_virtualbox_SupDrvClient::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_SupDrvClient::start(IOService *pProvider)
+{
+ LogFlow(("org_virtualbox_SupDrvClient::start([%p], %p) (cur pid=%d proc=%p)\n",
+ this, pProvider, RTProcSelf(), RTR0ProcHandleSelf() ));
+ AssertMsgReturn((RTR0PROCESS)m_Task == RTR0ProcHandleSelf(),
+ ("%p %p\n", m_Task, RTR0ProcHandleSelf()),
+ false);
+
+ if (IOUserClient::start(pProvider))
+ {
+ m_pProvider = OSDynamicCast(org_virtualbox_SupDrv, pProvider);
+ if (m_pProvider)
+ {
+ Assert(!m_pSession);
+
+ /*
+ * Create a new session.
+ */
+ int rc = supdrvCreateSession(&g_DevExt, true /* fUser */, false /*fUnrestricted*/, &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);
+
+ PSUPDRVSESSION 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->pvSupDrvClient = this;
+ ASMAtomicIncS32(&g_cSessions);
+ rc = VINF_SUCCESS;
+ }
+ else
+ rc = VERR_ALREADY_LOADED;
+
+ RTSpinlockRelease(g_Spinlock);
+ if (RT_SUCCESS(rc))
+ {
+ Log(("org_virtualbox_SupDrvClient::start: created session %p for pid %d\n", m_pSession, (int)RTProcSelf()));
+ return true;
+ }
+
+ LogFlow(("org_virtualbox_SupDrvClient::start: already got a session for this process (%p)\n", pCur));
+ supdrvSessionRelease(m_pSession);
+ }
+
+ m_pSession = NULL;
+ LogFlow(("org_virtualbox_SupDrvClient::start: rc=%Rrc from supdrvCreateSession\n", rc));
+ }
+ else
+ LogFlow(("org_virtualbox_SupDrvClient::start: %p isn't org_virtualbox_SupDrv\n", pProvider));
+ }
+ return false;
+}
+
+
+/**
+ * Common worker for clientClose and VBoxDrvDarwinClose.
+ */
+/* static */ void org_virtualbox_SupDrvClient::sessionClose(RTPROCESS Process)
+{
+ /*
+ * Find the session and remove it from the hash table.
+ *
+ * Note! Only one session per process. (Both start() and
+ * VBoxDrvDarwinOpen makes sure this is so.)
+ */
+ const unsigned iHash = SESSION_HASH(Process);
+ RTSpinlockAcquire(g_Spinlock);
+ PSUPDRVSESSION pSession = g_apSessionHashTab[iHash];
+ if (pSession)
+ {
+ if (pSession->Process == Process)
+ {
+ g_apSessionHashTab[iHash] = pSession->pNextHash;
+ pSession->pNextHash = NULL;
+ ASMAtomicDecS32(&g_cSessions);
+ }
+ else
+ {
+ PSUPDRVSESSION pPrev = pSession;
+ pSession = pSession->pNextHash;
+ while (pSession)
+ {
+ if (pSession->Process == Process)
+ {
+ pPrev->pNextHash = pSession->pNextHash;
+ pSession->pNextHash = NULL;
+ ASMAtomicDecS32(&g_cSessions);
+ break;
+ }
+
+ /* next */
+ pPrev = pSession;
+ pSession = pSession->pNextHash;
+ }
+ }
+ }
+ RTSpinlockRelease(g_Spinlock);
+ if (!pSession)
+ {
+ Log(("SupDrvClient::sessionClose: pSession == NULL, pid=%d; freed already?\n", (int)Process));
+ return;
+ }
+
+ /*
+ * Remove it from the client object.
+ */
+ org_virtualbox_SupDrvClient *pThis = (org_virtualbox_SupDrvClient *)pSession->pvSupDrvClient;
+ pSession->pvSupDrvClient = NULL;
+ if (pThis)
+ {
+ Assert(pThis->m_pSession == pSession);
+ pThis->m_pSession = NULL;
+ }
+
+ /*
+ * Close the session.
+ */
+ supdrvSessionRelease(pSession);
+}
+
+
+/**
+ * Client exits normally.
+ */
+IOReturn org_virtualbox_SupDrvClient::clientClose(void)
+{
+ LogFlow(("org_virtualbox_SupDrvClient::clientClose([%p]) (cur pid=%d proc=%p)\n", this, RTProcSelf(), RTR0ProcHandleSelf()));
+ AssertMsg((RTR0PROCESS)m_Task == RTR0ProcHandleSelf(), ("%p %p\n", m_Task, RTR0ProcHandleSelf()));
+
+ /*
+ * Clean up the session if it's still around.
+ *
+ * We cannot rely 100% on close, and in the case of a dead client
+ * we'll end up hanging inside vm_map_remove() if we postpone it.
+ */
+ if (m_pSession)
+ {
+ sessionClose(RTProcSelf());
+ Assert(!m_pSession);
+ }
+
+ m_pProvider = NULL;
+ terminate();
+
+ return kIOReturnSuccess;
+}
+
+
+/**
+ * The client exits abnormally / forgets to do cleanups. (logging)
+ */
+IOReturn org_virtualbox_SupDrvClient::clientDied(void)
+{
+ LogFlow(("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_SupDrvClient::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_SupDrvClient::finalize(IOOptionBits fOptions)
+{
+ LogFlow(("IOService::finalize([%p], %#x)\n", this, fOptions));
+ return IOUserClient::finalize(fOptions);
+}
+
+
+/**
+ * Stop the client service. (logging)
+ */
+void org_virtualbox_SupDrvClient::stop(IOService *pProvider)
+{
+ LogFlow(("IOService::stop([%p])\n", this));
+ IOUserClient::stop(pProvider);
+}
+
diff --git a/src/VBox/HostDrivers/Support/darwin/SUPLib-darwin.cpp b/src/VBox/HostDrivers/Support/darwin/SUPLib-darwin.cpp
new file mode 100644
index 00000000..77f1db79
--- /dev/null
+++ b/src/VBox/HostDrivers/Support/darwin/SUPLib-darwin.cpp
@@ -0,0 +1,333 @@
+/* $Id: SUPLib-darwin.cpp $ */
+/** @file
+ * VirtualBox Support Library - Darwin specific parts.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#define LOG_GROUP LOG_GROUP_SUP
+#ifdef IN_SUP_HARDENED_R3
+# undef DEBUG /* Warning: disables RT_STRICT */
+# ifndef LOG_DISABLED
+# define LOG_DISABLED
+# endif
+# define RTLOG_REL_DISABLED
+# include <iprt/log.h>
+#endif
+
+#include <VBox/types.h>
+#include <VBox/sup.h>
+#include <VBox/param.h>
+#include <VBox/err.h>
+#include <VBox/log.h>
+#include <iprt/path.h>
+#include <iprt/assert.h>
+#include <iprt/err.h>
+#include <iprt/string.h>
+#include "../SUPLibInternal.h"
+#include "../SUPDrvIOC.h"
+
+#include <sys/fcntl.h>
+#include <sys/ioctl.h>
+#include <errno.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <mach/mach_port.h>
+#include <IOKit/IOKitLib.h>
+
+
+/*********************************************************************************************************************************
+* Defined Constants And Macros *
+*********************************************************************************************************************************/
+/** System device name. */
+#define DEVICE_NAME_SYS "/dev/vboxdrv"
+/** User device name. */
+#define DEVICE_NAME_USR "/dev/vboxdrvu"
+/** The IOClass key of the service (see SUPDrv-darwin.cpp / Info.plist). */
+#define IOCLASS_NAME "org_virtualbox_SupDrv"
+
+
+
+/**
+ * Opens the BSD device node.
+ *
+ * @returns VBox status code.
+ */
+static int suplibDarwinOpenDevice(PSUPLIBDATA pThis, bool fUnrestricted)
+{
+ /*
+ * Open the BSD device.
+ * This will connect to the session created when the SupDrvClient was
+ * started, so it has to be done after opening the service (IOC v9.1+).
+ */
+ int hDevice = open(fUnrestricted ? DEVICE_NAME_SYS : DEVICE_NAME_USR, O_RDWR, 0);
+ if (hDevice < 0)
+ {
+ int rc;
+ switch (errno)
+ {
+ case ENODEV: rc = VERR_VM_DRIVER_LOAD_ERROR; break;
+ case EPERM:
+ case EACCES: rc = VERR_VM_DRIVER_NOT_ACCESSIBLE; break;
+ case ENOENT: rc = VERR_VM_DRIVER_NOT_INSTALLED; break;
+ default: rc = VERR_VM_DRIVER_OPEN_ERROR; break;
+ }
+ LogRel(("SUP: Failed to open \"%s\", errno=%d, rc=%Rrc\n", fUnrestricted ? DEVICE_NAME_SYS : DEVICE_NAME_USR, errno, rc));
+ return rc;
+ }
+
+ /*
+ * Mark the file handle close on exec.
+ */
+ if (fcntl(hDevice, F_SETFD, FD_CLOEXEC) != 0)
+ {
+#ifdef IN_SUP_HARDENED_R3
+ int rc = VERR_INTERNAL_ERROR;
+#else
+ int err = errno;
+ int rc = RTErrConvertFromErrno(err);
+ LogRel(("suplibOSInit: setting FD_CLOEXEC failed, errno=%d (%Rrc)\n", err, rc));
+#endif
+ close(hDevice);
+ return rc;
+ }
+
+ pThis->hDevice = hDevice;
+ pThis->fUnrestricted = fUnrestricted;
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * Opens the IOKit service, instantiating org_virtualbox_SupDrvClient.
+ *
+ * @returns VBox status code.
+ */
+static int suplibDarwinOpenService(PSUPLIBDATA pThis)
+{
+ /*
+ * Open the IOKit client first - The first step is finding the service.
+ */
+ mach_port_t MasterPort;
+ kern_return_t kr = IOMasterPort(MACH_PORT_NULL, &MasterPort);
+ if (kr != kIOReturnSuccess)
+ {
+ LogRel(("IOMasterPort -> %d\n", kr));
+ return VERR_GENERAL_FAILURE;
+ }
+
+ CFDictionaryRef ClassToMatch = IOServiceMatching(IOCLASS_NAME);
+ if (!ClassToMatch)
+ {
+ LogRel(("IOServiceMatching(\"%s\") failed.\n", IOCLASS_NAME));
+ return VERR_GENERAL_FAILURE;
+ }
+
+ /* Create an io_iterator_t for all instances of our drivers class that exist in the IORegistry. */
+ io_iterator_t Iterator;
+ kr = IOServiceGetMatchingServices(MasterPort, ClassToMatch, &Iterator);
+ if (kr != kIOReturnSuccess)
+ {
+ LogRel(("IOServiceGetMatchingServices returned %d\n", kr));
+ return VERR_GENERAL_FAILURE;
+ }
+
+ /* Get the first item in the iterator and release it. */
+ io_service_t ServiceObject = IOIteratorNext(Iterator);
+ IOObjectRelease(Iterator);
+ if (!ServiceObject)
+ {
+ LogRel(("SUP: Couldn't find any matches. The kernel module is probably not loaded.\n"));
+ return VERR_VM_DRIVER_NOT_INSTALLED;
+ }
+
+ /*
+ * Open the service.
+ *
+ * This will cause the user client class in SUPDrv-darwin.cpp to be
+ * instantiated and create a session for this process.
+ */
+ io_connect_t Connection = 0;
+ kr = IOServiceOpen(ServiceObject, mach_task_self(), SUP_DARWIN_IOSERVICE_COOKIE, &Connection);
+ IOObjectRelease(ServiceObject);
+ if (kr != kIOReturnSuccess)
+ {
+ LogRel(("SUP: IOServiceOpen returned %d. Driver open failed.\n", kr));
+ pThis->uConnection = 0;
+ return VERR_VM_DRIVER_OPEN_ERROR;
+ }
+
+ AssertCompile(sizeof(pThis->uConnection) >= sizeof(Connection));
+ pThis->uConnection = Connection;
+ return VINF_SUCCESS;
+}
+
+
+DECLHIDDEN(int) suplibOsInit(PSUPLIBDATA pThis, bool fPreInited, uint32_t fFlags, SUPINITOP *penmWhat, PRTERRINFO pErrInfo)
+{
+ RT_NOREF(penmWhat, pErrInfo);
+
+ /*
+ * Nothing to do if pre-inited.
+ */
+ if (fPreInited)
+ return VINF_SUCCESS;
+
+ /*
+ * Driverless?
+ */
+ if (fFlags & SUPR3INIT_F_DRIVERLESS)
+ {
+ pThis->fDriverless = true;
+ return VINF_SUCCESS;
+ }
+
+ /*
+ * Do the job.
+ */
+ Assert(pThis->hDevice == (intptr_t)NIL_RTFILE);
+ int rc = suplibDarwinOpenService(pThis);
+ if (RT_SUCCESS(rc))
+ {
+ rc = suplibDarwinOpenDevice(pThis, RT_BOOL(fFlags & SUPR3INIT_F_UNRESTRICTED));
+ if (RT_FAILURE(rc))
+ {
+ kern_return_t kr = IOServiceClose((io_connect_t)pThis->uConnection);
+ if (kr != kIOReturnSuccess)
+ {
+ LogRel(("Warning: IOServiceClose(%RCv) returned %d\n", pThis->uConnection, kr));
+ AssertFailed();
+ }
+ pThis->uConnection = 0;
+ }
+ }
+ if ( RT_FAILURE(rc)
+ && fFlags & SUPR3INIT_F_DRIVERLESS_MASK)
+ {
+ LogRel(("Failed to open \"%s\", rc=%Rrc - Switching to driverless mode.\n", IOCLASS_NAME, rc));
+ pThis->fDriverless = true;
+ rc = VINF_SUCCESS;
+ }
+
+ return rc;
+}
+
+
+DECLHIDDEN(int) suplibOsTerm(PSUPLIBDATA pThis)
+{
+ /*
+ * Close the connection to the IOService.
+ * This will cause the SUPDRVSESSION to be closed (starting IOC 9.1).
+ */
+ if (pThis->uConnection)
+ {
+ kern_return_t kr = IOServiceClose((io_connect_t)pThis->uConnection);
+ if (kr != kIOReturnSuccess)
+ {
+ LogRel(("Warning: IOServiceClose(%RCv) returned %d\n", pThis->uConnection, kr));
+ AssertFailed();
+ }
+ pThis->uConnection = 0;
+ }
+
+ /*
+ * Check if we're inited at all.
+ */
+ if (pThis->hDevice != (intptr_t)NIL_RTFILE)
+ {
+ if (close(pThis->hDevice))
+ AssertFailed();
+ pThis->hDevice = (intptr_t)NIL_RTFILE;
+ }
+
+ return VINF_SUCCESS;
+}
+
+
+#ifndef IN_SUP_HARDENED_R3
+
+DECLHIDDEN(int) suplibOsInstall(void)
+{
+ return VERR_NOT_IMPLEMENTED;
+}
+
+
+DECLHIDDEN(int) suplibOsUninstall(void)
+{
+ return VERR_NOT_IMPLEMENTED;
+}
+
+
+DECLHIDDEN(int) suplibOsIOCtl(PSUPLIBDATA pThis, uintptr_t uFunction, void *pvReq, size_t cbReq)
+{
+ RT_NOREF(cbReq);
+ if (RT_LIKELY(ioctl(pThis->hDevice, uFunction, pvReq) >= 0))
+ return VINF_SUCCESS;
+ return RTErrConvertFromErrno(errno);
+}
+
+
+DECLHIDDEN(int) suplibOsIOCtlFast(PSUPLIBDATA pThis, uintptr_t uFunction, uintptr_t idCpu)
+{
+ int rc = ioctl(pThis->hDevice, uFunction, idCpu);
+ if (rc == -1)
+ rc = errno;
+ return rc;
+}
+
+
+DECLHIDDEN(int) suplibOsPageAlloc(PSUPLIBDATA pThis, size_t cPages, uint32_t fFlags, void **ppvPages)
+{
+ RT_NOREF(pThis, fFlags);
+ *ppvPages = valloc(cPages << PAGE_SHIFT);
+ if (*ppvPages)
+ {
+ memset(*ppvPages, 0, cPages << PAGE_SHIFT);
+ return VINF_SUCCESS;
+ }
+ return RTErrConvertFromErrno(errno);
+}
+
+
+DECLHIDDEN(int) suplibOsPageFree(PSUPLIBDATA pThis, void *pvPages, size_t /* cPages */)
+{
+ NOREF(pThis);
+ free(pvPages);
+ return VINF_SUCCESS;
+}
+
+#endif /* !IN_SUP_HARDENED_R3 */
+
diff --git a/src/VBox/HostDrivers/Support/darwin/SUPR0IdcClient-darwin.c b/src/VBox/HostDrivers/Support/darwin/SUPR0IdcClient-darwin.c
new file mode 100644
index 00000000..4899d896
--- /dev/null
+++ b/src/VBox/HostDrivers/Support/darwin/SUPR0IdcClient-darwin.c
@@ -0,0 +1,66 @@
+/* $Id: SUPR0IdcClient-darwin.c $ */
+/** @file
+ * VirtualBox Support Driver - IDC Client Lib, Darwin Specific Code.
+ */
+
+/*
+ * Copyright (C) 2008-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include "../SUPR0IdcClientInternal.h"
+#include <iprt/errcore.h>
+
+
+int VBOXCALL supR0IdcNativeOpen(PSUPDRVIDCHANDLE pHandle, PSUPDRVIDCREQCONNECT pReq)
+{
+ return supR0IdcNativeCall(pHandle, SUPDRV_IDC_REQ_CONNECT, &pReq->Hdr);
+}
+
+
+int VBOXCALL supR0IdcNativeClose(PSUPDRVIDCHANDLE pHandle, PSUPDRVIDCREQHDR pReq)
+{
+ return supR0IdcNativeCall(pHandle, SUPDRV_IDC_REQ_DISCONNECT, pReq);
+}
+
+
+int VBOXCALL supR0IdcNativeCall(PSUPDRVIDCHANDLE pHandle, uint32_t iReq, PSUPDRVIDCREQHDR pReq)
+{
+ int rc = SUPDrvDarwinIDC(iReq, pReq);
+ if (RT_SUCCESS(rc))
+ rc = pReq->rc;
+
+ NOREF(pHandle);
+ return rc;
+}
+
diff --git a/src/VBox/HostDrivers/Support/darwin/SUPR3HardenedEntitlements.plist b/src/VBox/HostDrivers/Support/darwin/SUPR3HardenedEntitlements.plist
new file mode 100644
index 00000000..3587afb0
--- /dev/null
+++ b/src/VBox/HostDrivers/Support/darwin/SUPR3HardenedEntitlements.plist
@@ -0,0 +1,37 @@
+<?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>com.apple.security.cs.allow-jit</key> <true/> -->
+ <!-- <key>com.apple.security.cs.allow-unsigned-executable-memory</key> <true/> -->
+ <!--
+ The following two are required here even though they apply only to the VM
+ process. The issue is that TCC is looking up the primary bundle for these entitlements
+ and crashes the VM process if the entitlements are not here even though they are used
+ in the VM process only. This is not documented anywhere by Apple.
+ From Console.app when these are missing:
+
+ tccd: [com.apple.TCC:access] Prompting policy for hardened runtime; service:
+ kTCCServiceMicrophone requires entitlement com.apple.security.device.audio-input but it is missing for
+ RESP:{
+ ID: org.virtualbox.app.VirtualBox,
+ PID[17253],
+ auid: 501,
+ euid: 501,
+ responsible path: '/Applications/VirtualBox.app/Contents/MacOS/VirtualBox',
+ binary path: '/Applications/VirtualBox.app/Contents/MacOS/VirtualBox'
+ },
+ REQ:{
+ ID: org.virtualbox.app.VirtualBoxVM,
+ PID[17331],
+ auid: 501,
+ euid: 501,
+ binary path: '/Applications/VirtualBox.app/Contents/Resources/VirtualBoxVM.app/Contents/MacOS/VirtualBoxVM'
+ }
+ -->
+ <!-- For audio input -->
+ <key>com.apple.security.device.audio-input</key> <true/>
+ <!-- For emulated webcam -->
+ <key>com.apple.security.device.camera</key> <true/>
+</dict>
+</plist>
diff --git a/src/VBox/HostDrivers/Support/darwin/SUPR3HardenedEntitlementsVM.plist b/src/VBox/HostDrivers/Support/darwin/SUPR3HardenedEntitlementsVM.plist
new file mode 100644
index 00000000..b1ce780b
--- /dev/null
+++ b/src/VBox/HostDrivers/Support/darwin/SUPR3HardenedEntitlementsVM.plist
@@ -0,0 +1,35 @@
+<?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>com.apple.security.cs.allow-jit</key> <true/> -->
+ <!--
+ The following two entitlements are required for using AppleHV on Catalina.
+ The first entitlement allows us to have unsigned executable memory in the guests
+ address space like the BIOS code (and essentially all the guests address space which
+ is mapped as RWX).
+ The second entitlement is required in order to map guest memory as RWX into the
+ guests address space.
+ These entitlements are not required starting with BigSur+ where Apple has clearly
+ changed something in their entitlement scheme without properly documenting it.
+ -->
+ <key>com.apple.security.cs.allow-unsigned-executable-memory</key> <true/>
+ <key>com.apple.security.cs.disable-executable-page-protection</key> <true/>
+ <!-- For audio input -->
+ <key>com.apple.security.device.audio-input</key> <true/>
+ <!-- For emulated webcam -->
+ <key>com.apple.security.device.camera</key> <true/>
+ <!-- For HID input monitoring -->
+ <key>com.apple.security.device.usb</key> <true/>
+ <!-- For vmnet based bridging and host-only networking -->
+ <key>com.apple.vm.networking</key> <true/>
+ <!-- For USB capturing -->
+ <key>com.apple.vm.device-access</key> <true/>
+ <!--
+ The following are required for using AppleHV (need the second one for running Catalina)
+ but are actually stored in the provisioning profile because these are special entitlements.
+ -->
+ <!--<key>com.apple.security.hypervisor</key> <true/>-->
+ <!--<key>com.apple.vm.hypervisor</key> <true/>-->
+</dict>
+</plist>
diff --git a/src/VBox/HostDrivers/Support/darwin/SUPR3HardenedMain-darwin.cpp b/src/VBox/HostDrivers/Support/darwin/SUPR3HardenedMain-darwin.cpp
new file mode 100644
index 00000000..58e7e619
--- /dev/null
+++ b/src/VBox/HostDrivers/Support/darwin/SUPR3HardenedMain-darwin.cpp
@@ -0,0 +1,287 @@
+/* $Id: SUPR3HardenedMain-darwin.cpp $ */
+/** @file
+ * VirtualBox Support Library - Hardened main(), posix bits.
+ */
+
+/*
+ * Copyright (C) 2017-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include <VBox/err.h>
+#include <VBox/sup.h>
+
+#include <iprt/path.h>
+#include <iprt/string.h>
+
+#include <dlfcn.h>
+#include <sys/mman.h>
+#include <errno.h>
+#include <sys/sysctl.h> /* sysctlbyname() */
+#include <stdio.h>
+#include <stdint.h>
+#include <unistd.h> /* issetugid() */
+#include <mach-o/dyld.h>
+
+#include "SUPLibInternal.h"
+
+
+/*********************************************************************************************************************************
+* Defined Constants And Macros *
+*********************************************************************************************************************************/
+
+
+/*********************************************************************************************************************************
+* Structures and Typedefs *
+*********************************************************************************************************************************/
+
+/**
+ * Interpose table entry.
+ */
+typedef struct DYLDINTERPOSE
+{
+ /** The symbol address to replace with. */
+ const void *pvReplacement;
+ /** The replaced symbol address. */
+ const void *pvReplacee;
+} DYLDINTERPOSE;
+/** Pointer to an interposer table entry. */
+typedef DYLDINTERPOSE *PDYLDINTERPOSE;
+/** Pointer to a const interposer table entry. */
+typedef const DYLDINTERPOSE *PCDYLDINTERPOSE;
+
+/** @sa dyld_dynamic_interpose(). */
+typedef const mach_header *FNDYLDDYNAMICINTERPOSE(const struct mach_header *mh, PCDYLDINTERPOSE paSym, size_t cSyms);
+/** Pointer to dyld_dynamic_interpose. */
+typedef FNDYLDDYNAMICINTERPOSE *PFNDYLDDYNAMICINTERPOSE;
+
+/** @sa dlopen(). */
+typedef void *FNDLOPEN(const char *path, int mode);
+/** Pointer to dlopen. */
+typedef FNDLOPEN *PFNDLOPEN;
+
+
+/*********************************************************************************************************************************
+* Internal Functions *
+*********************************************************************************************************************************/
+extern "C" void _dyld_register_func_for_add_image(void (*func)(const struct mach_header *mh, intptr_t vmaddr_slide));
+
+static void *supR3HardenedDarwinDlopenInterpose(const char *path, int mode);
+static int supR3HardenedDarwinIssetugidInterpose(void);
+
+
+/*********************************************************************************************************************************
+* Global Variables *
+*********************************************************************************************************************************/
+/** Flag whether macOS 11.x (BigSur) or later was detected.
+ * See comments in supR3HardenedDarwinDlopenInterpose for details. */
+static bool g_fMacOs11Plus = false;
+/** Resolved dyld_dynamic_interpose() value. */
+static PFNDYLDDYNAMICINTERPOSE g_pfnDyldDynamicInterpose = NULL;
+/** Pointer to the real dlopen() function used from the interposer when verification succeeded. */
+static PFNDLOPEN g_pfnDlopenReal = NULL;
+/**
+ * The interposer table.
+ */
+static const DYLDINTERPOSE g_aInterposers[] =
+{
+ { (const void *)(uintptr_t)&supR3HardenedDarwinDlopenInterpose, (const void *)(uintptr_t)&dlopen },
+ { (const void *)(uintptr_t)&supR3HardenedDarwinIssetugidInterpose, (const void *)(uintptr_t)&issetugid }
+};
+
+
+/**
+ * dlopen() interposer which verifies that the path to be loaded meets the criteria for hardened builds.
+ *
+ * @sa dlopen() man page.
+ */
+static void *supR3HardenedDarwinDlopenInterpose(const char *path, int mode)
+{
+ /*
+ * Giving NULL as the filename indicates opening the main program which is fine
+ * We are already loaded and executing after all.
+ *
+ * Filenames without any path component (whether absolute or relative) are allowed
+ * unconditionally too as the loader will only search the default paths configured by root.
+ */
+ if ( path
+ && strchr(path, '/') != NULL)
+ {
+ int rc = VINF_SUCCESS;
+
+ /*
+ * Starting with macOS 11.0 (BigSur) system provided libraries
+ * under /System/Libraries are not stored on the filesystem anymore
+ * but in a dynamic linker cache. The integrity of the linker cache
+ * is maintained by the system and dyld. Our verification code fails because
+ * it can't find the file.
+ * The obvious solution is to exclude paths starting with /System/Libraries
+ * when we run on BigSur. Other paths are still subject to verification.
+ */
+ if ( !g_fMacOs11Plus
+ || strncmp(path, RT_STR_TUPLE("/System/Library")))
+ rc = supR3HardenedVerifyFileFollowSymlinks(path, RTHCUINTPTR_MAX, true /* fMaybe3rdParty */,
+ NULL /* pErrInfo */);
+ if (RT_FAILURE(rc))
+ return NULL;
+ }
+
+ return g_pfnDlopenReal(path, mode);
+}
+
+
+/**
+ * Override this one to try hide the fact that we're setuid to root orginially.
+ *
+ * @sa issetugid() man page.
+ *
+ * Mac OS X: Really ugly hack to bypass a set-uid check in AppKit.
+ *
+ * This will modify the issetugid() function to always return zero. This must
+ * be done _before_ AppKit is initialized, otherwise it will refuse to play ball
+ * with us as it distrusts set-uid processes since Snow Leopard. We, however,
+ * have carefully dropped all root privileges at this point and there should be
+ * no reason for any security concern here.
+ */
+static int supR3HardenedDarwinIssetugidInterpose(void)
+{
+#ifdef DEBUG
+ Dl_info Info = {0};
+ char szMsg[512];
+ size_t cchMsg;
+ const void * uCaller = __builtin_return_address(0);
+ if (dladdr(uCaller, &Info))
+ cchMsg = snprintf(szMsg, sizeof(szMsg), "DEBUG: issetugid_for_AppKit was called by %p %s::%s+%p (via %p)\n",
+ uCaller, Info.dli_fname, Info.dli_sname, (void *)((uintptr_t)uCaller - (uintptr_t)Info.dli_saddr), __builtin_return_address(1));
+ else
+ cchMsg = snprintf(szMsg, sizeof(szMsg), "DEBUG: issetugid_for_AppKit was called by %p (via %p)\n", uCaller, __builtin_return_address(1));
+ write(2, szMsg, cchMsg);
+#endif
+ return 0;
+}
+
+
+/**
+ * Callback to get notified of new images being loaded to be able to apply our dlopn() interposer.
+ *
+ * @param mh Pointer to the mach header of the loaded image.
+ * @param vmaddr_slide The slide value for ASLR.
+ */
+static DECLCALLBACK(void) supR3HardenedDarwinAddImage(const struct mach_header *mh, intptr_t vmaddr_slide)
+{
+ RT_NOREF(vmaddr_slide);
+
+ g_pfnDyldDynamicInterpose(mh, &g_aInterposers[0], RT_ELEMENTS(g_aInterposers));
+}
+
+
+/**
+ * Hardening initialization for macOS hosts.
+ *
+ * @note Doesn't return on error.
+ */
+DECLHIDDEN(void) supR3HardenedDarwinInit(void)
+{
+ /*
+ * Check whether we are running on macOS BigSur by checking kern.osproductversion
+ * available since some point in 2018.
+ */
+ char szVers[256]; RT_ZERO(szVers);
+ size_t cbVers = sizeof(szVers);
+ int rc = sysctlbyname("kern.osproductversion", &szVers[0], &cbVers, NULL, 0);
+ if ( !rc
+ && memcmp(&szVers[0], RT_STR_TUPLE("10.16")) >= 0)
+ g_fMacOs11Plus = true;
+
+ /* Saved to call real dlopen() later on, as we will interpose dlopen() from the main binary in the next step as well. */
+ g_pfnDlopenReal = (PFNDLOPEN)dlsym(RTLD_DEFAULT, "dlopen");
+ g_pfnDyldDynamicInterpose = (PFNDYLDDYNAMICINTERPOSE)dlsym(RTLD_DEFAULT, "dyld_dynamic_interpose");
+ if (!g_pfnDyldDynamicInterpose)
+ supR3HardenedFatalMsg("supR3HardenedDarwinInit", kSupInitOp_Integrity, VERR_SYMBOL_NOT_FOUND,
+ "Failed to find dyld_dynamic_interpose()");
+
+ /*
+ * The following will causes our add image notification to be called for all images loaded so far.
+ * The callback will set up the interposer.
+ */
+ _dyld_register_func_for_add_image(supR3HardenedDarwinAddImage);
+}
+
+
+
+/*
+ * assert.cpp
+ *
+ * ASSUMES working DECLHIDDEN or there will be symbol confusion!
+ */
+
+RTDATADECL(char) g_szRTAssertMsg1[1024];
+RTDATADECL(char) g_szRTAssertMsg2[4096];
+RTDATADECL(const char * volatile) g_pszRTAssertExpr;
+RTDATADECL(const char * volatile) g_pszRTAssertFile;
+RTDATADECL(uint32_t volatile) g_u32RTAssertLine;
+RTDATADECL(const char * volatile) g_pszRTAssertFunction;
+
+RTDECL(bool) RTAssertMayPanic(void)
+{
+ return true;
+}
+
+
+RTDECL(void) RTAssertMsg1(const char *pszExpr, unsigned uLine, const char *pszFile, const char *pszFunction)
+{
+ /*
+ * Fill in the globals.
+ */
+ g_pszRTAssertExpr = pszExpr;
+ g_pszRTAssertFile = pszFile;
+ g_pszRTAssertFunction = pszFunction;
+ g_u32RTAssertLine = uLine;
+ snprintf(g_szRTAssertMsg1, sizeof(g_szRTAssertMsg1),
+ "\n!!Assertion Failed!!\n"
+ "Expression: %s\n"
+ "Location : %s(%u) %s\n",
+ pszExpr, pszFile, uLine, pszFunction);
+}
+
+
+RTDECL(void) RTAssertMsg2V(const char *pszFormat, va_list va)
+{
+ vsnprintf(g_szRTAssertMsg2, sizeof(g_szRTAssertMsg2), pszFormat, va);
+ if (g_enmSupR3HardenedMainState < SUPR3HARDENEDMAINSTATE_CALLED_TRUSTED_MAIN)
+ supR3HardenedFatalMsg(g_pszRTAssertExpr, kSupInitOp_Misc, VERR_INTERNAL_ERROR,
+ "%s%s", g_szRTAssertMsg1, g_szRTAssertMsg2);
+ else
+ supR3HardenedError(VERR_INTERNAL_ERROR, false/*fFatal*/, "%s%s", g_szRTAssertMsg1, g_szRTAssertMsg2);
+}
+
diff --git a/src/VBox/HostDrivers/Support/darwin/load.sh b/src/VBox/HostDrivers/Support/darwin/load.sh
new file mode 100755
index 00000000..b30c154e
--- /dev/null
+++ b/src/VBox/HostDrivers/Support/darwin/load.sh
@@ -0,0 +1,164 @@
+#!/bin/bash
+# $Id: load.sh $
+## @file
+# For development.
+#
+
+#
+# Copyright (C) 2006-2023 Oracle and/or its affiliates.
+#
+# This file is part of VirtualBox base platform packages, as
+# available from https://www.virtualbox.org.
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation, in version 3 of the
+# License.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, see <https://www.gnu.org/licenses>.
+#
+# The contents of this file may alternatively be used under the terms
+# of the Common Development and Distribution License Version 1.0
+# (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+# in the VirtualBox distribution, in which case the provisions of the
+# CDDL are applicable instead of those of the GPL.
+#
+# You may elect to license modified versions of this file under the
+# terms and conditions of either the GPL or the CDDL or both.
+#
+# SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+#
+
+XNU_VERSION=`LC_ALL=C uname -r | LC_ALL=C cut -d . -f 1`
+DRVNAME="VBoxDrv.kext"
+BUNDLE="org.virtualbox.kext.VBoxDrv"
+
+DIR=`dirname "$0"`
+DIR=`cd "$DIR" && pwd`
+DIR="$DIR/$DRVNAME"
+if [ ! -d "$DIR" ]; then
+ echo "Cannot find $DIR or it's not a directory..."
+ exit 1;
+fi
+if [ -n "$*" ]; then
+ OPTS="$*"
+else
+ OPTS="-t"
+fi
+
+# Make sure VBoxUSB is unloaded as it might be using symbols from us.
+LOADED=`kextstat -b org.virtualbox.kext.VBoxUSB -l`
+if test -n "$LOADED"; then
+ echo "load.sh: Unloading org.virtualbox.kext.VBoxUSB..."
+ sudo kextunload -v 6 -b org.virtualbox.kext.VBoxUSB
+ LOADED=`kextstat -b org.virtualbox.kext.VBoxUSB -l`
+ if test -n "$LOADED"; then
+ echo "load.sh: failed to unload org.virtualbox.kext.VBoxUSB, see above..."
+ exit 1;
+ fi
+ echo "load.sh: Successfully unloaded org.virtualbox.kext.VBoxUSB"
+fi
+
+# Make sure VBoxNetFlt is unloaded as it might be using symbols from us.
+LOADED=`kextstat -b org.virtualbox.kext.VBoxNetFlt -l`
+if test -n "$LOADED"; then
+ echo "load.sh: Unloading org.virtualbox.kext.VBoxNetFlt..."
+ sudo kextunload -v 6 -b org.virtualbox.kext.VBoxNetFlt
+ LOADED=`kextstat -b org.virtualbox.kext.VBoxNetFlt -l`
+ if test -n "$LOADED"; then
+ echo "load.sh: failed to unload org.virtualbox.kext.VBoxNetFlt, see above..."
+ exit 1;
+ fi
+ echo "load.sh: Successfully unloaded org.virtualbox.kext.VBoxNetFlt"
+fi
+
+# Make sure VBoxNetAdp is unloaded as it might be using symbols from us.
+LOADED=`kextstat -b org.virtualbox.kext.VBoxNetAdp -l`
+if test -n "$LOADED"; then
+ echo "load.sh: Unloading org.virtualbox.kext.VBoxNetAdp..."
+ sudo kextunload -v 6 -b org.virtualbox.kext.VBoxNetAdp
+ LOADED=`kextstat -b org.virtualbox.kext.VBoxNetAdp -l`
+ if test -n "$LOADED"; then
+ echo "load.sh: failed to unload org.virtualbox.kext.VBoxNetAdp, see above..."
+ exit 1;
+ fi
+ echo "load.sh: Successfully unloaded org.virtualbox.kext.VBoxNetAdp"
+fi
+
+# Try unload any existing instance first.
+LOADED=`kextstat -b $BUNDLE -l`
+if test -n "$LOADED"; then
+ echo "load.sh: Unloading $BUNDLE..."
+ sudo kextunload -v 6 -b $BUNDLE
+ LOADED=`kextstat -b $BUNDLE -l`
+ if test -n "$LOADED"; then
+ echo "load.sh: failed to unload $BUNDLE, see above..."
+ exit 1;
+ fi
+ echo "load.sh: Successfully unloaded $BUNDLE"
+fi
+
+set -e
+
+# Copy the .kext to the symbols directory and tweak the kextload options.
+if test -n "$VBOX_DARWIN_SYMS"; then
+ echo "load.sh: copying the extension the symbol area..."
+ rm -Rf "$VBOX_DARWIN_SYMS/$DRVNAME"
+ mkdir -p "$VBOX_DARWIN_SYMS"
+ cp -R "$DIR" "$VBOX_DARWIN_SYMS/"
+ OPTS="$OPTS -s $VBOX_DARWIN_SYMS/ "
+ sync
+fi
+
+trap "sudo chown -R `whoami` $DIR; exit 1" INT
+trap "sudo chown -R `whoami` $DIR; exit 1" ERR
+# On smbfs, this might succeed just fine but make no actual changes,
+# so we might have to temporarily copy the driver to a local directory.
+if sudo chown -R root:wheel "$DIR"; then
+ OWNER=`/usr/bin/stat -f "%u" "$DIR"`
+else
+ OWNER=1000
+fi
+if test "$OWNER" -ne 0; then
+ TMP_DIR=/tmp/loaddrv.tmp
+ echo "load.sh: chown didn't work on $DIR, using temp location $TMP_DIR/$DRVNAME"
+
+ # clean up first (no sudo rm)
+ if test -e "$TMP_DIR"; then
+ sudo chown -R `whoami` "$TMP_DIR"
+ rm -Rf "$TMP_DIR"
+ fi
+
+ # make a copy and switch over DIR
+ mkdir -p "$TMP_DIR/"
+ sudo cp -Rp "$DIR" "$TMP_DIR/"
+ DIR="$TMP_DIR/$DRVNAME"
+
+ # retry
+ sudo chown -R root:wheel "$DIR"
+fi
+sudo chmod -R o-rwx "$DIR"
+sync
+echo "load.sh: loading $DIR..."
+
+if [ "$XNU_VERSION" -ge "10" ]; then
+ echo "${SCRIPT_NAME}.sh: loading $DIR... (kextutil $OPTS \"$DIR\")"
+ sudo kextutil $OPTS "$DIR"
+else
+ sudo kextload $OPTS "$DIR"
+fi
+sync
+sudo chown -R `whoami` "$DIR"
+#sudo chmod 666 /dev/vboxdrv
+kextstat | grep org.virtualbox.kext
+if [ -n "${VBOX_DARWIN_SYMS}" -a "$XNU_VERSION" -ge "10" ]; then
+ dsymutil -o "${VBOX_DARWIN_SYMS}/${DRVNAME}.dSYM" "${DIR}/Contents/MacOS/`basename -s .kext ${DRVNAME}`"
+ sync
+fi
+
diff --git a/src/VBox/HostDrivers/Support/darwin/sys/dtrace_glue.h b/src/VBox/HostDrivers/Support/darwin/sys/dtrace_glue.h
new file mode 100644
index 00000000..cacb890b
--- /dev/null
+++ b/src/VBox/HostDrivers/Support/darwin/sys/dtrace_glue.h
@@ -0,0 +1,49 @@
+/* $Id: dtrace_glue.h $ */
+/** @file
+ * VirtualBox Support Driver - Darwin, mock-up of missing sys/dtrace-glue.h.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+#ifndef VBOX_INCLUDED_SRC_Support_darwin_sys_dtrace_glue_h
+#define VBOX_INCLUDED_SRC_Support_darwin_sys_dtrace_glue_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+#define _KERNEL
+typedef struct solaris_cred cred_t;
+typedef struct solaris_kthread kthread_t;
+typedef struct solaris_x86_saved_state x86_saved_state_t;
+typedef unsigned int model_t;
+
+#endif /* !VBOX_INCLUDED_SRC_Support_darwin_sys_dtrace_glue_h */
diff --git a/src/VBox/HostDrivers/Support/freebsd/Makefile b/src/VBox/HostDrivers/Support/freebsd/Makefile
new file mode 100644
index 00000000..a174d8ec
--- /dev/null
+++ b/src/VBox/HostDrivers/Support/freebsd/Makefile
@@ -0,0 +1,207 @@
+# $Id: Makefile $
+## @file
+# Makefile for the VirtualBox FreeBSD Host Driver.
+#
+
+#
+# Copyright (C) 2006-2023 Oracle and/or its affiliates.
+#
+# This file is part of VirtualBox base platform packages, as
+# available from https://www.virtualbox.org.
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation, in version 3 of the
+# License.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, see <https://www.gnu.org/licenses>.
+#
+# The contents of this file may alternatively be used under the terms
+# of the Common Development and Distribution License Version 1.0
+# (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+# in the VirtualBox distribution, in which case the provisions of the
+# CDDL are applicable instead of those of the GPL.
+#
+# You may elect to license modified versions of this file under the
+# terms and conditions of either the GPL or the CDDL or both.
+#
+# SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+#
+
+KMOD = vboxdrv
+
+CFLAGS += -DRT_OS_FREEBSD -DIN_RING0 -DIN_RT_R0 -DIN_SUP_R0 -DSUPDRV_WITH_RELEASE_LOGGER -DVBOX -DRT_WITH_VBOX -Iinclude -I. -Ir0drv -w -DVBOX_WITH_HARDENING -DVBOX_WITH_64_BITS_GUESTS
+
+.if (${MACHINE_ARCH} == "i386")
+ CFLAGS += -DRT_ARCH_X86
+.elif (${MACHINE_ARCH} == "amd64")
+ CFLAGS += -DRT_ARCH_AMD64
+.endif
+
+SRCS = \
+ SUPDrv.c \
+ SUPDrvGip.c \
+ SUPDrvSem.c \
+ SUPDrvTracer.c \
+ SUPLibAll.c \
+
+# Include needed interface headers so they are created during build
+SRCS += \
+ device_if.h \
+ bus_if.h
+
+.PATH: ${.CURDIR}/freebsd
+SRCS += \
+ SUPDrv-freebsd.c
+
+.PATH: ${.CURDIR}/alloc
+SRCS += \
+ heapsimple.c \
+ alloc.c
+
+.PATH: ${.CURDIR}/common/err
+SRCS += \
+ RTErrConvertFromErrno.c \
+ RTErrConvertToErrno.c \
+ errinfo.c
+
+.PATH: ${.CURDIR}/common/log
+SRCS += \
+ log.c \
+ logellipsis.c \
+ logrel.c \
+ logrelellipsis.c \
+ logcom.c \
+ logformat.c \
+ RTLogCreateEx.c
+
+.PATH: ${.CURDIR}/common/misc
+SRCS += \
+ RTAssertMsg1Weak.c \
+ RTAssertMsg2.c \
+ RTAssertMsg2Add.c \
+ RTAssertMsg2AddWeak.c \
+ RTAssertMsg2AddWeakV.c \
+ RTAssertMsg2Weak.c \
+ RTAssertMsg2WeakV.c \
+ assert.c \
+ handletable.c \
+ handletablectx.c \
+ once.c \
+ term.c \
+ thread.c
+
+.PATH: ${.CURDIR}/common/string
+SRCS += \
+ RTStrCat.c \
+ RTStrNCmp.c \
+ RTStrNLen.c \
+ RTStrCopy.c \
+ RTStrCopyEx.c \
+ RTStrCopyP.c \
+ RTStrEnd.c \
+ strformat.c \
+ RTStrFormat.c \
+ strformatnum.c \
+ strformatrt.c \
+ strformattype.c \
+ strprintf.c \
+ strprintf-ellipsis.c \
+ strprintf2.c \
+ strprintf2-ellipsis.c \
+ strtonum.c \
+ memchr.c \
+ stringalloc.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 \
+ ipv6.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 \
+ RTLogWriteUser-generic.c \
+ RTMpGetArraySize-generic.c \
+ RTMpOnPair-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 \
+ errvars-generic.c \
+ mppresent-generic.c \
+ timer-generic.c
+
+.PATH: ${.CURDIR}/r0drv
+SRCS += \
+ alloc-r0drv.c \
+ alloc-ef-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 \
+ threadctxhooks-r0drv-generic.c \
+ RTMpIsCpuWorkPending-r0drv-generic.c
+
+.PATH: ${.CURDIR}/VBox
+SRCS += \
+ log-vbox.c \
+ RTLogWriteVmm-amd64-x86.c
+
+.include <bsd.kmod.mk>
+
diff --git a/src/VBox/HostDrivers/Support/freebsd/Makefile.kup b/src/VBox/HostDrivers/Support/freebsd/Makefile.kup
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/src/VBox/HostDrivers/Support/freebsd/Makefile.kup
diff --git a/src/VBox/HostDrivers/Support/freebsd/SUPDrv-freebsd.c b/src/VBox/HostDrivers/Support/freebsd/SUPDrv-freebsd.c
new file mode 100644
index 00000000..0a20031d
--- /dev/null
+++ b/src/VBox/HostDrivers/Support/freebsd/SUPDrv-freebsd.c
@@ -0,0 +1,679 @@
+/* $Id: SUPDrv-freebsd.c $ */
+/** @file
+ * VBoxDrv - The VirtualBox Support Driver - FreeBSD specifics.
+ */
+
+/*
+ * Copyright (c) 2007 knut st. osmundsen <bird-src-spam@anduin.net>
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use,
+ * copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following
+ * conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+ * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#define LOG_GROUP LOG_GROUP_SUP_DRV
+/* Deal with conflicts first. */
+#include <sys/param.h>
+#undef PVM
+#include <sys/types.h>
+#include <sys/module.h>
+#include <sys/systm.h>
+#include <sys/errno.h>
+#include <sys/kernel.h>
+#include <sys/fcntl.h>
+#include <sys/conf.h>
+#include <sys/uio.h>
+#include <vm/pmap.h> /* for pmap_map() */
+
+#include "../SUPDrvInternal.h"
+#include <VBox/version.h>
+#include <iprt/initterm.h>
+#include <iprt/string.h>
+#include <iprt/spinlock.h>
+#include <iprt/process.h>
+#include <iprt/assert.h>
+#include <iprt/uuid.h>
+#include <VBox/log.h>
+#include <iprt/alloc.h>
+#include <iprt/err.h>
+#include <iprt/asm.h>
+
+#ifdef VBOX_WITH_HARDENING
+# define VBOXDRV_PERM 0600
+#else
+# define VBOXDRV_PERM 0666
+#endif
+
+
+/*********************************************************************************************************************************
+* Internal Functions *
+*********************************************************************************************************************************/
+static int VBoxDrvFreeBSDModuleEvent(struct module *pMod, int enmEventType, void *pvArg);
+static int VBoxDrvFreeBSDLoad(void);
+static int VBoxDrvFreeBSDUnload(void);
+
+static d_open_t VBoxDrvFreeBSDOpenUsr;
+static d_open_t VBoxDrvFreeBSDOpenSys;
+static void vboxdrvFreeBSDDtr(void *pvData);
+static d_ioctl_t VBoxDrvFreeBSDIOCtl;
+static int VBoxDrvFreeBSDIOCtlSlow(PSUPDRVSESSION pSession, u_long ulCmd, caddr_t pvData, struct thread *pTd);
+
+
+/*********************************************************************************************************************************
+* Global Variables *
+*********************************************************************************************************************************/
+/**
+ * Module info structure used by the kernel.
+ */
+static moduledata_t g_VBoxDrvFreeBSDModule =
+{
+ "vboxdrv",
+ VBoxDrvFreeBSDModuleEvent,
+ NULL
+};
+
+/** Declare the module as a pseudo device. */
+DECLARE_MODULE(vboxdrv, g_VBoxDrvFreeBSDModule, SI_SUB_PSEUDO, SI_ORDER_ANY);
+MODULE_VERSION(vboxdrv, 1);
+
+/**
+ * The /dev/vboxdrv character device entry points.
+ */
+static struct cdevsw g_VBoxDrvFreeBSDChrDevSwSys =
+{
+ .d_version = D_VERSION,
+ .d_open = VBoxDrvFreeBSDOpenSys,
+ .d_ioctl = VBoxDrvFreeBSDIOCtl,
+ .d_name = "vboxdrv"
+};
+/** The /dev/vboxdrv character device. */
+static struct cdev *g_pVBoxDrvFreeBSDChrDevSys;
+
+/**
+ * The /dev/vboxdrvu character device entry points.
+ */
+static struct cdevsw g_VBoxDrvFreeBSDChrDevSwUsr =
+{
+ .d_version = D_VERSION,
+ .d_open = VBoxDrvFreeBSDOpenUsr,
+ .d_ioctl = VBoxDrvFreeBSDIOCtl,
+ .d_name = "vboxdrvu"
+};
+/** The /dev/vboxdrvu character device. */
+static struct cdev *g_pVBoxDrvFreeBSDChrDevUsr;
+
+/** Reference counter. */
+static volatile uint32_t g_cUsers;
+
+/** The device extention. */
+static SUPDRVDEVEXT g_VBoxDrvFreeBSDDevExt;
+
+/**
+ * Module event handler.
+ *
+ * @param pMod The module structure.
+ * @param enmEventType The event type (modeventtype_t).
+ * @param pvArg Module argument. NULL.
+ *
+ * @return 0 on success, errno.h status code on failure.
+ */
+static int VBoxDrvFreeBSDModuleEvent(struct module *pMod, int enmEventType, void *pvArg)
+{
+ int rc;
+ switch (enmEventType)
+ {
+ case MOD_LOAD:
+ rc = VBoxDrvFreeBSDLoad();
+ break;
+
+ case MOD_UNLOAD:
+ mtx_unlock(&Giant);
+ rc = VBoxDrvFreeBSDUnload();
+ mtx_lock(&Giant);
+ break;
+
+ case MOD_SHUTDOWN:
+ case MOD_QUIESCE:
+ default:
+ return EOPNOTSUPP;
+ }
+
+ if (RT_SUCCESS(rc))
+ return 0;
+ return RTErrConvertToErrno(rc);
+}
+
+
+static int VBoxDrvFreeBSDLoad(void)
+{
+ g_cUsers = 0;
+
+ /*
+ * Initialize the runtime.
+ */
+ int rc = RTR0Init(0);
+ if (RT_SUCCESS(rc))
+ {
+ Log(("VBoxDrvFreeBSDLoad:\n"));
+
+ /*
+ * Initialize the device extension.
+ */
+ rc = supdrvInitDevExt(&g_VBoxDrvFreeBSDDevExt, sizeof(SUPDRVSESSION));
+ if (RT_SUCCESS(rc))
+ {
+ /*
+ * Configure character devices. Add symbolic links for compatibility.
+ */
+ g_pVBoxDrvFreeBSDChrDevSys = make_dev(&g_VBoxDrvFreeBSDChrDevSwSys, 0, UID_ROOT, GID_WHEEL, VBOXDRV_PERM, "vboxdrv");
+ g_pVBoxDrvFreeBSDChrDevUsr = make_dev(&g_VBoxDrvFreeBSDChrDevSwUsr, 1, UID_ROOT, GID_WHEEL, 0666, "vboxdrvu");
+ return VINF_SUCCESS;
+ }
+
+ printf("vboxdrv: supdrvInitDevExt failed, rc=%d\n", rc);
+ RTR0Term();
+ }
+ else
+ printf("vboxdrv: RTR0Init failed, rc=%d\n", rc);
+ return rc;
+}
+
+static int VBoxDrvFreeBSDUnload(void)
+{
+ Log(("VBoxDrvFreeBSDUnload:\n"));
+
+ if (g_cUsers > 0)
+ return VERR_RESOURCE_BUSY;
+
+ /*
+ * Reserve what we did in VBoxDrvFreeBSDInit.
+ */
+ destroy_dev(g_pVBoxDrvFreeBSDChrDevUsr);
+ destroy_dev(g_pVBoxDrvFreeBSDChrDevSys);
+
+ supdrvDeleteDevExt(&g_VBoxDrvFreeBSDDevExt);
+
+ RTR0TermForced();
+
+ memset(&g_VBoxDrvFreeBSDDevExt, 0, sizeof(g_VBoxDrvFreeBSDDevExt));
+ return VINF_SUCCESS;
+}
+
+
+/**
+ *
+ * @returns 0 on success, errno on failure.
+ * EBUSY if the device is used by someone else.
+ * @param pDev The device node.
+ * @param fOpen The open flags.
+ * @param iDevType Some device type thing we don't use.
+ * @param pTd The thread.
+ * @param fUnrestricted Set if opening /dev/vboxdrv, clear if /dev/vboxdrvu.
+ */
+static int vboxdrvFreeBSDOpenCommon(struct cdev *pDev, int fOpen, int iDevType, struct thread *pTd, bool fUnrestricted)
+{
+ PSUPDRVSESSION pSession;
+ int rc;
+
+ /*
+ * Let's be a bit picky about the flags...
+ */
+ if (fOpen != (FREAD | FWRITE /*=O_RDWR*/))
+ {
+ Log(("VBoxDrvFreeBSDOpen: fOpen=%#x expected %#x\n", fOpen, O_RDWR));
+ return EINVAL;
+ }
+
+ /*
+ * Create a new session.
+ */
+ rc = supdrvCreateSession(&g_VBoxDrvFreeBSDDevExt, true /* fUser */, fUnrestricted, &pSession);
+ if (RT_SUCCESS(rc))
+ {
+ /** @todo get (r)uid and (r)gid.
+ pSession->Uid = stuff;
+ pSession->Gid = stuff; */
+ rc = devfs_set_cdevpriv(pSession, vboxdrvFreeBSDDtr); Assert(rc == 0);
+ Log(("VBoxDrvFreeBSDOpen: pSession=%p\n", pSession));
+ ASMAtomicIncU32(&g_cUsers);
+ return 0;
+ }
+
+ return RTErrConvertToErrno(rc);
+}
+
+
+/** For vboxdrv. */
+static int VBoxDrvFreeBSDOpenSys(struct cdev *pDev, int fOpen, int iDevType, struct thread *pTd)
+{
+ return vboxdrvFreeBSDOpenCommon(pDev, fOpen, iDevType, pTd, true);
+}
+
+
+/** For vboxdrvu. */
+static int VBoxDrvFreeBSDOpenUsr(struct cdev *pDev, int fOpen, int iDevType, struct thread *pTd)
+{
+ return vboxdrvFreeBSDOpenCommon(pDev, fOpen, iDevType, pTd, false);
+}
+
+
+/**
+ * Close a file device previously opened by VBoxDrvFreeBSDOpen.
+ *
+ * @param pvData The session being closed.
+ */
+static void vboxdrvFreeBSDDtr(void *pvData)
+{
+ PSUPDRVSESSION pSession = pvData;
+ Log(("vboxdrvFreeBSDDtr: pSession=%p\n", pSession));
+
+ /*
+ * Close the session.
+ */
+ supdrvSessionRelease(pSession);
+ ASMAtomicDecU32(&g_cUsers);
+}
+
+
+/**
+ * I/O control request.
+ *
+ * @returns depends...
+ * @param pDev The device.
+ * @param ulCmd The command.
+ * @param pvData Pointer to the data.
+ * @param fFile The file descriptor flags.
+ * @param pTd The calling thread.
+ */
+static int VBoxDrvFreeBSDIOCtl(struct cdev *pDev, u_long ulCmd, caddr_t pvData, int fFile, struct thread *pTd)
+{
+ PSUPDRVSESSION pSession;
+ devfs_get_cdevpriv((void **)&pSession);
+
+ /*
+ * Deal with the fast ioctl path first.
+ */
+ AssertCompile((SUP_IOCTL_FAST_DO_FIRST & 0xff) == (SUP_IOCTL_FLAG | 64));
+ if ( (uintptr_t)(ulCmd - SUP_IOCTL_FAST_DO_FIRST) < (uintptr_t)32
+ && pSession->fUnrestricted)
+ return supdrvIOCtlFast(ulCmd - SUP_IOCTL_FAST_DO_FIRST, *(uint32_t *)pvData, &g_VBoxDrvFreeBSDDevExt, pSession);
+
+ return VBoxDrvFreeBSDIOCtlSlow(pSession, ulCmd, pvData, pTd);
+}
+
+
+/**
+ * Deal with the 'slow' I/O control requests.
+ *
+ * @returns 0 on success, appropriate errno on failure.
+ * @param pSession The session.
+ * @param ulCmd The command.
+ * @param pvData The request data.
+ * @param pTd The calling thread.
+ */
+static int VBoxDrvFreeBSDIOCtlSlow(PSUPDRVSESSION pSession, u_long ulCmd, caddr_t pvData, struct thread *pTd)
+{
+ PSUPREQHDR pHdr;
+ uint32_t cbReq = IOCPARM_LEN(ulCmd);
+ void *pvUser = NULL;
+
+ /*
+ * Buffered request?
+ */
+ if ((IOC_DIRMASK & ulCmd) == IOC_INOUT)
+ {
+ pHdr = (PSUPREQHDR)pvData;
+ if (RT_UNLIKELY(cbReq < sizeof(*pHdr)))
+ {
+ OSDBGPRINT(("VBoxDrvFreeBSDIOCtlSlow: cbReq=%#x < %#x; ulCmd=%#lx\n", cbReq, (int)sizeof(*pHdr), ulCmd));
+ return EINVAL;
+ }
+ if (RT_UNLIKELY((pHdr->fFlags & SUPREQHDR_FLAGS_MAGIC_MASK) != SUPREQHDR_FLAGS_MAGIC))
+ {
+ OSDBGPRINT(("VBoxDrvFreeBSDIOCtlSlow: bad magic fFlags=%#x; ulCmd=%#lx\n", pHdr->fFlags, ulCmd));
+ return EINVAL;
+ }
+ if (RT_UNLIKELY( RT_MAX(pHdr->cbIn, pHdr->cbOut) != cbReq
+ || pHdr->cbIn < sizeof(*pHdr)
+ || pHdr->cbOut < sizeof(*pHdr)))
+ {
+ OSDBGPRINT(("VBoxDrvFreeBSDIOCtlSlow: max(%#x,%#x) != %#x; ulCmd=%#lx\n", pHdr->cbIn, pHdr->cbOut, cbReq, ulCmd));
+ return EINVAL;
+ }
+ }
+ /*
+ * Big unbuffered request?
+ */
+ else if ((IOC_DIRMASK & ulCmd) == IOC_VOID && !cbReq)
+ {
+ /*
+ * Read the header, validate it and figure out how much that needs to be buffered.
+ */
+ SUPREQHDR Hdr;
+ pvUser = *(void **)pvData;
+ int rc = copyin(pvUser, &Hdr, sizeof(Hdr));
+ if (RT_UNLIKELY(rc))
+ {
+ OSDBGPRINT(("VBoxDrvFreeBSDIOCtlSlow: copyin(%p,Hdr,) -> %#x; ulCmd=%#lx\n", pvUser, rc, ulCmd));
+ return rc;
+ }
+ if (RT_UNLIKELY((Hdr.fFlags & SUPREQHDR_FLAGS_MAGIC_MASK) != SUPREQHDR_FLAGS_MAGIC))
+ {
+ OSDBGPRINT(("VBoxDrvFreeBSDIOCtlSlow: bad magic fFlags=%#x; ulCmd=%#lx\n", Hdr.fFlags, ulCmd));
+ return EINVAL;
+ }
+ cbReq = RT_MAX(Hdr.cbIn, Hdr.cbOut);
+ if (RT_UNLIKELY( Hdr.cbIn < sizeof(Hdr)
+ || Hdr.cbOut < sizeof(Hdr)
+ || cbReq > _1M*16))
+ {
+ OSDBGPRINT(("VBoxDrvFreeBSDIOCtlSlow: max(%#x,%#x); ulCmd=%#lx\n", Hdr.cbIn, Hdr.cbOut, ulCmd));
+ return EINVAL;
+ }
+
+ /*
+ * Allocate buffer and copy in the data.
+ */
+ pHdr = (PSUPREQHDR)RTMemTmpAlloc(cbReq);
+ if (RT_UNLIKELY(!pHdr))
+ {
+ OSDBGPRINT(("VBoxDrvFreeBSDIOCtlSlow: failed to allocate buffer of %d bytes; ulCmd=%#lx\n", cbReq, ulCmd));
+ return ENOMEM;
+ }
+ rc = copyin(pvUser, pHdr, Hdr.cbIn);
+ if (RT_UNLIKELY(rc))
+ {
+ OSDBGPRINT(("VBoxDrvFreeBSDIOCtlSlow: copyin(%p,%p,%#x) -> %#x; ulCmd=%#lx\n",
+ pvUser, pHdr, Hdr.cbIn, rc, ulCmd));
+ RTMemTmpFree(pHdr);
+ return rc;
+ }
+ if (Hdr.cbIn < cbReq)
+ RT_BZERO((uint8_t *)pHdr + Hdr.cbIn, cbReq - Hdr.cbIn);
+ }
+ else
+ {
+ Log(("VBoxDrvFreeBSDIOCtlSlow: huh? cbReq=%#x ulCmd=%#lx\n", cbReq, ulCmd));
+ return EINVAL;
+ }
+
+ /*
+ * Process the IOCtl.
+ */
+ int rc = supdrvIOCtl(ulCmd, &g_VBoxDrvFreeBSDDevExt, 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)
+ {
+ OSDBGPRINT(("VBoxDrvFreeBSDIOCtlSlow: too much output! %#x > %#x; uCmd=%#lx!\n", cbOut, cbReq, ulCmd));
+ cbOut = cbReq;
+ }
+ rc = copyout(pHdr, pvUser, cbOut);
+ if (RT_UNLIKELY(rc))
+ OSDBGPRINT(("VBoxDrvFreeBSDIOCtlSlow: copyout(%p,%p,%#x) -> %d; uCmd=%#lx!\n", pHdr, pvUser, cbOut, rc, ulCmd));
+
+ Log(("VBoxDrvFreeBSDIOCtlSlow: 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(("VBoxDrvFreeBSDIOCtlSlow: ulCmd=%lx pData=%p failed, rc=%d\n", ulCmd, pvData, rc));
+ rc = EINVAL;
+ }
+
+ return rc;
+}
+
+
+/**
+ * The SUPDRV IDC entry point.
+ *
+ * @returns VBox status code, see supdrvIDC.
+ * @param uReq The request code.
+ * @param pReq The request.
+ */
+int VBOXCALL SUPDrvFreeBSDIDC(uint32_t uReq, PSUPDRVIDCREQHDR pReq)
+{
+ PSUPDRVSESSION pSession;
+
+ /*
+ * Some quick validations.
+ */
+ if (RT_UNLIKELY(!RT_VALID_PTR(pReq)))
+ return VERR_INVALID_POINTER;
+
+ pSession = pReq->pSession;
+ if (pSession)
+ {
+ if (RT_UNLIKELY(!RT_VALID_PTR(pReq->pSession)))
+ return VERR_INVALID_PARAMETER;
+ if (RT_UNLIKELY(pSession->pDevExt != &g_VBoxDrvFreeBSDDevExt))
+ return VERR_INVALID_PARAMETER;
+ }
+ else if (RT_UNLIKELY(uReq != SUPDRV_IDC_REQ_CONNECT))
+ return VERR_INVALID_PARAMETER;
+
+ /*
+ * Do the job.
+ */
+ return supdrvIDC(uReq, &g_VBoxDrvFreeBSDDevExt, pSession, pReq);
+}
+
+
+void VBOXCALL supdrvOSCleanupSession(PSUPDRVDEVEXT pDevExt, PSUPDRVSESSION pSession)
+{
+ NOREF(pDevExt);
+ NOREF(pSession);
+}
+
+
+void VBOXCALL supdrvOSSessionHashTabInserted(PSUPDRVDEVEXT pDevExt, PSUPDRVSESSION pSession, void *pvUser)
+{
+ NOREF(pDevExt); NOREF(pSession); NOREF(pvUser);
+}
+
+
+void VBOXCALL supdrvOSSessionHashTabRemoved(PSUPDRVDEVEXT pDevExt, PSUPDRVSESSION pSession, void *pvUser)
+{
+ NOREF(pDevExt); NOREF(pSession); NOREF(pvUser);
+}
+
+
+void VBOXCALL supdrvOSObjInitCreator(PSUPDRVOBJ pObj, PSUPDRVSESSION pSession)
+{
+ NOREF(pObj);
+ NOREF(pSession);
+}
+
+
+bool VBOXCALL supdrvOSObjCanAccess(PSUPDRVOBJ pObj, PSUPDRVSESSION pSession, const char *pszObjName, int *prc)
+{
+ NOREF(pObj);
+ NOREF(pSession);
+ NOREF(pszObjName);
+ NOREF(prc);
+ return false;
+}
+
+
+bool VBOXCALL supdrvOSGetForcedAsyncTscMode(PSUPDRVDEVEXT pDevExt)
+{
+ return false;
+}
+
+
+bool VBOXCALL supdrvOSAreCpusOfflinedOnSuspend(void)
+{
+ /** @todo verify this. */
+ return false;
+}
+
+
+bool VBOXCALL supdrvOSAreTscDeltasInSync(void)
+{
+ return false;
+}
+
+
+int VBOXCALL supdrvOSLdrOpen(PSUPDRVDEVEXT pDevExt, PSUPDRVLDRIMAGE pImage, const char *pszFilename)
+{
+ NOREF(pDevExt); NOREF(pImage); NOREF(pszFilename);
+ return VERR_NOT_SUPPORTED;
+}
+
+
+int VBOXCALL supdrvOSLdrValidatePointer(PSUPDRVDEVEXT pDevExt, PSUPDRVLDRIMAGE pImage, void *pv,
+ const uint8_t *pbImageBits, const char *pszSymbol)
+{
+ NOREF(pDevExt); NOREF(pImage); NOREF(pv); NOREF(pbImageBits); NOREF(pszSymbol);
+ return VERR_NOT_SUPPORTED;
+}
+
+
+int VBOXCALL supdrvOSLdrLoad(PSUPDRVDEVEXT pDevExt, PSUPDRVLDRIMAGE pImage, const uint8_t *pbImageBits, PSUPLDRLOAD pReq)
+{
+ NOREF(pDevExt); NOREF(pImage); NOREF(pbImageBits); NOREF(pReq);
+ return VERR_NOT_SUPPORTED;
+}
+
+
+void VBOXCALL supdrvOSLdrUnload(PSUPDRVDEVEXT pDevExt, PSUPDRVLDRIMAGE pImage)
+{
+ NOREF(pDevExt); NOREF(pImage);
+}
+
+
+void VBOXCALL supdrvOSLdrNotifyOpened(PSUPDRVDEVEXT pDevExt, PSUPDRVLDRIMAGE pImage, const char *pszFilename)
+{
+ NOREF(pDevExt); NOREF(pImage); NOREF(pszFilename);
+}
+
+
+void VBOXCALL supdrvOSLdrNotifyUnloaded(PSUPDRVDEVEXT pDevExt, PSUPDRVLDRIMAGE pImage)
+{
+ NOREF(pDevExt); NOREF(pImage);
+}
+
+
+int VBOXCALL supdrvOSLdrQuerySymbol(PSUPDRVDEVEXT pDevExt, PSUPDRVLDRIMAGE pImage,
+ const char *pszSymbol, size_t cchSymbol, void **ppvSymbol)
+{
+ RT_NOREF(pDevExt, pImage, pszSymbol, cchSymbol, ppvSymbol);
+ return VERR_WRONG_ORDER;
+}
+
+
+void VBOXCALL supdrvOSLdrRetainWrapperModule(PSUPDRVDEVEXT pDevExt, PSUPDRVLDRIMAGE pImage)
+{
+ RT_NOREF(pDevExt, pImage);
+ AssertFailed();
+}
+
+
+void VBOXCALL supdrvOSLdrReleaseWrapperModule(PSUPDRVDEVEXT pDevExt, PSUPDRVLDRIMAGE pImage)
+{
+ RT_NOREF(pDevExt, pImage);
+ AssertFailed();
+}
+
+#ifdef SUPDRV_WITH_MSR_PROBER
+
+int VBOXCALL supdrvOSMsrProberRead(uint32_t uMsr, RTCPUID idCpu, uint64_t *puValue)
+{
+ NOREF(uMsr); NOREF(idCpu); NOREF(puValue);
+ return VERR_NOT_SUPPORTED;
+}
+
+
+int VBOXCALL supdrvOSMsrProberWrite(uint32_t uMsr, RTCPUID idCpu, uint64_t uValue)
+{
+ NOREF(uMsr); NOREF(idCpu); NOREF(uValue);
+ return VERR_NOT_SUPPORTED;
+}
+
+
+int VBOXCALL supdrvOSMsrProberModify(RTCPUID idCpu, PSUPMSRPROBER pReq)
+{
+ NOREF(idCpu); NOREF(pReq);
+ return VERR_NOT_SUPPORTED;
+}
+
+#endif /* SUPDRV_WITH_MSR_PROBER */
+
+
+#if defined(RT_ARCH_AMD64) || defined(RT_ARCH_ARM64)
+SUPR0DECL(int) SUPR0HCPhysToVirt(RTHCPHYS HCPhys, void **ppv)
+{
+ AssertReturn(!(HCPhys & PAGE_OFFSET_MASK), VERR_INVALID_POINTER);
+ AssertReturn(HCPhys != NIL_RTHCPHYS, VERR_INVALID_POINTER);
+ *ppv = (void *)(uintptr_t)pmap_map(NULL, HCPhys, (HCPhys | PAGE_OFFSET_MASK) + 1, VM_PROT_WRITE | VM_PROT_READ);
+ return VINF_SUCCESS;
+}
+#endif
+
+
+SUPR0DECL(int) SUPR0PrintfV(const char *pszFormat, va_list va)
+{
+ char szMsg[256];
+ RTStrPrintfV(szMsg, sizeof(szMsg), pszFormat, va);
+ szMsg[sizeof(szMsg) - 1] = '\0';
+
+ printf("%s", szMsg);
+ return 0;
+}
+
+
+SUPR0DECL(uint32_t) SUPR0GetKernelFeatures(void)
+{
+ return 0;
+}
+
+
+SUPR0DECL(bool) SUPR0FpuBegin(bool fCtxHook)
+{
+ RT_NOREF(fCtxHook);
+ return false;
+}
+
+
+SUPR0DECL(void) SUPR0FpuEnd(bool fCtxHook)
+{
+ RT_NOREF(fCtxHook);
+}
+
diff --git a/src/VBox/HostDrivers/Support/freebsd/SUPDrv-freebsd.def b/src/VBox/HostDrivers/Support/freebsd/SUPDrv-freebsd.def
new file mode 100644
index 00000000..f6e776e5
--- /dev/null
+++ b/src/VBox/HostDrivers/Support/freebsd/SUPDrv-freebsd.def
@@ -0,0 +1,2 @@
+SUPDrvLinuxIDC
+
diff --git a/src/VBox/HostDrivers/Support/freebsd/SUPLib-freebsd.cpp b/src/VBox/HostDrivers/Support/freebsd/SUPLib-freebsd.cpp
new file mode 100644
index 00000000..cfd5244f
--- /dev/null
+++ b/src/VBox/HostDrivers/Support/freebsd/SUPLib-freebsd.cpp
@@ -0,0 +1,197 @@
+/* $Id: SUPLib-freebsd.cpp $ */
+/** @file
+ * VirtualBox Support Library - FreeBSD specific parts.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#define LOG_GROUP LOG_GROUP_SUP
+#ifdef IN_SUP_HARDENED_R3
+# undef DEBUG /* Warning: disables RT_STRICT */
+# define LOG_DISABLED
+# define RTLOG_REL_DISABLED
+# include <iprt/log.h>
+#endif
+
+#include <VBox/types.h>
+#include <VBox/sup.h>
+#include <VBox/param.h>
+#include <VBox/err.h>
+#include <VBox/log.h>
+#include <iprt/path.h>
+#include <iprt/assert.h>
+#include <iprt/mem.h>
+#include <iprt/err.h>
+#include <iprt/string.h>
+#include "../SUPLibInternal.h"
+#include "../SUPDrvIOC.h"
+
+#include <sys/fcntl.h>
+#include <sys/ioctl.h>
+#include <errno.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <stdio.h>
+
+
+/*********************************************************************************************************************************
+* Defined Constants And Macros *
+*********************************************************************************************************************************/
+/** System device name. */
+#define DEVICE_NAME_SYS "/dev/vboxdrv"
+/** User device name. */
+#define DEVICE_NAME_USR "/dev/vboxdrvu"
+
+
+
+DECLHIDDEN(int) suplibOsInit(PSUPLIBDATA pThis, bool fPreInited, uint32_t fFlags, SUPINITOP *penmWhat, PRTERRINFO pErrInfo)
+{
+ /*
+ * Nothing to do if pre-inited.
+ */
+ if (fPreInited)
+ return VINF_SUCCESS;
+
+ /*
+ * Try open the BSD device.
+ */
+ const char * const *pszDeviceNm = fFlags & SUPR3INIT_F_UNRESTRICTED ? DEVICE_NAME_SYS : DEVICE_NAME_USR;
+ int hDevice = open(pszDeviceNm, O_RDWR, 0);
+ if (hDevice < 0)
+ {
+ int rc;
+ switch (errno)
+ {
+ case ENODEV: rc = VERR_VM_DRIVER_LOAD_ERROR; break;
+ case EPERM:
+ case EACCES: rc = VERR_VM_DRIVER_NOT_ACCESSIBLE; break;
+ case ENOENT: rc = VERR_VM_DRIVER_NOT_INSTALLED; break;
+ default: rc = VERR_VM_DRIVER_OPEN_ERROR; break;
+ }
+ LogRel(("Failed to open \"%s\", errno=%d, rc=%Rrc\n", pszDeviceNm, errno, rc));
+ return rc;
+ }
+
+ /*
+ * Mark the file handle close on exec.
+ */
+ if (fcntl(hDevice, F_SETFD, FD_CLOEXEC) != 0)
+ {
+#ifdef IN_SUP_HARDENED_R3
+ int rc = VERR_INTERNAL_ERROR;
+#else
+ int err = errno;
+ int rc = RTErrConvertFromErrno(err);
+ LogRel(("suplibOSInit: setting FD_CLOEXEC failed, errno=%d (%Rrc)\n", err, rc));
+#endif
+ close(hDevice);
+ return rc;
+ }
+
+ /*
+ * We're done.
+ */
+ pThis->hDevice = hDevice;
+ pThis->fUnrestricted = RT_BOOL(fFlags & SUPR3INIT_F_UNRESTRICTED);
+ return VINF_SUCCESS;
+}
+
+
+DECLHIDDEN(int) suplibOsTerm(PSUPLIBDATA pThis)
+{
+ /*
+ * Check if we're inited at all.
+ */
+ if (pThis->hDevice != (intptr_t)NIL_RTFILE)
+ {
+ if (close(pThis->hDevice))
+ AssertFailed();
+ pThis->hDevice = (intptr_t)NIL_RTFILE;
+ }
+ return VINF_SUCCESS;
+}
+
+
+#ifndef IN_SUP_HARDENED_R3
+
+DECLHIDDEN(int) suplibOsInstall(void)
+{
+ return VERR_NOT_IMPLEMENTED;
+}
+
+
+DECLHIDDEN(int) suplibOsUninstall(void)
+{
+ return VERR_NOT_IMPLEMENTED;
+}
+
+
+DECLHIDDEN(int) suplibOsIOCtl(PSUPLIBDATA pThis, uintptr_t uFunction, void *pvReq, size_t cbReq)
+{
+ if (RT_LIKELY(ioctl(pThis->hDevice, uFunction, pvReq) >= 0))
+ return VINF_SUCCESS;
+ return RTErrConvertFromErrno(errno);
+}
+
+
+DECLHIDDEN(int) suplibOsIOCtlFast(PSUPLIBDATA pThis, uintptr_t uFunction, uintptr_t idCpu)
+{
+ int rc = ioctl(pThis->hDevice, uFunction, idCpu);
+ if (rc == -1)
+ rc = errno;
+ return rc;
+}
+
+
+DECLHIDDEN(int) suplibOsPageAlloc(PSUPLIBDATA pThis, size_t cPages, uint32_t fFlags, void **ppvPages)
+{
+ RT_NOREF(pThis, fFlags);
+ *ppvPages = RTMemPageAllocZ(cPages << PAGE_SHIFT);
+ if (*ppvPages)
+ return VINF_SUCCESS;
+ return RTErrConvertFromErrno(errno);
+}
+
+
+DECLHIDDEN(int) suplibOsPageFree(PSUPLIBDATA pThis, void *pvPages, size_t cPages)
+{
+ NOREF(pThis);
+ RTMemPageFree(pvPages, cPages * PAGE_SIZE);
+ return VINF_SUCCESS;
+}
+
+#endif /* !IN_SUP_HARDENED_R3 */
+
diff --git a/src/VBox/HostDrivers/Support/freebsd/SUPR0IdcClient-freebsd.c b/src/VBox/HostDrivers/Support/freebsd/SUPR0IdcClient-freebsd.c
new file mode 100644
index 00000000..c251c2e2
--- /dev/null
+++ b/src/VBox/HostDrivers/Support/freebsd/SUPR0IdcClient-freebsd.c
@@ -0,0 +1,66 @@
+/* $Id: SUPR0IdcClient-freebsd.c $ */
+/** @file
+ * VirtualBox Support Driver - IDC Client Lib, FreeBSD Specific Code.
+ */
+
+/*
+ * Copyright (C) 2008-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include "../SUPR0IdcClientInternal.h"
+#include <iprt/errcore.h>
+
+
+int VBOXCALL supR0IdcNativeOpen(PSUPDRVIDCHANDLE pHandle, PSUPDRVIDCREQCONNECT pReq)
+{
+ return supR0IdcNativeCall(pHandle, SUPDRV_IDC_REQ_CONNECT, &pReq->Hdr);
+}
+
+
+int VBOXCALL supR0IdcNativeClose(PSUPDRVIDCHANDLE pHandle, PSUPDRVIDCREQHDR pReq)
+{
+ return supR0IdcNativeCall(pHandle, SUPDRV_IDC_REQ_DISCONNECT, pReq);
+}
+
+
+int VBOXCALL supR0IdcNativeCall(PSUPDRVIDCHANDLE pHandle, uint32_t iReq, PSUPDRVIDCREQHDR pReq)
+{
+ int rc = SUPDrvFreeBSDIDC(iReq, pReq);
+ if (RT_SUCCESS(rc))
+ rc = pReq->rc;
+
+ NOREF(pHandle);
+ return rc;
+}
+
diff --git a/src/VBox/HostDrivers/Support/freebsd/files_vboxdrv b/src/VBox/HostDrivers/Support/freebsd/files_vboxdrv
new file mode 100755
index 00000000..0da2f2e6
--- /dev/null
+++ b/src/VBox/HostDrivers/Support/freebsd/files_vboxdrv
@@ -0,0 +1,240 @@
+#!/bin/sh
+# $Id: files_vboxdrv $
+## @file
+# Shared file between Makefile.kmk and export_modules.sh.
+#
+
+#
+# Copyright (C) 2007-2023 Oracle and/or its affiliates.
+#
+# This file is part of VirtualBox base platform packages, as
+# available from https://www.virtualbox.org.
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation, in version 3 of the
+# License.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, see <https://www.gnu.org/licenses>.
+#
+# The contents of this file may alternatively be used under the terms
+# of the Common Development and Distribution License Version 1.0
+# (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+# in the VirtualBox distribution, in which case the provisions of the
+# CDDL are applicable instead of those of the GPL.
+#
+# You may elect to license modified versions of this file under the
+# terms and conditions of either the GPL or the CDDL or both.
+#
+# SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+#
+
+FILES_VBOXDRV_NOBIN=" \
+ ${PATH_ROOT}/include/iprt/alloc.h=>include/iprt/alloc.h \
+ ${PATH_ROOT}/include/iprt/alloca.h=>include/iprt/alloca.h \
+ ${PATH_ROOT}/include/iprt/asm.h=>include/iprt/asm.h \
+ ${PATH_ROOT}/include/iprt/asm-amd64-x86.h=>include/iprt/asm-amd64-x86.h \
+ ${PATH_ROOT}/include/iprt/asm-math.h=>include/iprt/asm-math.h \
+ ${PATH_ROOT}/include/iprt/assert.h=>include/iprt/assert.h \
+ ${PATH_ROOT}/include/iprt/assertcompile.h=>include/iprt/assertcompile.h \
+ ${PATH_ROOT}/include/iprt/avl.h=>include/iprt/avl.h \
+ ${PATH_ROOT}/include/iprt/cdefs.h=>include/iprt/cdefs.h \
+ ${PATH_ROOT}/include/iprt/cpuset.h=>include/iprt/cpuset.h \
+ ${PATH_ROOT}/include/iprt/crc.h=>include/iprt/crc.h \
+ ${PATH_ROOT}/include/iprt/ctype.h=>include/iprt/ctype.h \
+ ${PATH_ROOT}/include/iprt/err.h=>include/iprt/err.h \
+ ${PATH_ROOT}/include/iprt/errcore.h=>include/iprt/errcore.h \
+ ${PATH_ROOT}/include/iprt/errno.h=>include/iprt/errno.h \
+ ${PATH_ROOT}/include/iprt/heap.h=>include/iprt/heap.h \
+ ${PATH_ROOT}/include/iprt/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/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/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/uint128.h=>include/iprt/uint128.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/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/lockvalidator.h=>include/iprt/lockvalidator.h \
+ ${PATH_ROOT}/include/iprt/x86.h=>include/iprt/x86.h \
+ ${PATH_ROOT}/include/iprt/x86-helpers.h=>include/iprt/x86-helpers.h \
+ ${PATH_ROOT}/include/iprt/nocrt/limits.h=>include/iprt/nocrt/limits.h \
+ ${PATH_ROOT}/include/VBox/cdefs.h=>include/VBox/cdefs.h \
+ ${PATH_ROOT}/include/VBox/err.h=>include/VBox/err.h \
+ ${PATH_ROOT}/include/VBox/log.h=>include/VBox/log.h \
+ ${PATH_ROOT}/include/VBox/param.h=>include/VBox/param.h \
+ ${PATH_ROOT}/include/VBox/sup.h=>include/VBox/sup.h \
+ ${PATH_ROOT}/include/VBox/types.h=>include/VBox/types.h \
+ ${PATH_ROOT}/include/VBox/version.h=>include/VBox/version.h \
+ ${PATH_ROOT}/include/VBox/SUPDrvMangling.h=>include/VBox/SUPDrvMangling.h \
+ ${PATH_ROOT}/include/VBox/VBoxTpG.h=>include/VBox/VBoxTpG.h \
+ ${PATH_ROOT}/include/VBox/vmm/hm_vmx.h=>include/VBox/vmm/hm_vmx.h \
+ ${PATH_ROOT}/include/VBox/vmm/hm_svm.h=>include/VBox/vmm/hm_svm.h \
+ ${PATH_ROOT}/include/VBox/vmm/cpuidcall.h=>include/VBox/vmm/cpuidcall.h \
+ ${PATH_ROOT}/src/VBox/HostDrivers/Support/freebsd/SUPDrv-freebsd.c=>freebsd/SUPDrv-freebsd.c \
+ ${PATH_ROOT}/src/VBox/HostDrivers/Support/SUPDrv.cpp=>SUPDrv.c \
+ ${PATH_ROOT}/src/VBox/HostDrivers/Support/SUPDrvGip.cpp=>SUPDrvGip.c \
+ ${PATH_ROOT}/src/VBox/HostDrivers/Support/SUPDrvSem.cpp=>SUPDrvSem.c \
+ ${PATH_ROOT}/src/VBox/HostDrivers/Support/SUPDrvTracer.cpp=>SUPDrvTracer.c \
+ ${PATH_ROOT}/src/VBox/HostDrivers/Support/SUPDrvIDC.h=>SUPDrvIDC.h \
+ ${PATH_ROOT}/src/VBox/HostDrivers/Support/SUPDrvIOC.h=>SUPDrvIOC.h \
+ ${PATH_ROOT}/src/VBox/HostDrivers/Support/SUPDrvInternal.h=>SUPDrvInternal.h \
+ ${PATH_ROOT}/src/VBox/HostDrivers/Support/SUPLibAll.cpp=>SUPLibAll.c \
+ ${PATH_ROOT}/src/VBox/Runtime/common/alloc/heapsimple.cpp=>alloc/heapsimple.c \
+ ${PATH_ROOT}/src/VBox/Runtime/common/alloc/alloc.cpp=>alloc/alloc.c \
+ ${PATH_ROOT}/src/VBox/Runtime/common/err/RTErrConvertFromErrno.cpp=>common/err/RTErrConvertFromErrno.c \
+ ${PATH_ROOT}/src/VBox/Runtime/common/err/RTErrConvertToErrno.cpp=>common/err/RTErrConvertToErrno.c \
+ ${PATH_ROOT}/src/VBox/Runtime/common/err/errinfo.cpp=>common/err/errinfo.c \
+ ${PATH_ROOT}/src/VBox/Runtime/common/log/log.cpp=>common/log/log.c \
+ ${PATH_ROOT}/src/VBox/Runtime/common/log/logellipsis.cpp=>common/log/logellipsis.c \
+ ${PATH_ROOT}/src/VBox/Runtime/common/log/logrel.cpp=>common/log/logrel.c \
+ ${PATH_ROOT}/src/VBox/Runtime/common/log/logrelellipsis.cpp=>common/log/logrelellipsis.c \
+ ${PATH_ROOT}/src/VBox/Runtime/common/log/logcom.cpp=>common/log/logcom.c \
+ ${PATH_ROOT}/src/VBox/Runtime/common/log/logformat.cpp=>common/log/logformat.c \
+ ${PATH_ROOT}/src/VBox/Runtime/common/log/RTLogCreateEx.cpp=>common/log/RTLogCreateEx.c \
+ ${PATH_ROOT}/src/VBox/Runtime/common/misc/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/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/term.cpp=>common/misc/term.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/RTStrCopyP.cpp=>common/string/RTStrCopyP.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/RTStrEnd.cpp=>common/string/RTStrEnd.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/strformat.cpp=>common/string/strformat.c \
+ ${PATH_ROOT}/src/VBox/Runtime/common/string/RTStrFormat.cpp=>common/string/RTStrFormat.c \
+ ${PATH_ROOT}/src/VBox/Runtime/common/string/strformatnum.cpp=>common/string/strformatnum.c \
+ ${PATH_ROOT}/src/VBox/Runtime/common/string/strformatrt.cpp=>common/string/strformatrt.c \
+ ${PATH_ROOT}/src/VBox/Runtime/common/string/strformattype.cpp=>common/string/strformattype.c \
+ ${PATH_ROOT}/src/VBox/Runtime/common/string/strprintf.cpp=>common/string/strprintf.c \
+ ${PATH_ROOT}/src/VBox/Runtime/common/string/strprintf-ellipsis.cpp=>common/string/strprintf-ellipsis.c \
+ ${PATH_ROOT}/src/VBox/Runtime/common/string/strprintf2.cpp=>common/string/strprintf2.c \
+ ${PATH_ROOT}/src/VBox/Runtime/common/string/strprintf2-ellipsis.cpp=>common/string/strprintf2-ellipsis.c \
+ ${PATH_ROOT}/src/VBox/Runtime/common/string/strtonum.cpp=>common/string/strtonum.c \
+ ${PATH_ROOT}/src/VBox/Runtime/common/string/memchr.cpp=>common/string/memchr.c \
+ ${PATH_ROOT}/src/VBox/Runtime/common/string/stringalloc.cpp=>common/string/stringalloc.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/checksum/ipv6.cpp=>common/checksum/ipv6.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/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/include/internal/time.h=>include/internal/time.h \
+ ${PATH_ROOT}/src/VBox/Runtime/generic/rtStrFormatKernelAddress-generic.cpp=>generic/rtStrFormatKernelAddress-generic.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/RTLogWriteUser-generic.cpp=>generic/RTLogWriteUser-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/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/RTMpGetArraySize-generic.cpp=>generic/RTMpGetArraySize-generic.c \
+ ${PATH_ROOT}/src/VBox/Runtime/generic/RTMpOnPair-generic.cpp=>generic/RTMpOnPair-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/generic/timer-generic.cpp=>generic/timer-generic.c \
+ ${PATH_ROOT}/src/VBox/Runtime/generic/uuid-generic.cpp=>generic/uuid-generic.c \
+ ${PATH_ROOT}/src/VBox/Runtime/r0drv/alloc-r0drv.cpp=>r0drv/alloc-r0drv.c \
+ ${PATH_ROOT}/src/VBox/Runtime/r0drv/alloc-ef-r0drv.cpp=>r0drv/alloc-ef-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/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/threadctxhooks-r0drv-generic.cpp=>r0drv/generic/threadctxhooks-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/RTLogWriteVmm-amd64-x86.cpp=>VBox/RTLogWriteVmm-amd64-x86.c \
+ ${PATH_OUT}/version-generated.h=>version-generated.h \
+ ${PATH_OUT}/product-generated.h=>product-generated.h \
+"
+
+FILES_VBOXDRV_BIN=" \
+"
diff --git a/src/VBox/HostDrivers/Support/linux/LnxPerfHack.cpp b/src/VBox/HostDrivers/Support/linux/LnxPerfHack.cpp
new file mode 100644
index 00000000..056a2da8
--- /dev/null
+++ b/src/VBox/HostDrivers/Support/linux/LnxPerfHack.cpp
@@ -0,0 +1,446 @@
+/* $Id: LnxPerfHack.cpp $ */
+/** @file
+ * LnxPerfHack - Dirty hack to make perf find our .r0 modules.
+ */
+
+/*
+ * Copyright (C) 2021-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include <iprt/assert.h>
+#include <iprt/file.h>
+#include <iprt/initterm.h>
+#include <iprt/getopt.h>
+#include <iprt/ldr.h>
+#include <iprt/sort.h>
+#include <iprt/string.h>
+#include <iprt/stream.h>
+#include <iprt/mem.h>
+#include <iprt/message.h>
+
+
+/*********************************************************************************************************************************
+* Defined Constants And Macros *
+*********************************************************************************************************************************/
+#define LNXPERFILEHDR_MAGIC RT_MAKE_U64_FROM_U8('P','E','R','F','I','L','E','2')
+
+#define LNXPERF_RECORD_MMAP 1
+#define LNXPERF_RECORD_MMAP2 10
+
+#define LNXPERF_RECORD_MISC_CPUMODE_MASK UINT16_C(0x0007)
+#define LNXPERF_RECORD_MISC_CPUMODE_UNKNOWN UINT16_C(0x0000)
+#define LNXPERF_RECORD_MISC_KERNEL UINT16_C(0x0001)
+#define LNXPERF_RECORD_MISC_USER UINT16_C(0x0002)
+#define LNXPERF_RECORD_MISC_HYPERVISOR UINT16_C(0x0003)
+#define LNXPERF_RECORD_MISC_GUEST_KERNEL UINT16_C(0x0004)
+#define LNXPERF_RECORD_MISC_GUEST_USER UINT16_C(0x0005)
+
+
+/*********************************************************************************************************************************
+* Structures and Typedefs *
+*********************************************************************************************************************************/
+/** The file header. */
+typedef struct LNXPERFFILEHDR
+{
+ uint64_t uMagic; /**< LNXPERFILEHDR_MAGIC */
+ uint64_t cbHdr;
+ uint64_t cbAttr;
+ struct LNXPERFFILESECTION
+ {
+ uint64_t off, cb;
+ } Attrs, Data, EventTypes;
+ uint64_t bmAddsFeatures[256/64];
+} LNXPERFFILEHDR;
+typedef LNXPERFFILEHDR *PLNXPERFFILEHDR;
+
+
+typedef struct LNXPERFRECORDHEADER
+{
+ uint32_t uType;
+ uint16_t fMisc;
+ uint16_t cb;
+} LNXPERFRECORDHEADER;
+AssertCompileSize(LNXPERFRECORDHEADER, 8);
+typedef LNXPERFRECORDHEADER *PLNXPERFRECORDHEADER;
+
+typedef struct LNXPERFRECORDMMAP
+{
+ LNXPERFRECORDHEADER Hdr;
+ uint32_t pid;
+ uint32_t tid;
+ uint64_t uAddress;
+ uint64_t cbMapping;
+ uint64_t offFile;
+ RT_FLEXIBLE_ARRAY_EXTENSION
+ char szFilename[RT_FLEXIBLE_ARRAY];
+} LNXPERFRECORDMMAP;
+typedef LNXPERFRECORDMMAP *PLNXPERFRECORDMMAP;
+
+typedef struct MYMODULE
+{
+ uint64_t uAddress;
+ uint64_t cbMapping;
+ uint64_t offFile;
+ const char *pszName;
+ uint32_t cchName;
+ uint16_t cbRecord;
+ uint64_t offRecord;
+} MYMODULE;
+typedef MYMODULE *PMYMODULE;
+
+
+DECLCALLBACK(int) CompModuleRecordOffset(void const *pvElement1, void const *pvElement2, void *pvUser)
+{
+ PMYMODULE pLeft = (PMYMODULE)pvElement1;
+ PMYMODULE pRight = (PMYMODULE)pvElement2;
+ RT_NOREF(pvUser);
+ return pLeft->offRecord < pRight->offRecord ? -1
+ : pLeft->offRecord > pRight->offRecord ? 1 : 0;
+
+}
+
+
+DECLCALLBACK(int) CompModuleNameLengthDesc(void const *pvElement1, void const *pvElement2, void *pvUser)
+{
+ PMYMODULE pLeft = (PMYMODULE)pvElement1;
+ PMYMODULE pRight = (PMYMODULE)pvElement2;
+ RT_NOREF(pvUser);
+ return pLeft->cchName < pRight->cchName ? 1
+ : pLeft->cchName > pRight->cchName ? -1 : 0;
+}
+
+
+/** @callback_method_impl{FNRTLDRENUMSEGS} */
+static DECLCALLBACK(int) SegmentEnumCallback(RTLDRMOD hLdrMod, PCRTLDRSEG pSeg, void *pvUser)
+{
+ RT_NOREF(hLdrMod);
+ if (pSeg->pszName && RTStrStartsWith(pSeg->pszName, ".text"))
+ {
+ PMYMODULE pModEntry = (PMYMODULE)pvUser;
+ //pModEntry->offFile = pModEntry->uAddress;
+ pModEntry->uAddress += pSeg->RVA;
+ pModEntry->cbMapping = pSeg->cbMapped;
+ //pModEntry->offFile = pModEntry->uAddress - pSeg->LinkAddress;
+ pModEntry->offFile = RT_MAX(pSeg->offFile, 0);
+ return VINF_CALLBACK_RETURN;
+ }
+ return VINF_SUCCESS;
+}
+
+
+
+int main(int argc, char **argv)
+{
+ int rc = RTR3InitExe(argc, &argv, 0);
+ if (RT_FAILURE(rc))
+ return RTMsgInitFailure(rc);
+
+ static const RTGETOPTDEF s_aOptions[] =
+ {
+ { "--input", 'i', RTGETOPT_REQ_STRING },
+ { "--output", 'o', RTGETOPT_REQ_STRING },
+ { "--module", 'm', RTGETOPT_REQ_STRING },
+ { "--quiet", 'q', RTGETOPT_REQ_NOTHING },
+ { "--verbose", 'v', RTGETOPT_REQ_NOTHING },
+ };
+ const char *pszInput = NULL;
+ const char *pszOutput = NULL;
+ unsigned cVerbosity = 0;
+ unsigned cModules = 0;
+ MYMODULE aModules[10];
+ unsigned cSkipPatterns = 1;
+ const char *apszSkipPatterns[10] = { "*kallsyms*", };
+
+ int ch;
+ RTGETOPTUNION ValueUnion;
+ RTGETOPTSTATE GetState;
+ rc = RTGetOptInit(&GetState, argc, argv, s_aOptions, RT_ELEMENTS(s_aOptions), 1, 0);
+ AssertRCReturn(rc, RTMsgErrorExitFailure("RTGetOptInit failed: %Rrc", rc));
+ while ((ch = RTGetOpt(&GetState, &ValueUnion)) != 0)
+ {
+ switch (ch)
+ {
+ case 'i':
+ pszInput = ValueUnion.psz;
+ break;
+
+ case 'o':
+ pszOutput = ValueUnion.psz;
+ break;
+
+ case 'm':
+ {
+ if (cModules >= RT_ELEMENTS(aModules))
+ return RTMsgErrorExitFailure("Too many modules (max %u)", RT_ELEMENTS(aModules));
+ aModules[cModules].pszName = ValueUnion.psz;
+ aModules[cModules].cchName = strlen(ValueUnion.psz);
+
+ rc = RTGetOptFetchValue(&GetState, &ValueUnion, RTGETOPT_REQ_UINT64 | RTGETOPT_FLAG_HEX);
+ if (RT_FAILURE(rc))
+ return RTGetOptPrintError(rc, &ValueUnion); /*??*/
+ aModules[cModules].uAddress = ValueUnion.u64;
+
+ /* We need to find the .text section as that's what we'll be creating an mmap record for. */
+ RTERRINFOSTATIC ErrInfo;
+ RTLDRMOD hLdrMod;
+ rc = RTLdrOpenEx(aModules[cModules].pszName, RTLDR_O_FOR_DEBUG, RTLDRARCH_WHATEVER,
+ &hLdrMod, RTErrInfoInitStatic(&ErrInfo));
+ if (RT_FAILURE(rc))
+ {
+ return RTMsgErrorExitFailure("RTLdrOpenEx failed on '%s': %Rrc%#RTeim",
+ aModules[cModules].pszName, rc, &ErrInfo.Core);
+ }
+ rc = RTLdrEnumSegments(hLdrMod, SegmentEnumCallback, &aModules[cModules]);
+ if (rc != VINF_CALLBACK_RETURN)
+ return RTMsgErrorExitFailure("Failed to locate the .text section in '%s'!", aModules[cModules].pszName);
+
+ aModules[cModules].cbRecord = 0;
+ aModules[cModules].offRecord = UINT64_MAX;
+
+ cModules++;
+ break;
+ }
+
+ case 'q':
+ cVerbosity = 0;
+ break;
+
+ case 'v':
+ cVerbosity++;
+ break;
+
+ case 'h':
+ RTPrintf("usage: %s -i <perf.in> -o <perf.out> -m vmmr0.r0 <loadaddress> [-m ..] [-v]\n"
+ "\n"
+ "It is recommended to use eu-unstrip to combine the VMMR0.r0 and\n"
+ "VMMR0.debug files into a single file again.\n"
+ "\n"
+ "For the 'annotation' feature of perf to work, it is necessary to patch\n"
+ "machine__process_kernel_mmap_event() in tools/perf/utils/machine.c, adding"
+ "the following after 'map->end = map->start + ...:\n"
+ "\n"
+ "/* bird: Transfer pgoff to reloc as dso__process_kernel_symbol overwrites\n"
+ " map->pgoff with sh_offset later. Kind of ASSUMES sh_offset == sh_addr. */\n"
+ "if (event->mmap.pgoff && map->dso && !map->dso->rel)\n"
+ " map->reloc = map->start - event->mmap.pgoff;\n"
+ , argv[0]);
+ return RTEXITCODE_SUCCESS;
+
+ default:
+ return RTGetOptPrintError(ch, &ValueUnion);
+ }
+ }
+ if (!pszInput)
+ return RTMsgErrorExit(RTEXITCODE_SYNTAX, "No input file specified");
+ if (!pszOutput)
+ return RTMsgErrorExit(RTEXITCODE_SYNTAX, "No output file specified");
+ if (RTFileExists(pszOutput))
+ return RTMsgErrorExit(RTEXITCODE_FAILURE, "Output file exists: %s", pszOutput);
+
+
+ /*
+ * Open the input file and check the header.
+ */
+ RTFILE hFile;
+ rc = RTFileOpen(&hFile, pszInput, RTFILE_O_READ | RTFILE_O_DENY_NONE | RTFILE_O_OPEN);
+ if (RT_FAILURE(rc))
+ return RTMsgErrorExitFailure("Failed to open '%s': %Rrc", pszInput, rc);
+
+ union
+ {
+ LNXPERFFILEHDR FileHdr;
+ uint8_t ab[_64K]; /* max record size */
+ } u;
+ rc = RTFileRead(hFile, &u.FileHdr, sizeof(u.FileHdr), NULL);
+ if (RT_FAILURE(rc))
+ return RTMsgErrorExitFailure("Error reading file header: %Rrc", rc);
+ if (u.FileHdr.uMagic != LNXPERFILEHDR_MAGIC)
+ return RTMsgErrorExitFailure("Invalid file header magic: %.8Rhxs", &u.FileHdr.uMagic);
+ if (u.FileHdr.cbHdr != sizeof(u.FileHdr))
+ return RTMsgErrorExitFailure("Invalid file header size: %RU64, expected %zu", u.FileHdr.cbHdr, sizeof(u.FileHdr));
+ uint64_t const offData = u.FileHdr.Data.off;
+ uint64_t const cbData = u.FileHdr.Data.cb;
+
+ /*
+ * Jump to the data portion and look for suitable kmod mmap
+ * records to replace.
+ *
+ * We sort the modules in descreasing name length first to make sure
+ * not to waste voluminous records on short replacement names.
+ */
+ RTSortShell(aModules, cModules, sizeof(aModules[0]), CompModuleNameLengthDesc, NULL);
+
+ unsigned cModulesLeft = cModules ? cModules : cVerbosity > 0;
+ uint64_t offRecord = 0;
+ while (offRecord + 32 < cbData && cModulesLeft > 0)
+ {
+ size_t cbToRead = (size_t)RT_MIN(cbData - offRecord, sizeof(u));
+ rc = RTFileReadAt(hFile, offData + offRecord, &u, cbToRead, NULL);
+ if (RT_FAILURE(rc))
+ return RTMsgErrorExitFailure("RTFileReadAt(,%llu,,%zu,) failed: %Rrc", offData + offRecord, cbToRead, rc);
+
+ uint64_t const offEnd = offRecord + cbToRead;
+ uint8_t *pb = u.ab;
+ while (offRecord + 32 < offEnd)
+ {
+ PLNXPERFRECORDHEADER pRecHdr = (PLNXPERFRECORDHEADER)pb;
+ uint64_t const offNext = offRecord + pRecHdr->cb;
+ if (offNext > offEnd)
+ break;
+ if ( pRecHdr->uType == LNXPERF_RECORD_MMAP
+ && (pRecHdr->fMisc & LNXPERF_RECORD_MISC_CPUMODE_MASK) == LNXPERF_RECORD_MISC_KERNEL)
+ {
+ PLNXPERFRECORDMMAP pMmapRec = (PLNXPERFRECORDMMAP)pRecHdr;
+ if (cVerbosity > 0)
+ RTMsgInfo("MMAP: %016RX64 (%016RX64) LB %012RX64 %s\n",
+ pMmapRec->uAddress, pMmapRec->offFile, pMmapRec->cbMapping, pMmapRec->szFilename);
+
+ bool fSkip = false;
+ for (unsigned i = 0; i < cSkipPatterns && !fSkip; i++)
+ fSkip = RTStrSimplePatternMatch(apszSkipPatterns[i], pMmapRec->szFilename);
+
+ if (!fSkip)
+ {
+ /* Figure the max filename length we dare to put here. */
+ size_t cchFilename = strlen(pMmapRec->szFilename);
+ cchFilename = RT_ALIGN_Z(cchFilename + 1, 8) - 1;
+
+ for (unsigned i = 0; i < cModules; i++)
+ if ( aModules[i].offRecord == UINT64_MAX
+ && aModules[i].cchName <= cchFilename)
+ {
+ aModules[i].cbRecord = pRecHdr->cb;
+ aModules[i].offRecord = offData + offRecord;
+ cModulesLeft--;
+ if (cVerbosity > 0)
+ RTMsgInfo("Will replace module %s at offset %RU64 with %s\n",
+ pMmapRec->szFilename, offRecord, aModules[i].pszName);
+ break;
+ }
+ }
+ }
+
+ /* Advance */
+ pb += pRecHdr->cb;
+ offRecord = offNext;
+ }
+ }
+
+ /*
+ * Only proceed if we found insertion points for all specified modules.
+ */
+ if (cModulesLeft)
+ {
+ if (cModules)
+ {
+ RTMsgError("Unable to find suitable targets for:\n");
+ for (unsigned i = 0; i < cModules; i++)
+ if (aModules[i].offRecord == UINT64_MAX)
+ RTMsgError(" %s\n", aModules[i].pszName);
+ }
+ else
+ RTMsgError("No modules given, so nothing to do.\n");
+ return RTEXITCODE_FAILURE;
+ }
+
+ /*
+ * Sort the modules by record offset to simplify the copying.
+ */
+ RTSortShell(aModules, cModules, sizeof(aModules[0]), CompModuleRecordOffset, NULL);
+
+ RTFILE hOutFile;
+ rc = RTFileOpen(&hOutFile, pszOutput, RTFILE_O_WRITE | RTFILE_O_DENY_WRITE | RTFILE_O_CREATE);
+ if (RT_FAILURE(rc))
+ return RTMsgErrorExitFailure("Failed to creating '%s' for the output: %Rrc", pszOutput, rc);
+
+ unsigned iModule = 0;
+ uint64_t offNext = aModules[0].offRecord;
+ uint64_t off = 0;
+ for (;;)
+ {
+ Assert(off <= offNext);
+
+ /* Read a chunk of data. Records we modify are read separately. */
+ size_t cbToRead = RT_MIN(offNext - off, sizeof(u));
+ if (cbToRead == 0)
+ cbToRead = aModules[iModule].cbRecord;
+ size_t cbActual = 0;
+ rc = RTFileReadAt(hFile, off, &u, cbToRead, &cbActual);
+ if (RT_FAILURE(rc))
+ return RTMsgErrorExitFailure("Error reading %zu bytes at %RU64 in '%s': %Rrc", cbToRead, off, pszInput, rc);
+
+ /* EOF? */
+ if (cbActual == 0)
+ break;
+
+ /* A record we wish to modify? */
+ if (off == offNext)
+ {
+ if (cbActual != aModules[iModule].cbRecord)
+ return RTMsgErrorExitFailure("Internal error: cbActual=%zu cbRecord=%u off=%RU64\n",
+ cbActual, aModules[iModule].cbRecord, off);
+
+ PLNXPERFRECORDMMAP pMmapRec = (PLNXPERFRECORDMMAP)&u.ab[0];
+ strcpy(pMmapRec->szFilename, aModules[iModule].pszName);
+ pMmapRec->uAddress = aModules[iModule].uAddress;
+ pMmapRec->cbMapping = aModules[iModule].cbMapping;
+ pMmapRec->offFile = aModules[iModule].offFile;
+ RTMsgInfo("Done: %s\n", pMmapRec->szFilename);
+
+ iModule++;
+ if (iModule < cModules)
+ offNext = aModules[iModule].offRecord;
+ else
+ offNext = UINT64_MAX;
+ }
+
+ /* Write out the data. */
+ rc = RTFileWrite(hOutFile, &u, cbActual, NULL);
+ if (RT_FAILURE(rc))
+ return RTMsgErrorExitFailure("Error writing %zu bytes at %RU64 to '%s': %Rrc", cbActual, off, pszOutput, rc);
+
+ /* Advance.*/
+ off += cbActual;
+ }
+
+ if (iModule != cModules)
+ return RTMsgErrorExitFailure("Internal error: iModule=%u cModules=%u\n", iModule, cModules);
+
+ rc = RTFileClose(hOutFile);
+ if (RT_FAILURE(rc))
+ return RTMsgErrorExitFailure("Error closing output file '%s': %Rrc", pszOutput, rc);
+
+ return RTEXITCODE_SUCCESS;
+}
+
diff --git a/src/VBox/HostDrivers/Support/linux/Makefile b/src/VBox/HostDrivers/Support/linux/Makefile
new file mode 100644
index 00000000..900bcbd7
--- /dev/null
+++ b/src/VBox/HostDrivers/Support/linux/Makefile
@@ -0,0 +1,208 @@
+# $Id: Makefile $
+## @file
+# Makefile for the VirtualBox Linux Host Driver.
+#
+
+#
+# Copyright (C) 2006-2023 Oracle and/or its affiliates.
+#
+# This file is part of VirtualBox base platform packages, as
+# available from https://www.virtualbox.org.
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation, in version 3 of the
+# License.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, see <https://www.gnu.org/licenses>.
+#
+# The contents of this file may alternatively be used under the terms
+# of the Common Development and Distribution License Version 1.0
+# (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+# in the VirtualBox distribution, in which case the provisions of the
+# CDDL are applicable instead of those of the GPL.
+#
+# You may elect to license modified versions of this file under the
+# terms and conditions of either the GPL or the CDDL or both.
+#
+# SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+#
+
+# Linux kbuild sets this to our source directory if we are called from there
+obj ?= $(CURDIR)
+include $(obj)/Makefile-header.gmk
+VBOXDRV_DIR := $(VBOX_MODULE_SRC_DIR)
+
+VBOXMOD_NAME = vboxdrv
+VBOXMOD_OBJS = \
+ linux/SUPDrv-linux.o \
+ SUPDrv.o \
+ SUPDrvGip.o \
+ SUPDrvSem.o \
+ SUPDrvTracer.o \
+ SUPLibAll.o \
+ common/string/strformatrt.o
+
+ifndef VBOX_WITHOUT_COMBINED_SOURCES
+VBOXMOD_OBJS += \
+ combined-agnostic1.o \
+ combined-agnostic2.o \
+ combined-os-specific.o
+else # VBOX_WITHOUT_COMBINED_SOURCES
+VBOXMOD_OBJS += \
+ r0drv/alloc-r0drv.o \
+ r0drv/initterm-r0drv.o \
+ r0drv/memobj-r0drv.o \
+ r0drv/mpnotification-r0drv.o \
+ r0drv/powernotification-r0drv.o \
+ r0drv/linux/assert-r0drv-linux.o \
+ r0drv/linux/alloc-r0drv-linux.o \
+ r0drv/linux/initterm-r0drv-linux.o \
+ r0drv/linux/memobj-r0drv-linux.o \
+ r0drv/linux/memuserkernel-r0drv-linux.o \
+ r0drv/linux/mp-r0drv-linux.o \
+ r0drv/linux/mpnotification-r0drv-linux.o \
+ r0drv/linux/process-r0drv-linux.o \
+ r0drv/linux/rtStrFormatKernelAddress-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/threadctxhooks-r0drv-linux.o \
+ r0drv/linux/time-r0drv-linux.o \
+ r0drv/linux/timer-r0drv-linux.o \
+ r0drv/generic/semspinmutex-r0drv-generic.o \
+ common/alloc/alloc.o \
+ common/checksum/crc32.o \
+ common/checksum/ipv4.o \
+ common/checksum/ipv6.o \
+ common/err/RTErrConvertFromErrno.o \
+ common/err/RTErrConvertToErrno.o \
+ common/err/errinfo.o \
+ common/log/log.o \
+ common/log/logellipsis.o \
+ common/log/logrel.o \
+ common/log/logrelellipsis.o \
+ common/log/logcom.o \
+ common/log/logformat.o \
+ common/log/RTLogCreateEx.o \
+ common/misc/RTAssertMsg1Weak.o \
+ common/misc/RTAssertMsg2.o \
+ common/misc/RTAssertMsg2Add.o \
+ common/misc/RTAssertMsg2AddWeak.o \
+ common/misc/RTAssertMsg2AddWeakV.o \
+ common/misc/RTAssertMsg2Weak.o \
+ common/misc/RTAssertMsg2WeakV.o \
+ common/misc/assert.o \
+ common/misc/handletable.o \
+ common/misc/handletablectx.o \
+ common/misc/thread.o \
+ common/string/RTStrCat.o \
+ common/string/RTStrCopy.o \
+ common/string/RTStrCopyEx.o \
+ common/string/RTStrCopyP.o \
+ common/string/RTStrEnd.o \
+ common/string/RTStrNCmp.o \
+ common/string/RTStrNLen.o \
+ common/string/stringalloc.o \
+ common/string/strformat.o \
+ common/string/RTStrFormat.o \
+ common/string/strformatnum.o \
+ common/string/strformattype.o \
+ common/string/strprintf.o \
+ common/string/strprintf-ellipsis.o \
+ common/string/strprintf2.o \
+ common/string/strprintf2-ellipsis.o \
+ common/string/strtonum.o \
+ common/table/avlpv.o \
+ common/time/time.o \
+ r0drv/linux/RTLogWriteDebugger-r0drv-linux.o \
+ generic/RTAssertShouldPanic-generic.o \
+ generic/RTLogWriteStdErr-stub-generic.o \
+ generic/RTLogWriteStdOut-stub-generic.o \
+ generic/RTLogWriteUser-generic.o \
+ generic/RTMpGetArraySize-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/RTTimerCreate-generic.o \
+ generic/errvars-generic.o \
+ generic/mppresent-generic.o \
+ generic/uuid-generic.o \
+ VBox/log-vbox.o \
+ VBox/RTLogWriteVmm-amd64-x86.o
+ ifeq ($(VBOX_KBUILD_TARGET_ARCH),amd64)
+VBOXMOD_OBJS += common/alloc/heapsimple.o
+ endif
+endif # VBOX_WITHOUT_COMBINED_SOURCES
+ifeq ($(VBOX_KBUILD_TARGET_ARCH),x86)
+VBOXMOD_OBJS += \
+ math/gcc/divdi3.o \
+ math/gcc/divmoddi4.o \
+ math/gcc/moddi3.o \
+ math/gcc/qdivrem.o \
+ math/gcc/udivdi3.o \
+ math/gcc/udivmoddi4.o \
+ math/gcc/divdi3.o \
+ math/gcc/umoddi3.o
+endif
+ifdef VBOX_WITH_NATIVE_DTRACE
+VBOXMOD_OBJS += SUPDrvDTrace.o
+endif
+
+VBOXMOD_INCL = \
+ $(VBOXDRV_DIR) \
+ $(VBOXDRV_DIR)include \
+ $(VBOXDRV_DIR)r0drv/linux
+ifdef VBOX_WITH_NATIVE_DTRACE
+VBOXMOD_INCL += \
+ /usr/include/linux \
+ /usr/include
+endif
+
+VBOXMOD_DEFS = \
+ RT_OS_LINUX \
+ IN_RING0 \
+ IN_RT_R0 \
+ IN_SUP_R0 \
+ VBOX \
+ RT_WITH_VBOX \
+ VBOX_WITH_HARDENING \
+ SUPDRV_WITH_RELEASE_LOGGER \
+ VBOX_WITHOUT_EFLAGS_AC_SET_IN_VBOXDRV \
+ IPRT_WITHOUT_EFLAGS_AC_PRESERVING \
+ VBOX_WITH_64_BITS_GUESTS # <-- must be consistent with Config.kmk!
+ifndef CONFIG_VBOXDRV_FIXEDMAJOR
+VBOXMOD_DEFS += CONFIG_VBOXDRV_AS_MISC
+endif
+ifdef VBOX_WITH_NATIVE_DTRACE
+VBOXMOD_DEFS += VBOX_WITH_NATIVE_DTRACE
+endif
+ifdef VBOX_WITH_TEXT_MODMEM_HACK
+VBOXMOD_DEFS += RTMEMALLOC_EXEC_HEAP VBOX_WITH_TEXT_MODMEM_HACK
+endif
+VBOXMOD_CFLAGS = -include $(VBOXDRV_DIR)include/VBox/SUPDrvMangling.h \
+ -fno-omit-frame-pointer -fno-pie -Wno-declaration-after-statement
+
+
+include $(obj)/Makefile-footer.gmk
+
+check: $(VBOXMOD_0_TARGET)
+ @if ! readelf -p __ksymtab_strings vboxdrv.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/HostDrivers/Support/linux/Makefile-vbox_vboxddr0.gmk b/src/VBox/HostDrivers/Support/linux/Makefile-vbox_vboxddr0.gmk
new file mode 100644
index 00000000..08c7f604
--- /dev/null
+++ b/src/VBox/HostDrivers/Support/linux/Makefile-vbox_vboxddr0.gmk
@@ -0,0 +1,43 @@
+# $Id: Makefile-vbox_vboxddr0.gmk $
+## @file
+# Makefile for the VBoxDDR0.r0 wrapper module.
+#
+
+#
+# Copyright (C) 2021-2023 Oracle and/or its affiliates.
+#
+# This file is part of VirtualBox base platform packages, as
+# available from https://www.virtualbox.org.
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation, in version 3 of the
+# License.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, see <https://www.gnu.org/licenses>.
+#
+# The contents of this file may alternatively be used under the terms
+# of the Common Development and Distribution License Version 1.0
+# (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+# in the VirtualBox distribution, in which case the provisions of the
+# CDDL are applicable instead of those of the GPL.
+#
+# You may elect to license modified versions of this file under the
+# terms and conditions of either the GPL or the CDDL or both.
+#
+# SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+#
+
+VBOXMOD_NAME := vbox_vboxddr0
+WRAPPED_MODULE_NAME := VBoxDDR0
+WRAPPED_MODULE_FLAGS := 0
+WRAPPER_NEED_VMMR0 := yes
+
+include $(dir $(lastword $(MAKEFILE_LIST)))Makefile-wrapper.gmk
+
diff --git a/src/VBox/HostDrivers/Support/linux/Makefile-vbox_vmmr0.gmk b/src/VBox/HostDrivers/Support/linux/Makefile-vbox_vmmr0.gmk
new file mode 100644
index 00000000..c338d025
--- /dev/null
+++ b/src/VBox/HostDrivers/Support/linux/Makefile-vbox_vmmr0.gmk
@@ -0,0 +1,44 @@
+# $Id: Makefile-vbox_vmmr0.gmk $
+## @file
+# Makefile for the VMMR0.r0 wrapper module.
+#
+
+#
+# Copyright (C) 2021-2023 Oracle and/or its affiliates.
+#
+# This file is part of VirtualBox base platform packages, as
+# available from https://www.virtualbox.org.
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation, in version 3 of the
+# License.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, see <https://www.gnu.org/licenses>.
+#
+# The contents of this file may alternatively be used under the terms
+# of the Common Development and Distribution License Version 1.0
+# (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+# in the VirtualBox distribution, in which case the provisions of the
+# CDDL are applicable instead of those of the GPL.
+#
+# You may elect to license modified versions of this file under the
+# terms and conditions of either the GPL or the CDDL or both.
+#
+# SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+#
+
+VBOXMOD_NAME := vbox_vmmr0
+WRAPPED_MODULE_NAME := VMMR0
+WRAPPED_MODULE_FLAGS := SUPLDRWRAPPEDMODULE_F_VMMR0
+WRAPPED_MODULE_IS_VMMR0 := yes
+WRAPPED_MODULE_LINUX_EXPORTS := yes
+
+include $(dir $(lastword $(MAKEFILE_LIST)))Makefile-wrapper.gmk
+
diff --git a/src/VBox/HostDrivers/Support/linux/Makefile-wrapper.gmk b/src/VBox/HostDrivers/Support/linux/Makefile-wrapper.gmk
new file mode 100644
index 00000000..a47d55be
--- /dev/null
+++ b/src/VBox/HostDrivers/Support/linux/Makefile-wrapper.gmk
@@ -0,0 +1,166 @@
+# $Id: Makefile-wrapper.gmk $
+## @file
+# Makefile template for a wrapper module.
+#
+
+#
+# Copyright (C) 2006-2023 Oracle and/or its affiliates.
+#
+# This file is part of VirtualBox base platform packages, as
+# available from https://www.virtualbox.org.
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation, in version 3 of the
+# License.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, see <https://www.gnu.org/licenses>.
+#
+# The contents of this file may alternatively be used under the terms
+# of the Common Development and Distribution License Version 1.0
+# (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+# in the VirtualBox distribution, in which case the provisions of the
+# CDDL are applicable instead of those of the GPL.
+#
+# You may elect to license modified versions of this file under the
+# terms and conditions of either the GPL or the CDDL or both.
+#
+# SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+#
+
+# Check template parameters.
+ifndef WRAPPED_MODULE_NAME
+$(error WRAPPED_MODULE_NAME not defined)
+endif
+ifndef VBOXMOD_NAME
+$(error VBOXMOD_NAME not defined)
+endif
+
+
+# Linux kbuild sets this to our source directory if we are called from there
+ifndef VBOX_MODULE_SRC_DIR
+ VBOX_MODULE_SRC_DIR := $(abspath $(dir $(lastword $(MAKEFILE_LIST))))/
+endif
+VBOXDRV_DIR := $(abspath $(VBOX_MODULE_SRC_DIR)../vboxdrv/)/
+VBOX_VMMR0_DIR := $(abspath $(VBOX_MODULE_SRC_DIR)../vbox_vmmr0/)/
+include $(VBOXDRV_DIR)Makefile-header.gmk
+
+# All of these wrappers depend on
+ifndef KBUILD_EXTRA_SYMBOLS
+KBUILD_EXTRA_SYMBOLS := $(VBOXDRV_DIR)Module.symvers
+ ifdef WRAPPER_NEED_VMMR0
+KBUILD_EXTRA_SYMBOLS += $(VBOX_VMMR0_DIR)Module.symvers
+ endif
+endif
+
+VBOXMOD_OBJS = \
+ SUPWrapperMod-linux.o \
+ $(WRAPPED_MODULE_NAME).o
+
+VBOXMOD_INCL = \
+ $(VBOXDRV_DIR) \
+ $(VBOXDRV_DIR)include \
+ $(VBOXDRV_DIR)r0drv/linux
+
+VBOXMOD_DEFS += \
+ RT_OS_LINUX \
+ IN_RING0 \
+ IN_RT_R0 \
+ IN_SUP_R0 \
+ VBOX \
+ \
+ WRAPPED_MODULE_NAME=\"$(WRAPPED_MODULE_NAME).r0\" \
+ WRAPPED_MODULE_SYMBOL_INCLUDE=\"$(WRAPPED_MODULE_NAME)-symbols.h\"
+ifdef WRAPPED_MODULE_FLAGS
+VBOXMOD_DEFS += WRAPPED_MODULE_FLAGS="$(WRAPPED_MODULE_FLAGS)"
+endif
+ifdef WRAPPED_MODULE_LINUX_EXPORTS
+VBOXMOD_DEFS += WRAPPED_MODULE_LINUX_EXPORTS
+endif
+ifdef WRAPPED_MODULE_SRV_REQ_HANDLER
+VBOXMOD_DEFS += WRAPPED_MODULE_SRV_REQ_HANDLER="$(WRAPPED_MODULE_SRV_REQ_HANDLER)"
+endif
+ifdef WRAPPED_MODULE_IS_VMMR0
+VBOXMOD_DEFS += WRAPPED_MODULE_VMMR0_ENTRY_FAST=VMMR0EntryFast
+VBOXMOD_DEFS += WRAPPED_MODULE_VMMR0_ENTRY_EX=VMMR0EntryEx
+endif
+ifdef WRAPPED_MODULE_NO_INIT
+VBOXMOD_DEFS += WRAPPED_MODULE_INIT=NULL
+endif
+ifdef WRAPPED_MODULE_NO_TERM
+VBOXMOD_DEFS += WRAPPED_MODULE_TERM=NULL
+endif
+ifdef WRAPPED_MODULE_LICENSE_PROPRIETARY
+VBOXMOD_DEFS += WRAPPED_MODULE_LICENSE_PROPRIETARY
+endif
+
+VBOXMOD_CFLAGS = -include $(VBOXDRV_DIR)include/VBox/SUPDrvMangling.h \
+ -fno-omit-frame-pointer -fno-pie -Wno-declaration-after-statement
+
+## @todo cleanup
+
+include $(VBOXDRV_DIR)Makefile-footer.gmk
+
+
+#
+# Custom rules (some of this could later be done before install).
+#
+SUPWrapperMod-linux.c \
+$(VBOX_MODULE_SRC_DIR)SUPWrapperMod-linux.c \
+$(VBOX_MODULE_SRC_DIR)SUPWrapperMod-linux.o \
+: $(VBOX_MODULE_SRC_DIR)$(WRAPPED_MODULE_NAME)-symbols.h
+
+$(VBOX_MODULE_SRC_DIR)$(WRAPPED_MODULE_NAME)-symbols.h: \
+ $(VBOX_MODULE_SRC_DIR)$(WRAPPED_MODULE_NAME).r0 \
+ $(VBOX_MODULE_SRC_DIR)Makefile-wrapper.gmk \
+ $(VBOX_MODULE_SRC_DIR)Makefile
+ rm -f -- "$@" "$@-tmp1" "$@-tmp2"
+ objdump --syms "$<" > "$@-tmp1"
+ sed -e '/[[:space:]].hidden[[:space:]]/d' \
+ -e '/[[:space:]]\*UND\*[[:space:]]/d' \
+ -e '/[[:space:]]vboxr0mod_/d' \
+ -e '/^[[:xdigit:]][[:xdigit:]]*[[:space:]][gu]/!d' \
+ -e 's/^\([[:xdigit:]]*\)[[:space:]].*000[[:xdigit:]]*[[:space:]]\([_a-zA-Z].*\)$$/SYMBOL_ENTRY(\2)/' \
+ -e '/SYMBOL_ENTRY(ModuleInit)/d' \
+ -e '/SYMBOL_ENTRY(ModuleTerm)/d' \
+ "$@-tmp1" > "$@-tmp2"
+ sort "$@-tmp2" > "$@"
+ rm -f -- "$@-tmp1" "$@-tmp2"
+
+
+$(VBOX_MODULE_SRC_DIR)$(WRAPPED_MODULE_NAME).mangle-symbols: \
+ $(VBOXDRV_DIR)Module.symvers \
+ $(VBOX_MODULE_SRC_DIR)$(WRAPPED_MODULE_NAME)-symbols.h \
+ $(VBOX_MODULE_SRC_DIR)Makefile-wrapper.gmk \
+ $(VBOX_MODULE_SRC_DIR)Makefile
+ sed -e 's/SYMBOL_ENTRY(\([^)]*\))/\/\1 VBoxHost_\1\/d/' \
+ "$(VBOX_MODULE_SRC_DIR)$(WRAPPED_MODULE_NAME)-symbols.h" \
+ > "$@-ignore.sed"
+ sed -e '/[[:space:]]VBoxHost_/!d' \
+ -e 's/^0x[[:xdigit:]]*[[:space:]]VBoxHost_\([^[:space:]]*\)[[:space:]].*$$/\1 VBoxHost_\1/' \
+ -f "$@-ignore.sed" \
+ "$<" \
+ > "$@"
+
+$(VBOX_MODULE_SRC_DIR)$(WRAPPED_MODULE_NAME).o: \
+ $(VBOX_MODULE_SRC_DIR)$(WRAPPED_MODULE_NAME).r0 \
+ $(VBOX_MODULE_SRC_DIR)$(WRAPPED_MODULE_NAME).debug \
+ $(VBOX_MODULE_SRC_DIR)$(WRAPPED_MODULE_NAME).mangle-symbols \
+ $(VBOX_MODULE_SRC_DIR)Makefile-wrapper.gmk \
+ $(VBOX_MODULE_SRC_DIR)Makefile \
+ $(if $(CONFIG_UNWINDER_ORC),$(objtool_dep),)
+ rm -f -- "$@" "$@-tmp"
+ eu-unstrip -o "$@-tmp" $(VBOX_MODULE_SRC_DIR)$(WRAPPED_MODULE_NAME).r0 $(VBOX_MODULE_SRC_DIR)$(WRAPPED_MODULE_NAME).debug
+ objcopy --redefine-syms=$(VBOX_MODULE_SRC_DIR)$(WRAPPED_MODULE_NAME).mangle-symbols "$@-tmp" "$@"
+ rm -f -- "$@-tmp"
+ifdef CONFIG_UNWINDER_ORC # Must generate the ORC sections.
+ $(__objtool_obj) $(objtool_args) $@
+endif
+ touch $(VBOX_MODULE_SRC_DIR).$(WRAPPED_MODULE_NAME).o.cmd
+
diff --git a/src/VBox/HostDrivers/Support/linux/Makefile.kup b/src/VBox/HostDrivers/Support/linux/Makefile.kup
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/src/VBox/HostDrivers/Support/linux/Makefile.kup
diff --git a/src/VBox/HostDrivers/Support/linux/SUPDrv-linux.c b/src/VBox/HostDrivers/Support/linux/SUPDrv-linux.c
new file mode 100644
index 00000000..2a77412c
--- /dev/null
+++ b/src/VBox/HostDrivers/Support/linux/SUPDrv-linux.c
@@ -0,0 +1,1796 @@
+/* $Id: SUPDrv-linux.c $ */
+/** @file
+ * VBoxDrv - The VirtualBox Support Driver - Linux specifics.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#define LOG_GROUP LOG_GROUP_SUP_DRV
+#include "../SUPDrvInternal.h"
+#include "the-linux-kernel.h"
+#include "version-generated.h"
+#include "product-generated.h"
+#include "revision-generated.h"
+
+#include <iprt/assert.h>
+#include <iprt/spinlock.h>
+#include <iprt/semaphore.h>
+#include <iprt/initterm.h>
+#include <iprt/process.h>
+#include <iprt/thread.h>
+#include <VBox/err.h>
+#include <iprt/mem.h>
+#include <VBox/log.h>
+#include <iprt/mp.h>
+
+/** @todo figure out the exact version number */
+#if RTLNX_VER_MIN(2,6,16)
+# include <iprt/power.h>
+# define VBOX_WITH_SUSPEND_NOTIFICATION
+#endif
+
+#include <linux/sched.h>
+#include <linux/miscdevice.h>
+#ifdef VBOX_WITH_SUSPEND_NOTIFICATION
+# include <linux/platform_device.h>
+#endif
+#if (RTLNX_VER_MIN(2,6,28)) && defined(SUPDRV_WITH_MSR_PROBER)
+# define SUPDRV_LINUX_HAS_SAFE_MSR_API
+# include <asm/msr.h>
+#endif
+
+#include <asm/desc.h>
+
+#include <iprt/asm-amd64-x86.h>
+
+
+/*********************************************************************************************************************************
+* Defined Constants And Macros *
+*********************************************************************************************************************************/
+/* check kernel version */
+# ifndef SUPDRV_AGNOSTIC
+# if RTLNX_VER_MAX(2,6,0)
+# error Unsupported kernel version!
+# endif
+# endif
+
+#ifdef CONFIG_X86_HIGH_ENTRY
+# error "CONFIG_X86_HIGH_ENTRY is not supported by VBoxDrv at this time."
+#endif
+
+/* We cannot include x86.h, so we copy the defines we need here: */
+#define X86_EFL_IF RT_BIT(9)
+#define X86_EFL_AC RT_BIT(18)
+#define X86_EFL_DF RT_BIT(10)
+#define X86_EFL_IOPL (RT_BIT(12) | RT_BIT(13))
+
+/* To include the version number of VirtualBox into kernel backtraces: */
+#define VBoxDrvLinuxVersion RT_CONCAT3(RT_CONCAT(VBOX_VERSION_MAJOR, _), \
+ RT_CONCAT(VBOX_VERSION_MINOR, _), \
+ VBOX_VERSION_BUILD)
+#define VBoxDrvLinuxIOCtl RT_CONCAT(VBoxDrvLinuxIOCtl_,VBoxDrvLinuxVersion)
+
+/* Once externally provided, this string will be printed into kernel log on
+ * module start together with the rest of versioning information. */
+#ifndef VBOX_EXTRA_VERSION_STRING
+# define VBOX_EXTRA_VERSION_STRING ""
+#endif
+
+
+/*********************************************************************************************************************************
+* Structures and Typedefs *
+*********************************************************************************************************************************/
+#if RTLNX_VER_MIN(5,0,0)
+/** Wrapper module list entry. */
+typedef struct SUPDRVLNXMODULE
+{
+ RTLISTNODE ListEntry;
+ struct module *pModule;
+} SUPDRVLNXMODULE;
+/** Pointer to a wrapper module list entry. */
+typedef SUPDRVLNXMODULE *PSUPDRVLNXMODULE;
+#endif
+
+
+/*********************************************************************************************************************************
+* Internal Functions *
+*********************************************************************************************************************************/
+static int __init VBoxDrvLinuxInit(void);
+static void __exit VBoxDrvLinuxUnload(void);
+static int VBoxDrvLinuxCreateSys(struct inode *pInode, struct file *pFilp);
+static int VBoxDrvLinuxCreateUsr(struct inode *pInode, struct file *pFilp);
+static int VBoxDrvLinuxClose(struct inode *pInode, struct file *pFilp);
+#ifdef HAVE_UNLOCKED_IOCTL
+static long VBoxDrvLinuxIOCtl(struct file *pFilp, unsigned int uCmd, unsigned long ulArg);
+#else
+static int VBoxDrvLinuxIOCtl(struct inode *pInode, struct file *pFilp, unsigned int uCmd, unsigned long ulArg);
+#endif
+static int VBoxDrvLinuxIOCtlSlow(struct file *pFilp, unsigned int uCmd, unsigned long ulArg, PSUPDRVSESSION pSession);
+static int VBoxDrvLinuxErr2LinuxErr(int);
+#ifdef VBOX_WITH_SUSPEND_NOTIFICATION
+static int VBoxDrvProbe(struct platform_device *pDev);
+# if RTLNX_VER_MIN(2,6,30)
+static int VBoxDrvSuspend(struct device *pDev);
+static int VBoxDrvResume(struct device *pDev);
+# else
+static int VBoxDrvSuspend(struct platform_device *pDev, pm_message_t State);
+static int VBoxDrvResume(struct platform_device *pDev);
+# endif
+static void VBoxDevRelease(struct device *pDev);
+#endif
+#if RTLNX_VER_MIN(5,0,0)
+static int supdrvLinuxLdrModuleNotifyCallback(struct notifier_block *pBlock,
+ unsigned long uModuleState, void *pvModule);
+#endif
+
+
+/*********************************************************************************************************************************
+* Global Variables *
+*********************************************************************************************************************************/
+/**
+ * Device extention & session data association structure.
+ */
+static SUPDRVDEVEXT g_DevExt;
+
+/** Module parameter.
+ * Not prefixed because the name is used by macros and the end of this file. */
+static int force_async_tsc = 0;
+
+/** The system device name. */
+#define DEVICE_NAME_SYS "vboxdrv"
+/** The user device name. */
+#define DEVICE_NAME_USR "vboxdrvu"
+
+/** The file_operations structure. */
+static struct file_operations gFileOpsVBoxDrvSys =
+{
+ owner: THIS_MODULE,
+ open: VBoxDrvLinuxCreateSys,
+ release: VBoxDrvLinuxClose,
+#ifdef HAVE_UNLOCKED_IOCTL
+ unlocked_ioctl: VBoxDrvLinuxIOCtl,
+#else
+ ioctl: VBoxDrvLinuxIOCtl,
+#endif
+};
+
+/** The file_operations structure. */
+static struct file_operations gFileOpsVBoxDrvUsr =
+{
+ owner: THIS_MODULE,
+ open: VBoxDrvLinuxCreateUsr,
+ release: VBoxDrvLinuxClose,
+#ifdef HAVE_UNLOCKED_IOCTL
+ unlocked_ioctl: VBoxDrvLinuxIOCtl,
+#else
+ ioctl: VBoxDrvLinuxIOCtl,
+#endif
+};
+
+/** The miscdevice structure for vboxdrv. */
+static struct miscdevice gMiscDeviceSys =
+{
+ minor: MISC_DYNAMIC_MINOR,
+ name: DEVICE_NAME_SYS,
+ fops: &gFileOpsVBoxDrvSys,
+# if RTLNX_VER_MAX(2,6,18)
+ devfs_name: DEVICE_NAME_SYS,
+# endif
+};
+/** The miscdevice structure for vboxdrvu. */
+static struct miscdevice gMiscDeviceUsr =
+{
+ minor: MISC_DYNAMIC_MINOR,
+ name: DEVICE_NAME_USR,
+ fops: &gFileOpsVBoxDrvUsr,
+# if RTLNX_VER_MAX(2,6,18)
+ devfs_name: DEVICE_NAME_USR,
+# endif
+};
+
+
+#ifdef VBOX_WITH_SUSPEND_NOTIFICATION
+
+# if RTLNX_VER_MIN(2,6,30)
+static struct dev_pm_ops gPlatformPMOps =
+{
+ .suspend = VBoxDrvSuspend, /* before entering deep sleep */
+ .resume = VBoxDrvResume, /* after wakeup from deep sleep */
+ .freeze = VBoxDrvSuspend, /* before creating hibernation image */
+ .restore = VBoxDrvResume, /* after waking up from hibernation */
+};
+# endif
+
+static struct platform_driver gPlatformDriver =
+{
+ .probe = VBoxDrvProbe,
+# if RTLNX_VER_MAX(2,6,30)
+ .suspend = VBoxDrvSuspend,
+ .resume = VBoxDrvResume,
+# endif
+ /** @todo .shutdown? */
+ .driver =
+ {
+ .name = "vboxdrv",
+# if RTLNX_VER_MIN(2,6,30)
+ .pm = &gPlatformPMOps,
+# endif
+ }
+};
+
+static struct platform_device gPlatformDevice =
+{
+ .name = "vboxdrv",
+ .dev =
+ {
+ .release = VBoxDevRelease
+ }
+};
+
+#endif /* VBOX_WITH_SUSPEND_NOTIFICATION */
+
+#if RTLNX_VER_MIN(5,0,0)
+/** Module load/unload notification registration record. */
+static struct notifier_block g_supdrvLinuxModuleNotifierBlock =
+{
+ .notifier_call = supdrvLinuxLdrModuleNotifyCallback,
+ .priority = 0
+};
+/** Spinlock protecting g_supdrvLinuxWrapperModuleList. */
+static spinlock_t g_supdrvLinuxWrapperModuleSpinlock;
+/** List of potential wrapper modules (PSUPDRVLNXMODULE). */
+static RTLISTANCHOR g_supdrvLinuxWrapperModuleList;
+#endif
+
+
+/** Get the kernel UID for the current process. */
+DECLINLINE(RTUID) vboxdrvLinuxKernUid(void)
+{
+#if RTLNX_VER_MIN(2,6,29)
+# if RTLNX_VER_MIN(3,5,0)
+ return __kuid_val(current->cred->uid);
+# else
+ return current->cred->uid;
+# endif
+#else
+ return current->uid;
+#endif
+}
+
+
+/** Get the kernel GID for the current process. */
+DECLINLINE(RTGID) vboxdrvLinuxKernGid(void)
+{
+#if RTLNX_VER_MIN(2,6,29)
+# if RTLNX_VER_MIN(3,5,0)
+ return __kgid_val(current->cred->gid);
+# else
+ return current->cred->gid;
+# endif
+#else
+ return current->gid;
+#endif
+}
+
+
+#ifdef VBOX_WITH_HARDENING
+/** Get the effective UID within the current user namespace. */
+DECLINLINE(RTUID) vboxdrvLinuxEuidInNs(void)
+{
+# if RTLNX_VER_MIN(2,6,29)
+# if RTLNX_VER_MIN(3,5,0)
+ return from_kuid(current_user_ns(), current->cred->euid);
+# else
+ return current->cred->euid;
+# endif
+# else
+ return current->euid;
+# endif
+}
+#endif
+
+
+/**
+ * Initialize module.
+ *
+ * @returns appropriate status code.
+ */
+static int __init VBoxDrvLinuxInit(void)
+{
+ int rc;
+
+#if RTLNX_VER_MIN(5,0,0)
+ spin_lock_init(&g_supdrvLinuxWrapperModuleSpinlock);
+ RTListInit(&g_supdrvLinuxWrapperModuleList);
+#endif
+
+ /*
+ * Check for synchronous/asynchronous TSC mode.
+ */
+ printk(KERN_DEBUG "vboxdrv: Found %u processor cores/threads\n", (unsigned)RTMpGetOnlineCount());
+ rc = misc_register(&gMiscDeviceSys);
+ if (rc)
+ {
+ printk(KERN_ERR "vboxdrv: Can't register system misc device! rc=%d\n", rc);
+ return rc;
+ }
+ rc = misc_register(&gMiscDeviceUsr);
+ if (rc)
+ {
+ printk(KERN_ERR "vboxdrv: Can't register user misc device! rc=%d\n", rc);
+ misc_deregister(&gMiscDeviceSys);
+ return rc;
+ }
+ if (!rc)
+ {
+ /*
+ * Initialize the runtime.
+ * On AMD64 we'll have to donate the high rwx memory block to the exec allocator.
+ */
+ rc = RTR0Init(0);
+ if (RT_SUCCESS(rc))
+ {
+ Log(("VBoxDrv::ModuleInit\n"));
+
+ /*
+ * Initialize the device extension.
+ */
+ rc = supdrvInitDevExt(&g_DevExt, sizeof(SUPDRVSESSION));
+ if (RT_SUCCESS(rc))
+ {
+#ifdef VBOX_WITH_SUSPEND_NOTIFICATION
+ rc = platform_driver_register(&gPlatformDriver);
+ if (rc == 0)
+ {
+ rc = platform_device_register(&gPlatformDevice);
+ if (rc == 0)
+#endif
+ {
+#if RTLNX_VER_MIN(5,0,0)
+ /*
+ * Register the module notifier.
+ */
+ int rc2 = register_module_notifier(&g_supdrvLinuxModuleNotifierBlock);
+ if (rc2)
+ printk(KERN_WARNING "vboxdrv: failed to register module notifier! rc2=%d\n", rc2);
+#endif
+
+
+ printk(KERN_INFO "vboxdrv: TSC mode is %s, tentative frequency %llu Hz\n",
+ SUPGetGIPModeName(g_DevExt.pGip), g_DevExt.pGip->u64CpuHz);
+ LogFlow(("VBoxDrv::ModuleInit returning %#x\n", rc));
+ printk(KERN_DEBUG "vboxdrv: Successfully loaded version "
+ VBOX_VERSION_STRING " r" RT_XSTR(VBOX_SVN_REV)
+ VBOX_EXTRA_VERSION_STRING
+ " (interface " RT_XSTR(SUPDRV_IOC_VERSION) ")\n");
+ return rc;
+ }
+#ifdef VBOX_WITH_SUSPEND_NOTIFICATION
+ else
+ platform_driver_unregister(&gPlatformDriver);
+ }
+#endif
+ }
+
+ rc = -EINVAL;
+ RTR0TermForced();
+ }
+ else
+ rc = -EINVAL;
+
+ /*
+ * Failed, cleanup and return the error code.
+ */
+ }
+ misc_deregister(&gMiscDeviceSys);
+ misc_deregister(&gMiscDeviceUsr);
+ Log(("VBoxDrv::ModuleInit returning %#x (minor:%d & %d)\n", rc, gMiscDeviceSys.minor, gMiscDeviceUsr.minor));
+ return rc;
+}
+
+
+/**
+ * Unload the module.
+ */
+static void __exit VBoxDrvLinuxUnload(void)
+{
+ Log(("VBoxDrvLinuxUnload\n"));
+
+#ifdef VBOX_WITH_SUSPEND_NOTIFICATION
+ platform_device_unregister(&gPlatformDevice);
+ platform_driver_unregister(&gPlatformDriver);
+#endif
+
+#if RTLNX_VER_MIN(5,0,0)
+ /*
+ * Kick the list of potential wrapper modules.
+ */
+ unregister_module_notifier(&g_supdrvLinuxModuleNotifierBlock);
+
+ spin_lock(&g_supdrvLinuxWrapperModuleSpinlock);
+ while (!RTListIsEmpty(&g_supdrvLinuxWrapperModuleList))
+ {
+ PSUPDRVLNXMODULE pCur = RTListRemoveFirst(&g_supdrvLinuxWrapperModuleList, SUPDRVLNXMODULE, ListEntry);
+ spin_unlock(&g_supdrvLinuxWrapperModuleSpinlock);
+
+ pCur->pModule = NULL;
+ RTMemFree(pCur);
+
+ spin_lock(&g_supdrvLinuxWrapperModuleSpinlock);
+ }
+ spin_unlock(&g_supdrvLinuxWrapperModuleSpinlock);
+#endif
+
+ /*
+ * I Don't think it's possible to unload a driver which processes have
+ * opened, at least we'll blindly assume that here.
+ */
+ misc_deregister(&gMiscDeviceUsr);
+ misc_deregister(&gMiscDeviceSys);
+
+ /*
+ * Destroy GIP, delete the device extension and terminate IPRT.
+ */
+ supdrvDeleteDevExt(&g_DevExt);
+ RTR0TermForced();
+}
+
+
+/**
+ * Common open code.
+ *
+ * @param pInode Pointer to inode info structure.
+ * @param pFilp Associated file pointer.
+ * @param fUnrestricted Indicates which device node which was opened.
+ */
+static int vboxdrvLinuxCreateCommon(struct inode *pInode, struct file *pFilp, bool fUnrestricted)
+{
+ int rc;
+ PSUPDRVSESSION pSession;
+ Log(("VBoxDrvLinuxCreate: pFilp=%p pid=%d/%d %s\n", pFilp, RTProcSelf(), current->pid, current->comm));
+
+#ifdef VBOX_WITH_HARDENING
+ /*
+ * Only root is allowed to access the unrestricted device, enforce it!
+ */
+ if ( fUnrestricted
+ && vboxdrvLinuxEuidInNs() != 0 /* root */ )
+ {
+ Log(("VBoxDrvLinuxCreate: euid=%d, expected 0 (root)\n", vboxdrvLinuxEuidInNs()));
+ return -EPERM;
+ }
+#endif /* VBOX_WITH_HARDENING */
+
+ /*
+ * Call common code for the rest.
+ */
+ rc = supdrvCreateSession(&g_DevExt, true /* fUser */, fUnrestricted, &pSession);
+ if (!rc)
+ {
+ pSession->Uid = vboxdrvLinuxKernUid();
+ pSession->Gid = vboxdrvLinuxKernGid();
+ }
+
+ pFilp->private_data = pSession;
+
+ Log(("VBoxDrvLinuxCreate: g_DevExt=%p pSession=%p rc=%d/%d (pid=%d/%d %s)\n",
+ &g_DevExt, pSession, rc, VBoxDrvLinuxErr2LinuxErr(rc),
+ RTProcSelf(), current->pid, current->comm));
+ return VBoxDrvLinuxErr2LinuxErr(rc);
+}
+
+
+/** /dev/vboxdrv. */
+static int VBoxDrvLinuxCreateSys(struct inode *pInode, struct file *pFilp)
+{
+ return vboxdrvLinuxCreateCommon(pInode, pFilp, true);
+}
+
+
+/** /dev/vboxdrvu. */
+static int VBoxDrvLinuxCreateUsr(struct inode *pInode, struct file *pFilp)
+{
+ return vboxdrvLinuxCreateCommon(pInode, pFilp, false);
+}
+
+
+/**
+ * Close device.
+ *
+ * @param pInode Pointer to inode info structure.
+ * @param pFilp Associated file pointer.
+ */
+static int VBoxDrvLinuxClose(struct inode *pInode, struct file *pFilp)
+{
+ Log(("VBoxDrvLinuxClose: pFilp=%p pSession=%p pid=%d/%d %s\n",
+ pFilp, pFilp->private_data, RTProcSelf(), current->pid, current->comm));
+ supdrvSessionRelease((PSUPDRVSESSION)pFilp->private_data);
+ pFilp->private_data = NULL;
+ return 0;
+}
+
+
+#ifdef VBOX_WITH_SUSPEND_NOTIFICATION
+/**
+ * Dummy device release function. We have to provide this function,
+ * otherwise the kernel will complain.
+ *
+ * @param pDev Pointer to the platform device.
+ */
+static void VBoxDevRelease(struct device *pDev)
+{
+}
+
+/**
+ * Dummy probe function.
+ *
+ * @param pDev Pointer to the platform device.
+ */
+static int VBoxDrvProbe(struct platform_device *pDev)
+{
+ return 0;
+}
+
+/**
+ * Suspend callback.
+ * @param pDev Pointer to the platform device.
+ * @param State Message type, see Documentation/power/devices.txt.
+ * Ignored.
+ */
+# if RTLNX_VER_MIN(2,6,30) && !defined(DOXYGEN_RUNNING)
+static int VBoxDrvSuspend(struct device *pDev)
+# else
+static int VBoxDrvSuspend(struct platform_device *pDev, pm_message_t State)
+# endif
+{
+ RTPowerSignalEvent(RTPOWEREVENT_SUSPEND);
+ return 0;
+}
+
+/**
+ * Resume callback.
+ *
+ * @param pDev Pointer to the platform device.
+ */
+# if RTLNX_VER_MIN(2,6,30)
+static int VBoxDrvResume(struct device *pDev)
+# else
+static int VBoxDrvResume(struct platform_device *pDev)
+# endif
+{
+ RTPowerSignalEvent(RTPOWEREVENT_RESUME);
+ return 0;
+}
+#endif /* VBOX_WITH_SUSPEND_NOTIFICATION */
+
+
+/**
+ * Device I/O Control entry point.
+ *
+ * @param pFilp Associated file pointer.
+ * @param uCmd The function specified to ioctl().
+ * @param ulArg The argument specified to ioctl().
+ */
+#if defined(HAVE_UNLOCKED_IOCTL) || defined(DOXYGEN_RUNNING)
+static long VBoxDrvLinuxIOCtl(struct file *pFilp, unsigned int uCmd, unsigned long ulArg)
+#else
+static int VBoxDrvLinuxIOCtl(struct inode *pInode, struct file *pFilp, unsigned int uCmd, unsigned long ulArg)
+#endif
+{
+ PSUPDRVSESSION pSession = (PSUPDRVSESSION)pFilp->private_data;
+ int rc;
+#ifndef VBOX_WITHOUT_EFLAGS_AC_SET_IN_VBOXDRV
+# if defined(VBOX_STRICT) || defined(VBOX_WITH_EFLAGS_AC_SET_IN_VBOXDRV)
+ RTCCUINTREG fSavedEfl;
+
+ /*
+ * Refuse all I/O control calls if we've ever detected EFLAGS.AC being cleared.
+ *
+ * This isn't a problem, as there is absolutely nothing in the kernel context that
+ * depend on user context triggering cleanups. That would be pretty wild, right?
+ */
+ if (RT_UNLIKELY(g_DevExt.cBadContextCalls > 0))
+ {
+ SUPR0Printf("VBoxDrvLinuxIOCtl: EFLAGS.AC=0 detected %u times, refusing all I/O controls!\n", g_DevExt.cBadContextCalls);
+ return ESPIPE;
+ }
+
+ fSavedEfl = ASMAddFlags(X86_EFL_AC);
+# else
+ stac();
+# endif
+#endif
+
+ /*
+ * Deal with the two high-speed IOCtl that takes it's arguments from
+ * the session and iCmd, and only returns a VBox status code.
+ */
+ AssertCompile(_IOC_NRSHIFT == 0 && _IOC_NRBITS == 8);
+#ifdef HAVE_UNLOCKED_IOCTL
+ if (RT_LIKELY( (unsigned int)(uCmd - SUP_IOCTL_FAST_DO_FIRST) < (unsigned int)32
+ && pSession->fUnrestricted))
+ rc = supdrvIOCtlFast(uCmd - SUP_IOCTL_FAST_DO_FIRST, ulArg, &g_DevExt, pSession);
+ else
+ rc = VBoxDrvLinuxIOCtlSlow(pFilp, uCmd, ulArg, pSession);
+#else /* !HAVE_UNLOCKED_IOCTL */
+ unlock_kernel();
+ if (RT_LIKELY( (unsigned int)(uCmd - SUP_IOCTL_FAST_DO_FIRST) < (unsigned int)32
+ && pSession->fUnrestricted))
+ rc = supdrvIOCtlFast(uCmd - SUP_IOCTL_FAST_DO_FIRST, ulArg, &g_DevExt, pSession);
+ else
+ rc = VBoxDrvLinuxIOCtlSlow(pFilp, uCmd, ulArg, pSession);
+ lock_kernel();
+#endif /* !HAVE_UNLOCKED_IOCTL */
+
+#ifndef VBOX_WITHOUT_EFLAGS_AC_SET_IN_VBOXDRV
+# if defined(VBOX_STRICT) || defined(VBOX_WITH_EFLAGS_AC_SET_IN_VBOXDRV)
+ /*
+ * Before we restore AC and the rest of EFLAGS, check if the IOCtl handler code
+ * accidentially modified it or some other important flag.
+ */
+ if (RT_UNLIKELY( (ASMGetFlags() & (X86_EFL_AC | X86_EFL_IF | X86_EFL_DF))
+ != ((fSavedEfl & (X86_EFL_AC | X86_EFL_IF | X86_EFL_DF)) | X86_EFL_AC) ))
+ {
+ char szTmp[48];
+ RTStrPrintf(szTmp, sizeof(szTmp), "uCmd=%#x: %#x->%#x!", _IOC_NR(uCmd), (uint32_t)fSavedEfl, (uint32_t)ASMGetFlags());
+ supdrvBadContext(&g_DevExt, "SUPDrv-linux.c", __LINE__, szTmp);
+ }
+ ASMSetFlags(fSavedEfl);
+# else
+ clac();
+# endif
+#endif
+ return rc;
+}
+
+
+/**
+ * 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().
+ * @param pSession The session instance.
+ */
+static int VBoxDrvLinuxIOCtlSlow(struct file *pFilp, unsigned int uCmd, unsigned long ulArg, PSUPDRVSESSION pSession)
+{
+ int rc;
+ SUPREQHDR Hdr;
+ PSUPREQHDR pHdr;
+ uint32_t cbBuf;
+
+ Log6(("VBoxDrvLinuxIOCtl: pFilp=%p uCmd=%#x ulArg=%p pid=%d/%d\n", pFilp, uCmd, (void *)ulArg, RTProcSelf(), current->pid));
+
+ /*
+ * Read the header.
+ */
+ if (RT_FAILURE(RTR0MemUserCopyFrom(&Hdr, ulArg, sizeof(Hdr))))
+ {
+ Log(("VBoxDrvLinuxIOCtl: copy_from_user(,%#lx,) failed; uCmd=%#x\n", ulArg, uCmd));
+ return -EFAULT;
+ }
+ if (RT_UNLIKELY((Hdr.fFlags & SUPREQHDR_FLAGS_MAGIC_MASK) != SUPREQHDR_FLAGS_MAGIC))
+ {
+ Log(("VBoxDrvLinuxIOCtl: bad header magic %#x; uCmd=%#x\n", Hdr.fFlags & SUPREQHDR_FLAGS_MAGIC_MASK, uCmd));
+ return -EINVAL;
+ }
+
+ /*
+ * Buffer the request.
+ */
+ cbBuf = RT_MAX(Hdr.cbIn, Hdr.cbOut);
+ if (RT_UNLIKELY(cbBuf > _1M*16))
+ {
+ Log(("VBoxDrvLinuxIOCtl: too big cbBuf=%#x; uCmd=%#x\n", cbBuf, uCmd));
+ return -E2BIG;
+ }
+ if (RT_UNLIKELY(_IOC_SIZE(uCmd) ? cbBuf != _IOC_SIZE(uCmd) : Hdr.cbIn < sizeof(Hdr)))
+ {
+ Log(("VBoxDrvLinuxIOCtl: bad ioctl cbBuf=%#x _IOC_SIZE=%#x; uCmd=%#x\n", cbBuf, _IOC_SIZE(uCmd), uCmd));
+ return -EINVAL;
+ }
+ pHdr = RTMemAlloc(cbBuf);
+ if (RT_UNLIKELY(!pHdr))
+ {
+ OSDBGPRINT(("VBoxDrvLinuxIOCtl: failed to allocate buffer of %d bytes for uCmd=%#x\n", cbBuf, uCmd));
+ return -ENOMEM;
+ }
+ if (RT_FAILURE(RTR0MemUserCopyFrom(pHdr, ulArg, Hdr.cbIn)))
+ {
+ Log(("VBoxDrvLinuxIOCtl: 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 = supdrvIOCtl(uCmd, &g_DevExt, pSession, pHdr, cbBuf);
+
+ /*
+ * Copy ioctl data and output buffer back to user space.
+ */
+ if (RT_LIKELY(!rc))
+ {
+ uint32_t cbOut = pHdr->cbOut;
+ if (RT_UNLIKELY(cbOut > cbBuf))
+ {
+ OSDBGPRINT(("VBoxDrvLinuxIOCtl: too much output! %#x > %#x; uCmd=%#x!\n", cbOut, cbBuf, uCmd));
+ cbOut = cbBuf;
+ }
+ if (RT_FAILURE(RTR0MemUserCopyTo(ulArg, pHdr, cbOut)))
+ {
+ /* this is really bad! */
+ OSDBGPRINT(("VBoxDrvLinuxIOCtl: copy_to_user(%#lx,,%#x); uCmd=%#x!\n", ulArg, cbOut, uCmd));
+ rc = -EFAULT;
+ }
+ }
+ else
+ {
+ Log(("VBoxDrvLinuxIOCtl: pFilp=%p uCmd=%#x ulArg=%p failed, rc=%d\n", pFilp, uCmd, (void *)ulArg, rc));
+ rc = -EINVAL;
+ }
+ RTMemFree(pHdr);
+
+ Log6(("VBoxDrvLinuxIOCtl: returns %d (pid=%d/%d)\n", rc, RTProcSelf(), current->pid));
+ return rc;
+}
+
+
+/**
+ * The SUPDRV IDC entry point.
+ *
+ * @returns VBox status code, see supdrvIDC.
+ * @param uReq The request code.
+ * @param pReq The request.
+ */
+int VBOXCALL SUPDrvLinuxIDC(uint32_t uReq, PSUPDRVIDCREQHDR pReq)
+{
+ PSUPDRVSESSION pSession;
+
+ /*
+ * Some quick validations.
+ */
+ if (RT_UNLIKELY(!RT_VALID_PTR(pReq)))
+ return VERR_INVALID_POINTER;
+
+ pSession = pReq->pSession;
+ if (pSession)
+ {
+ if (RT_UNLIKELY(!RT_VALID_PTR(pSession)))
+ return VERR_INVALID_PARAMETER;
+ if (RT_UNLIKELY(pSession->pDevExt != &g_DevExt))
+ return VERR_INVALID_PARAMETER;
+ }
+ else if (RT_UNLIKELY(uReq != SUPDRV_IDC_REQ_CONNECT))
+ return VERR_INVALID_PARAMETER;
+
+ /*
+ * Do the job.
+ */
+ return supdrvIDC(uReq, &g_DevExt, pSession, pReq);
+}
+EXPORT_SYMBOL(SUPDrvLinuxIDC);
+
+
+#if RTLNX_VER_MIN(5,0,0)
+
+/**
+ * Checks if the given module is one of our potential wrapper modules or not.
+ */
+static bool supdrvLinuxLdrIsPotentialWrapperModule(struct module const *pModule)
+{
+ if ( pModule
+ && strncmp(pModule->name, RT_STR_TUPLE("vbox_")) == 0)
+ return true;
+ return false;
+}
+
+/**
+ * Called when a kernel module changes state.
+ *
+ * We use this to listen for wrapper modules being loaded, since some evil
+ * bugger removed the find_module() export in 5.13.
+ */
+static int supdrvLinuxLdrModuleNotifyCallback(struct notifier_block *pBlock, unsigned long uModuleState, void *pvModule)
+{
+ struct module *pModule = (struct module *)pvModule;
+ switch (uModuleState)
+ {
+ case MODULE_STATE_UNFORMED: /* Setting up the module... */
+ break;
+
+ /*
+ * The module is about to have its ctors & init functions called.
+ *
+ * Add anything that looks like a wrapper module to our tracker list.
+ */
+ case MODULE_STATE_COMING:
+ if (supdrvLinuxLdrIsPotentialWrapperModule(pModule))
+ {
+ PSUPDRVLNXMODULE pTracker = (PSUPDRVLNXMODULE)RTMemAlloc(sizeof(*pTracker));
+ if (pTracker)
+ {
+ pTracker->pModule = pModule;
+ spin_lock(&g_supdrvLinuxWrapperModuleSpinlock);
+ RTListPrepend(&g_supdrvLinuxWrapperModuleList, &pTracker->ListEntry);
+ spin_unlock(&g_supdrvLinuxWrapperModuleSpinlock);
+ }
+ }
+ break;
+
+ case MODULE_STATE_LIVE:
+ break;
+
+ /*
+ * The module has been uninited and is going away.
+ *
+ * Remove the tracker entry for the module, if we have one.
+ */
+ case MODULE_STATE_GOING:
+ {
+ PSUPDRVLNXMODULE pCur;
+ spin_lock(&g_supdrvLinuxWrapperModuleSpinlock);
+ RTListForEach(&g_supdrvLinuxWrapperModuleList, pCur, SUPDRVLNXMODULE, ListEntry)
+ {
+ if (pCur->pModule == pModule)
+ {
+ RTListNodeRemove(&pCur->ListEntry);
+ spin_unlock(&g_supdrvLinuxWrapperModuleSpinlock);
+
+ pCur->pModule = NULL;
+ RTMemFree(pCur);
+
+ spin_lock(&g_supdrvLinuxWrapperModuleSpinlock); /* silly */
+ break;
+ }
+ }
+ spin_unlock(&g_supdrvLinuxWrapperModuleSpinlock);
+ break;
+ }
+ }
+ RT_NOREF(pBlock);
+ return NOTIFY_OK;
+}
+
+/**
+ * Replacement for find_module() that's no longer exported with 5.13.
+ */
+static struct module *supdrvLinuxLdrFindModule(const char *pszLnxModName)
+{
+ PSUPDRVLNXMODULE pCur;
+
+ spin_lock(&g_supdrvLinuxWrapperModuleSpinlock);
+ RTListForEach(&g_supdrvLinuxWrapperModuleList, pCur, SUPDRVLNXMODULE, ListEntry)
+ {
+ struct module * const pModule = pCur->pModule;
+ if ( pModule
+ && strcmp(pszLnxModName, pModule->name) == 0)
+ {
+ spin_unlock(&g_supdrvLinuxWrapperModuleSpinlock);
+ return pModule;
+ }
+ }
+ spin_unlock(&g_supdrvLinuxWrapperModuleSpinlock);
+ return NULL;
+}
+
+#endif /* >= 5.0.0 */
+
+
+/**
+ * Used by native wrapper modules, forwarding to supdrvLdrRegisterWrappedModule
+ * with device extension prepended to the argument list.
+ */
+SUPR0DECL(int) SUPDrvLinuxLdrRegisterWrappedModule(PCSUPLDRWRAPPEDMODULE pWrappedModInfo,
+ const char *pszLnxModName, void **phMod)
+{
+ AssertPtrReturn(pszLnxModName, VERR_INVALID_POINTER);
+ AssertReturn(*pszLnxModName, VERR_INVALID_NAME);
+
+ /* Locate the module structure for the caller so can later reference
+ and dereference it to prevent unloading while it is being used.
+
+ Before Linux v5.9 this could be done by address (__module_address()
+ or __module_text_address()), but someone (guess who) apparently on
+ a mission to make life miserable for out-of-tree modules or something,
+ decided it was only used by build-in code and unexported both of them.
+
+ I could find no init callouts getting a struct module pointer either,
+ nor any module name hint anywhere I could see. So, we're left with
+ hardcoding the module name via the compiler and pass it along to
+ SUPDrv so we can call find_module() here.
+
+ Sigh^2.
+
+ Update 5.13:
+ The find_module() and module_mutex symbols are no longer exported,
+ probably the doing of the same evil bugger mentioned above. So, we now
+ register a module notification callback and track the modules we're
+ interested in that way. */
+
+#if RTLNX_VER_MIN(5,0,0)
+ struct module *pLnxModule = supdrvLinuxLdrFindModule(pszLnxModName);
+ if (pLnxModule)
+ return supdrvLdrRegisterWrappedModule(&g_DevExt, pWrappedModInfo, pLnxModule, phMod);
+ printk("vboxdrv: supdrvLinuxLdrFindModule(%s) failed in SUPDrvLinuxLdrRegisterWrappedModule!\n", pszLnxModName);
+ return VERR_MODULE_NOT_FOUND;
+
+#elif RTLNX_VER_MIN(2,6,30)
+ if (mutex_lock_interruptible(&module_mutex) == 0)
+ {
+ struct module *pLnxModule = find_module(pszLnxModName);
+ mutex_unlock(&module_mutex);
+ if (pLnxModule)
+ return supdrvLdrRegisterWrappedModule(&g_DevExt, pWrappedModInfo, pLnxModule, phMod);
+ printk("vboxdrv: find_module(%s) failed in SUPDrvLinuxLdrRegisterWrappedModule!\n", pszLnxModName);
+ return VERR_MODULE_NOT_FOUND;
+ }
+ return VERR_INTERRUPTED;
+
+#else
+ printk("vboxdrv: wrapper modules are not supported on 2.6.29 and earlier. sorry.\n");
+ return VERR_NOT_SUPPORTED;
+#endif
+}
+EXPORT_SYMBOL(SUPDrvLinuxLdrRegisterWrappedModule);
+
+
+/**
+ * Used by native wrapper modules, forwarding to supdrvLdrDeregisterWrappedModule
+ * with device extension prepended to the argument list.
+ */
+SUPR0DECL(int) SUPDrvLinuxLdrDeregisterWrappedModule(PCSUPLDRWRAPPEDMODULE pWrappedModInfo, void **phMod)
+{
+ return supdrvLdrDeregisterWrappedModule(&g_DevExt, pWrappedModInfo, phMod);
+}
+EXPORT_SYMBOL(SUPDrvLinuxLdrDeregisterWrappedModule);
+
+
+RTCCUINTREG VBOXCALL supdrvOSChangeCR4(RTCCUINTREG fOrMask, RTCCUINTREG fAndMask)
+{
+#if RTLNX_VER_MIN(5,8,0)
+ unsigned long fSavedFlags;
+ local_irq_save(fSavedFlags);
+ RTCCUINTREG const uOld = cr4_read_shadow();
+ cr4_update_irqsoff(fOrMask, ~fAndMask); /* Same as this function, only it is not returning the old value. */
+ AssertMsg(cr4_read_shadow() == ((uOld & fAndMask) | fOrMask),
+ ("fOrMask=%#RTreg fAndMask=%#RTreg uOld=%#RTreg; new cr4=%#llx\n", fOrMask, fAndMask, uOld, cr4_read_shadow()));
+ local_irq_restore(fSavedFlags);
+#else
+# if RTLNX_VER_MIN(3,20,0)
+ RTCCUINTREG const uOld = this_cpu_read(cpu_tlbstate.cr4);
+# else
+ RTCCUINTREG const uOld = ASMGetCR4();
+# endif
+ RTCCUINTREG const uNew = (uOld & fAndMask) | fOrMask;
+ if (uNew != uOld)
+ {
+# if RTLNX_VER_MIN(3,20,0)
+ this_cpu_write(cpu_tlbstate.cr4, uNew);
+ __write_cr4(uNew);
+# else
+ ASMSetCR4(uNew);
+# endif
+ }
+#endif
+ return uOld;
+}
+
+
+void VBOXCALL supdrvOSCleanupSession(PSUPDRVDEVEXT pDevExt, PSUPDRVSESSION pSession)
+{
+ NOREF(pDevExt);
+ NOREF(pSession);
+}
+
+
+void VBOXCALL supdrvOSSessionHashTabInserted(PSUPDRVDEVEXT pDevExt, PSUPDRVSESSION pSession, void *pvUser)
+{
+ NOREF(pDevExt); NOREF(pSession); NOREF(pvUser);
+}
+
+
+void VBOXCALL supdrvOSSessionHashTabRemoved(PSUPDRVDEVEXT pDevExt, PSUPDRVSESSION pSession, void *pvUser)
+{
+ NOREF(pDevExt); NOREF(pSession); NOREF(pvUser);
+}
+
+
+/**
+ * Initializes any OS specific object creator fields.
+ */
+void VBOXCALL supdrvOSObjInitCreator(PSUPDRVOBJ pObj, PSUPDRVSESSION pSession)
+{
+ NOREF(pObj);
+ NOREF(pSession);
+}
+
+
+/**
+ * Checks if the session can access the object.
+ *
+ * @returns true if a decision has been made.
+ * @returns false if the default access policy should be applied.
+ *
+ * @param pObj The object in question.
+ * @param pSession The session wanting to access the object.
+ * @param pszObjName The object name, can be NULL.
+ * @param prc Where to store the result when returning true.
+ */
+bool VBOXCALL supdrvOSObjCanAccess(PSUPDRVOBJ pObj, PSUPDRVSESSION pSession, const char *pszObjName, int *prc)
+{
+ NOREF(pObj);
+ NOREF(pSession);
+ NOREF(pszObjName);
+ NOREF(prc);
+ return false;
+}
+
+
+bool VBOXCALL supdrvOSGetForcedAsyncTscMode(PSUPDRVDEVEXT pDevExt)
+{
+ return force_async_tsc != 0;
+}
+
+
+bool VBOXCALL supdrvOSAreCpusOfflinedOnSuspend(void)
+{
+ return true;
+}
+
+
+bool VBOXCALL supdrvOSAreTscDeltasInSync(void)
+{
+ return false;
+}
+
+
+int VBOXCALL supdrvOSLdrOpen(PSUPDRVDEVEXT pDevExt, PSUPDRVLDRIMAGE pImage, const char *pszFilename)
+{
+ NOREF(pDevExt); NOREF(pImage); NOREF(pszFilename);
+ return VERR_NOT_SUPPORTED;
+}
+
+
+int VBOXCALL supdrvOSLdrValidatePointer(PSUPDRVDEVEXT pDevExt, PSUPDRVLDRIMAGE pImage, void *pv,
+ const uint8_t *pbImageBits, const char *pszSymbol)
+{
+ NOREF(pDevExt); NOREF(pImage); NOREF(pv); NOREF(pbImageBits); NOREF(pszSymbol);
+ return VERR_NOT_SUPPORTED;
+}
+
+
+int VBOXCALL supdrvOSLdrLoad(PSUPDRVDEVEXT pDevExt, PSUPDRVLDRIMAGE pImage, const uint8_t *pbImageBits, PSUPLDRLOAD pReq)
+{
+ NOREF(pDevExt); NOREF(pImage); NOREF(pbImageBits); NOREF(pReq);
+ return VERR_NOT_SUPPORTED;
+}
+
+
+void VBOXCALL supdrvOSLdrUnload(PSUPDRVDEVEXT pDevExt, PSUPDRVLDRIMAGE pImage)
+{
+ NOREF(pDevExt); NOREF(pImage);
+}
+
+
+/** @def VBOX_WITH_NON_PROD_HACK_FOR_PERF_STACKS
+ * A very crude hack for debugging using perf and dtrace.
+ *
+ * DO ABSOLUTELY NOT ENABLE IN PRODUCTION BUILDS! DEVELOPMENT ONLY!!
+ * DO ABSOLUTELY NOT ENABLE IN PRODUCTION BUILDS! DEVELOPMENT ONLY!!
+ * DO ABSOLUTELY NOT ENABLE IN PRODUCTION BUILDS! DEVELOPMENT ONLY!!
+ *
+ */
+#if 0 || defined(DOXYGEN_RUNNING)
+# define VBOX_WITH_NON_PROD_HACK_FOR_PERF_STACKS
+#endif
+
+#if defined(VBOX_WITH_NON_PROD_HACK_FOR_PERF_STACKS) && defined(CONFIG_MODULES_TREE_LOOKUP)
+/** Whether g_pfnModTreeInsert and g_pfnModTreeRemove have been initialized.
+ * @remarks can still be NULL after init. */
+static volatile bool g_fLookedForModTreeFunctions = false;
+static void (*g_pfnModTreeInsert)(struct mod_tree_node *) = NULL; /**< __mod_tree_insert */
+static void (*g_pfnModTreeRemove)(struct mod_tree_node *) = NULL; /**< __mod_tree_remove */
+#endif
+
+
+void VBOXCALL supdrvOSLdrNotifyOpened(PSUPDRVDEVEXT pDevExt, PSUPDRVLDRIMAGE pImage, const char *pszFilename)
+{
+#ifdef VBOX_WITH_NON_PROD_HACK_FOR_PERF_STACKS /* Not for production use!! Debugging only! */
+ /*
+ * This trick stops working with 4.2 when CONFIG_MODULES_TREE_LOOKUP is
+ * defined. The module lookups are done via a tree structure and we
+ * cannot get at the root of it. :-(
+ */
+# ifdef CONFIG_KALLSYMS
+ size_t const cchName = strlen(pImage->szName);
+# endif
+ struct module *pMyMod, *pSelfMod, *pTestMod, *pTestModByName;
+ IPRT_LINUX_SAVE_EFL_AC();
+
+ pImage->pLnxModHack = NULL;
+
+# ifdef CONFIG_MODULES_TREE_LOOKUP
+ /*
+ * This is pretty naive, but works for 4.2 on arch linux. I don't think we
+ * can count on finding __mod_tree_remove in all kernel builds as it's not
+ * marked noinline like __mod_tree_insert.
+ */
+ if (!g_fLookedForModTreeFunctions)
+ {
+ unsigned long ulInsert = kallsyms_lookup_name("__mod_tree_insert");
+ unsigned long ulRemove = kallsyms_lookup_name("__mod_tree_remove");
+ if (!ulInsert || !ulRemove)
+ {
+ g_fLookedForModTreeFunctions = true;
+ printk(KERN_ERR "vboxdrv: failed to locate __mod_tree_insert and __mod_tree_remove.\n");
+ IPRT_LINUX_RESTORE_EFL_AC();
+ return;
+ }
+ *(unsigned long *)&g_pfnModTreeInsert = ulInsert;
+ *(unsigned long *)&g_pfnModTreeRemove = ulRemove;
+ ASMCompilerBarrier();
+ g_fLookedForModTreeFunctions = true;
+ }
+ else if (!g_pfnModTreeInsert || !g_pfnModTreeRemove)
+ return;
+#endif
+
+ /*
+ * Make sure we've found our own module, otherwise we cannot access the linked list.
+ */
+ mutex_lock(&module_mutex);
+ pSelfMod = find_module("vboxdrv");
+ mutex_unlock(&module_mutex);
+ if (!pSelfMod)
+ {
+ IPRT_LINUX_RESTORE_EFL_AC();
+ return;
+ }
+
+ /*
+ * Cook up a module structure for the image.
+ * We allocate symbol and string tables in the allocation and the module to keep things simple.
+ */
+# ifdef CONFIG_KALLSYMS
+ pMyMod = (struct module *)RTMemAllocZ(sizeof(*pMyMod)
+ + sizeof(Elf_Sym) * 3
+ + 1 + cchName * 2 + sizeof("_start") + sizeof("_end") + 4 );
+# else
+ pMyMod = (struct module *)RTMemAllocZ(sizeof(*pMyMod));
+# endif
+ if (pMyMod)
+ {
+ int rc = VINF_SUCCESS;
+# ifdef CONFIG_KALLSYMS
+ Elf_Sym *paSymbols = (Elf_Sym *)(pMyMod + 1);
+ char *pchStrTab = (char *)(paSymbols + 3);
+# endif
+
+ pMyMod->state = MODULE_STATE_LIVE;
+ INIT_LIST_HEAD(&pMyMod->list); /* just in case */
+
+ /* Perf only matches up files with a .ko extension (maybe .ko.gz),
+ so in order for this crap to work smoothly, we append .ko to the
+ module name and require the user to create symbolic links in
+ /lib/modules/`uname -r`:
+ for i in VMMR0.r0 VBoxDDR0.r0 VBoxDD2R0.r0; do
+ sudo ln -s /mnt/scratch/vbox/svn/trunk/out/linux.amd64/debug/bin/$i /lib/modules/`uname -r`/$i.ko;
+ done */
+ RTStrPrintf(pMyMod->name, sizeof(pMyMod->name), "%s", pImage->szName);
+
+ /* sysfs bits. */
+ INIT_LIST_HEAD(&pMyMod->mkobj.kobj.entry); /* rest of kobj is already zeroed, hopefully never accessed... */
+ pMyMod->mkobj.mod = pMyMod;
+ pMyMod->mkobj.drivers_dir = NULL;
+ pMyMod->mkobj.mp = NULL;
+ pMyMod->mkobj.kobj_completion = NULL;
+
+ pMyMod->modinfo_attrs = NULL; /* hopefully not accessed after setup. */
+ pMyMod->holders_dir = NULL; /* hopefully not accessed. */
+ pMyMod->version = "N/A";
+ pMyMod->srcversion = "N/A";
+
+ /* We export no symbols. */
+ pMyMod->num_syms = 0;
+ pMyMod->syms = NULL;
+ pMyMod->crcs = NULL;
+
+ pMyMod->num_gpl_syms = 0;
+ pMyMod->gpl_syms = NULL;
+ pMyMod->gpl_crcs = NULL;
+
+ pMyMod->num_gpl_future_syms = 0;
+ pMyMod->gpl_future_syms = NULL;
+ pMyMod->gpl_future_crcs = NULL;
+
+# if CONFIG_UNUSED_SYMBOLS
+ pMyMod->num_unused_syms = 0;
+ pMyMod->unused_syms = NULL;
+ pMyMod->unused_crcs = NULL;
+
+ pMyMod->num_unused_gpl_syms = 0;
+ pMyMod->unused_gpl_syms = NULL;
+ pMyMod->unused_gpl_crcs = NULL;
+# endif
+ /* No kernel parameters either. */
+ pMyMod->kp = NULL;
+ pMyMod->num_kp = 0;
+
+# ifdef CONFIG_MODULE_SIG
+ /* Pretend ok signature. */
+ pMyMod->sig_ok = true;
+# endif
+ /* No exception table. */
+ pMyMod->num_exentries = 0;
+ pMyMod->extable = NULL;
+
+ /* No init function */
+ pMyMod->init = NULL;
+ pMyMod->module_init = NULL;
+ pMyMod->init_size = 0;
+ pMyMod->init_ro_size = 0;
+ pMyMod->init_text_size = 0;
+
+ /* The module address and size. It's all text. */
+ pMyMod->module_core = pImage->pvImage;
+ pMyMod->core_size = pImage->cbImageBits;
+ pMyMod->core_text_size = pImage->cbImageBits;
+ pMyMod->core_ro_size = pImage->cbImageBits;
+
+#ifdef CONFIG_MODULES_TREE_LOOKUP
+ /* Fill in the self pointers for the tree nodes. */
+ pMyMod->mtn_core.mod = pMyMod;
+ pMyMod->mtn_init.mod = pMyMod;
+#endif
+ /* They invented the tained bit for us, didn't they? */
+ pMyMod->taints = 1;
+
+# ifdef CONFIG_GENERIC_BUGS
+ /* No BUGs in our modules. */
+ pMyMod->num_bugs = 0;
+ INIT_LIST_HEAD(&pMyMod->bug_list);
+ pMyMod->bug_table = NULL;
+# endif
+
+# ifdef CONFIG_KALLSYMS
+ /* The core stuff is documented as only used when loading. So just zero them. */
+ pMyMod->core_num_syms = 0;
+ pMyMod->core_symtab = NULL;
+ pMyMod->core_strtab = NULL;
+
+ /* Construct a symbol table with start and end symbols.
+ Note! We don't have our own symbol table at this point, image bit
+ are not uploaded yet! */
+ pMyMod->num_symtab = 3;
+ pMyMod->symtab = paSymbols;
+ pMyMod->strtab = pchStrTab;
+ RT_ZERO(paSymbols[0]);
+ pchStrTab[0] = '\0';
+ paSymbols[1].st_name = 1;
+ paSymbols[2].st_name = 2 + RTStrPrintf(&pchStrTab[paSymbols[1].st_name], cchName + sizeof("_start"),
+ "%s_start", pImage->szName);
+ RTStrPrintf(&pchStrTab[paSymbols[2].st_name], cchName + sizeof("_end"), "%s_end", pImage->szName);
+ paSymbols[1].st_info = 't';
+ paSymbols[2].st_info = 'b';
+ paSymbols[1].st_other = 0;
+ paSymbols[2].st_other = 0;
+ paSymbols[1].st_shndx = 0;
+ paSymbols[2].st_shndx = 0;
+ paSymbols[1].st_value = (uintptr_t)pImage->pvImage;
+ paSymbols[2].st_value = (uintptr_t)pImage->pvImage + pImage->cbImageBits - 1;
+ paSymbols[1].st_size = pImage->cbImageBits - 1;
+ paSymbols[2].st_size = 1;
+# endif
+ /* No arguments, but seems its always non-NULL so put empty string there. */
+ pMyMod->args = "";
+
+# ifdef CONFIG_SMP
+ /* No per CPU data. */
+ pMyMod->percpu = NULL;
+ pMyMod->percpu_size = 0;
+# endif
+# ifdef CONFIG_TRACEPOINTS
+ /* No tracepoints we like to share. */
+ pMyMod->num_tracepoints = 0;
+ pMyMod->tracepoints_ptrs = NULL;
+#endif
+# ifdef HAVE_JUMP_LABEL
+ /* No jump lable stuff either. */
+ pMyMod->jump_entries = NULL;
+ pMyMod->num_jump_entries = 0;
+# endif
+# ifdef CONFIG_TRACING
+ pMyMod->num_trace_bprintk_fmt = 0;
+ pMyMod->trace_bprintk_fmt_start = NULL;
+# endif
+# ifdef CONFIG_EVENT_TRACING
+ pMyMod->trace_events = NULL;
+ pMyMod->num_trace_events = 0;
+# endif
+# ifdef CONFIG_FTRACE_MCOUNT_RECORD
+ pMyMod->num_ftrace_callsites = 0;
+ pMyMod->ftrace_callsites = NULL;
+# endif
+# ifdef CONFIG_MODULE_UNLOAD
+ /* Dependency lists, not worth sharing */
+ INIT_LIST_HEAD(&pMyMod->source_list);
+ INIT_LIST_HEAD(&pMyMod->target_list);
+
+ /* Nobody waiting and no exit function. */
+# if RTLNX_VER_MAX(3,13,0)
+ pMyMod->waiter = NULL;
+# endif
+ pMyMod->exit = NULL;
+
+ /* References, very important as we must not allow the module
+ to be unloaded using rmmod. */
+# if RTLNX_VER_MIN(3,19,0)
+ atomic_set(&pMyMod->refcnt, 42);
+# else
+ pMyMod->refptr = alloc_percpu(struct module_ref);
+ if (pMyMod->refptr)
+ {
+ int iCpu;
+ for_each_possible_cpu(iCpu)
+ {
+ per_cpu_ptr(pMyMod->refptr, iCpu)->decs = 0;
+ per_cpu_ptr(pMyMod->refptr, iCpu)->incs = 1;
+ }
+ }
+ else
+ rc = VERR_NO_MEMORY;
+# endif
+# endif
+# ifdef CONFIG_CONSTRUCTORS
+ /* No constructors. */
+ pMyMod->ctors = NULL;
+ pMyMod->num_ctors = 0;
+# endif
+ if (RT_SUCCESS(rc))
+ {
+ bool fIsModText;
+
+ /*
+ * Add the module to the list.
+ */
+ mutex_lock(&module_mutex);
+ list_add_rcu(&pMyMod->list, &pSelfMod->list);
+ pImage->pLnxModHack = pMyMod;
+# ifdef CONFIG_MODULES_TREE_LOOKUP
+ g_pfnModTreeInsert(&pMyMod->mtn_core); /* __mod_tree_insert */
+# endif
+ mutex_unlock(&module_mutex);
+
+ /*
+ * Test it.
+ */
+ mutex_lock(&module_mutex);
+ pTestModByName = find_module(pMyMod->name);
+ pTestMod = __module_address((uintptr_t)pImage->pvImage + pImage->cbImageBits / 4);
+ fIsModText = __module_text_address((uintptr_t)pImage->pvImage + pImage->cbImageBits / 2);
+ mutex_unlock(&module_mutex);
+ if ( pTestMod == pMyMod
+ && pTestModByName == pMyMod
+ && fIsModText)
+ printk(KERN_ERR "vboxdrv: fake module works for '%s' (%#lx to %#lx)\n",
+ pMyMod->name, (unsigned long)paSymbols[1].st_value, (unsigned long)paSymbols[2].st_value);
+ else
+ printk(KERN_ERR "vboxdrv: failed to find fake module (pTestMod=%p, pTestModByName=%p, pMyMod=%p, fIsModText=%d)\n",
+ pTestMod, pTestModByName, pMyMod, fIsModText);
+ }
+ else
+ RTMemFree(pMyMod);
+ }
+
+ IPRT_LINUX_RESTORE_EFL_AC();
+#else
+ pImage->pLnxModHack = NULL;
+#endif
+ NOREF(pDevExt); NOREF(pImage);
+}
+
+
+void VBOXCALL supdrvOSLdrNotifyUnloaded(PSUPDRVDEVEXT pDevExt, PSUPDRVLDRIMAGE pImage)
+{
+#ifdef VBOX_WITH_NON_PROD_HACK_FOR_PERF_STACKS /* Not for production use!! Debugging only! */
+ struct module *pMyMod = pImage->pLnxModHack;
+ pImage->pLnxModHack = NULL;
+ if (pMyMod)
+ {
+ /*
+ * Remove the fake module list entry and free it.
+ */
+ IPRT_LINUX_SAVE_EFL_AC();
+ mutex_lock(&module_mutex);
+ list_del_rcu(&pMyMod->list);
+# ifdef CONFIG_MODULES_TREE_LOOKUP
+ g_pfnModTreeRemove(&pMyMod->mtn_core);
+# endif
+ synchronize_sched();
+ mutex_unlock(&module_mutex);
+
+# if RTLNX_VER_MAX(3,19,0)
+ free_percpu(pMyMod->refptr);
+# endif
+ RTMemFree(pMyMod);
+ IPRT_LINUX_RESTORE_EFL_AC();
+ }
+
+#else
+ Assert(pImage->pLnxModHack == NULL);
+#endif
+ NOREF(pDevExt); NOREF(pImage);
+}
+
+
+int VBOXCALL supdrvOSLdrQuerySymbol(PSUPDRVDEVEXT pDevExt, PSUPDRVLDRIMAGE pImage,
+ const char *pszSymbol, size_t cchSymbol, void **ppvSymbol)
+{
+#ifdef VBOX_WITH_NON_PROD_HACK_FOR_PERF_STACKS
+# error "implement me!"
+#endif
+ RT_NOREF(pDevExt, pImage, pszSymbol, cchSymbol, ppvSymbol);
+ return VERR_WRONG_ORDER;
+}
+
+
+void VBOXCALL supdrvOSLdrRetainWrapperModule(PSUPDRVDEVEXT pDevExt, PSUPDRVLDRIMAGE pImage)
+{
+ struct module *pLnxMod = (struct module *)pImage->pvWrappedNative;
+ Assert(!pImage->fLnxWrapperRef);
+ AssertReturnVoid(pLnxMod);
+ pImage->fLnxWrapperRef = try_module_get(pLnxMod);
+ RT_NOREF(pDevExt);
+}
+
+
+void VBOXCALL supdrvOSLdrReleaseWrapperModule(PSUPDRVDEVEXT pDevExt, PSUPDRVLDRIMAGE pImage)
+{
+ if (pImage->fLnxWrapperRef)
+ {
+ struct module *pLnxMod = (struct module *)pImage->pvWrappedNative;
+ pImage->fLnxWrapperRef = false;
+ module_put(pLnxMod);
+ }
+ RT_NOREF(pDevExt);
+}
+
+
+#ifdef SUPDRV_WITH_MSR_PROBER
+
+int VBOXCALL supdrvOSMsrProberRead(uint32_t uMsr, RTCPUID idCpu, uint64_t *puValue)
+{
+# ifdef SUPDRV_LINUX_HAS_SAFE_MSR_API
+ uint32_t u32Low, u32High;
+ int rc;
+
+ IPRT_LINUX_SAVE_EFL_AC();
+ if (idCpu == NIL_RTCPUID)
+ rc = rdmsr_safe(uMsr, &u32Low, &u32High);
+ else if (RTMpIsCpuOnline(idCpu))
+ rc = rdmsr_safe_on_cpu(idCpu, uMsr, &u32Low, &u32High);
+ else
+ return VERR_CPU_OFFLINE;
+ IPRT_LINUX_RESTORE_EFL_AC();
+ if (rc == 0)
+ {
+ *puValue = RT_MAKE_U64(u32Low, u32High);
+ return VINF_SUCCESS;
+ }
+ return VERR_ACCESS_DENIED;
+# else
+ return VERR_NOT_SUPPORTED;
+# endif
+}
+
+
+int VBOXCALL supdrvOSMsrProberWrite(uint32_t uMsr, RTCPUID idCpu, uint64_t uValue)
+{
+# ifdef SUPDRV_LINUX_HAS_SAFE_MSR_API
+ int rc;
+
+ IPRT_LINUX_SAVE_EFL_AC();
+ if (idCpu == NIL_RTCPUID)
+ rc = wrmsr_safe(uMsr, RT_LODWORD(uValue), RT_HIDWORD(uValue));
+ else if (RTMpIsCpuOnline(idCpu))
+ rc = wrmsr_safe_on_cpu(idCpu, uMsr, RT_LODWORD(uValue), RT_HIDWORD(uValue));
+ else
+ return VERR_CPU_OFFLINE;
+ IPRT_LINUX_RESTORE_EFL_AC();
+
+ if (rc == 0)
+ return VINF_SUCCESS;
+ return VERR_ACCESS_DENIED;
+# else
+ return VERR_NOT_SUPPORTED;
+# endif
+}
+
+# ifdef SUPDRV_LINUX_HAS_SAFE_MSR_API
+/**
+ * Worker for supdrvOSMsrProberModify.
+ */
+static DECLCALLBACK(void) supdrvLnxMsrProberModifyOnCpu(RTCPUID idCpu, void *pvUser1, void *pvUser2)
+{
+ PSUPMSRPROBER pReq = (PSUPMSRPROBER)pvUser1;
+ register uint32_t uMsr = pReq->u.In.uMsr;
+ bool const fFaster = pReq->u.In.enmOp == SUPMSRPROBEROP_MODIFY_FASTER;
+ uint64_t uBefore;
+ uint64_t uWritten;
+ uint64_t uAfter;
+ int rcBefore, rcWrite, rcAfter, rcRestore;
+ RTCCUINTREG fOldFlags;
+
+ /* Initialize result variables. */
+ uBefore = uWritten = uAfter = 0;
+ rcWrite = rcAfter = rcRestore = -EIO;
+
+ /*
+ * Do the job.
+ */
+ fOldFlags = ASMIntDisableFlags();
+ ASMCompilerBarrier(); /* paranoia */
+ if (!fFaster)
+ ASMWriteBackAndInvalidateCaches();
+
+ rcBefore = rdmsrl_safe(uMsr, &uBefore);
+ if (rcBefore >= 0)
+ {
+ register uint64_t uRestore = uBefore;
+ uWritten = uRestore;
+ uWritten &= pReq->u.In.uArgs.Modify.fAndMask;
+ uWritten |= pReq->u.In.uArgs.Modify.fOrMask;
+
+ rcWrite = wrmsr_safe(uMsr, RT_LODWORD(uWritten), RT_HIDWORD(uWritten));
+ rcAfter = rdmsrl_safe(uMsr, &uAfter);
+ rcRestore = wrmsr_safe(uMsr, RT_LODWORD(uRestore), RT_HIDWORD(uRestore));
+
+ if (!fFaster)
+ {
+ ASMWriteBackAndInvalidateCaches();
+ ASMReloadCR3();
+ ASMNopPause();
+ }
+ }
+
+ ASMCompilerBarrier(); /* paranoia */
+ ASMSetFlags(fOldFlags);
+
+ /*
+ * Write out the results.
+ */
+ pReq->u.Out.uResults.Modify.uBefore = uBefore;
+ pReq->u.Out.uResults.Modify.uWritten = uWritten;
+ pReq->u.Out.uResults.Modify.uAfter = uAfter;
+ pReq->u.Out.uResults.Modify.fBeforeGp = rcBefore != 0;
+ pReq->u.Out.uResults.Modify.fModifyGp = rcWrite != 0;
+ pReq->u.Out.uResults.Modify.fAfterGp = rcAfter != 0;
+ pReq->u.Out.uResults.Modify.fRestoreGp = rcRestore != 0;
+ RT_ZERO(pReq->u.Out.uResults.Modify.afReserved);
+}
+# endif
+
+
+int VBOXCALL supdrvOSMsrProberModify(RTCPUID idCpu, PSUPMSRPROBER pReq)
+{
+# ifdef SUPDRV_LINUX_HAS_SAFE_MSR_API
+ if (idCpu == NIL_RTCPUID)
+ {
+ supdrvLnxMsrProberModifyOnCpu(idCpu, pReq, NULL);
+ return VINF_SUCCESS;
+ }
+ return RTMpOnSpecific(idCpu, supdrvLnxMsrProberModifyOnCpu, pReq, NULL);
+# else
+ return VERR_NOT_SUPPORTED;
+# endif
+}
+
+#endif /* SUPDRV_WITH_MSR_PROBER */
+
+
+/**
+ * Converts a supdrv error code to an linux error code.
+ *
+ * @returns corresponding linux error code.
+ * @param rc IPRT status code.
+ */
+static int VBoxDrvLinuxErr2LinuxErr(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;
+ case VERR_IDT_FAILED: return -1000;
+ }
+
+ return -EPERM;
+}
+
+
+SUPR0DECL(int) SUPR0HCPhysToVirt(RTHCPHYS HCPhys, void **ppv)
+{
+ AssertReturn(!(HCPhys & PAGE_OFFSET_MASK), VERR_INVALID_POINTER);
+ AssertReturn(HCPhys != NIL_RTHCPHYS, VERR_INVALID_POINTER);
+ /* Would've like to use valid_phys_addr_range for this test, but it isn't exported. */
+ AssertReturn((HCPhys | PAGE_OFFSET_MASK) < __pa(high_memory), VERR_INVALID_POINTER);
+ *ppv = phys_to_virt(HCPhys);
+ return VINF_SUCCESS;
+}
+SUPR0_EXPORT_SYMBOL(SUPR0HCPhysToVirt);
+
+
+RTDECL(int) SUPR0PrintfV(const char *pszFormat, va_list va)
+{
+ char szMsg[512];
+ IPRT_LINUX_SAVE_EFL_AC();
+
+ RTStrPrintfV(szMsg, sizeof(szMsg) - 1, pszFormat, va);
+ szMsg[sizeof(szMsg) - 1] = '\0';
+
+ printk("%s", szMsg);
+
+ IPRT_LINUX_RESTORE_EFL_AC();
+ return 0;
+}
+SUPR0_EXPORT_SYMBOL(SUPR0PrintfV);
+
+
+SUPR0DECL(uint32_t) SUPR0GetKernelFeatures(void)
+{
+ uint32_t fFlags = 0;
+ /* Note! bird 2023-10-20: Apparently, with CONFIG_PAX_KERNEXEC these days,
+ not only is the regular GDT read-only, but the one returned by
+ get_current_gdt_rw() is also read-only despite the name.
+
+ We don't know exactly when this started, or if it was always like
+ this, but getting hold of the relevant patches isn't all that
+ straight forward any longer it seems (which is weird for linux
+ patches), so, we've just enabled slow-mode for all PAX_KERNEXEC
+ kernels regardless of kernel version.
+
+ Looking at grsecurity patch for 4.9.9, it looks like the writable
+ GDT stuff never worked with PaX/grsec.
+ */
+#ifdef CONFIG_PAX_KERNEXEC
+ fFlags |= SUPKERNELFEATURES_GDT_READ_ONLY;
+#elif RTLNX_VER_MIN(4,12,0)
+ fFlags |= SUPKERNELFEATURES_GDT_NEED_WRITABLE;
+#endif
+
+#if defined(VBOX_STRICT) || defined(VBOX_WITH_EFLAGS_AC_SET_IN_VBOXDRV)
+ fFlags |= SUPKERNELFEATURES_SMAP;
+#elif defined(CONFIG_X86_SMAP)
+ if (ASMGetCR4() & X86_CR4_SMAP)
+ fFlags |= SUPKERNELFEATURES_SMAP;
+#endif
+ return fFlags;
+}
+SUPR0_EXPORT_SYMBOL(SUPR0GetKernelFeatures);
+
+
+SUPR0DECL(bool) SUPR0FpuBegin(bool fCtxHook)
+{
+ RT_NOREF(fCtxHook);
+#if RTLNX_VER_MIN(4,19,0) /* Going back to 4.19.0 for better coverage, we
+ probably only need 5.17.7+ in the end. */
+ /*
+ * HACK ALERT!
+ *
+ * We'd like to use the old __kernel_fpu_begin() API which was removed in
+ * early 2019, because we typically run with preemption enabled and have an
+ * preemption hook installed which will call kernel_fpu_end() in case we're
+ * scheduled out after getting in here. The preemption hook is almost
+ * useless if we run with preemption disabled.
+ *
+ * For the case where the kernel does not have preemption hooks, we get here
+ * with preemption already disabled and one more count doesn't make any
+ * difference.
+ *
+ * So, after the kernel_fpu_begin() call we undo the implicit preempt_disable()
+ * call it does, so the preemption hook can do its work and the VBox user has
+ * a more responsive system.
+ *
+ * See @bugref{10209#c12} and onwards for more details.
+ */
+ Assert(fCtxHook || !RTThreadPreemptIsEnabled(NIL_RTTHREAD));
+ kernel_fpu_begin();
+# if 0 /* Always do it for now for better test coverage. */
+ if (fCtxHook)
+# endif
+ preempt_enable();
+ return false; /** @todo Not sure if we have license to use any extended state, or
+ * if we're limited to the SSE & x87 FPU. If it's the former,
+ * we should return @a true and the caller can skip
+ * saving+restoring the host state and save some time. */
+#else
+ return false;
+#endif
+}
+SUPR0_EXPORT_SYMBOL(SUPR0FpuBegin);
+
+
+SUPR0DECL(void) SUPR0FpuEnd(bool fCtxHook)
+{
+ RT_NOREF(fCtxHook);
+#if RTLNX_VER_MIN(4,19,0)
+ /* HACK ALERT! See SUPR0FpuBegin for an explanation of this. */
+ Assert(!RTThreadPreemptIsEnabled(NIL_RTTHREAD));
+# if 0 /* Always do it for now for better test coverage. */
+ if (fCtxHook)
+# endif
+ preempt_disable();
+ kernel_fpu_end();
+#endif
+}
+SUPR0_EXPORT_SYMBOL(SUPR0FpuEnd);
+
+
+int VBOXCALL supdrvOSGetCurrentGdtRw(RTHCUINTPTR *pGdtRw)
+{
+#if RTLNX_VER_MIN(4,12,0) && !defined(CONFIG_PAX_KERNEXEC)
+ *pGdtRw = (RTHCUINTPTR)get_current_gdt_rw();
+ return VINF_SUCCESS;
+#else
+ return VERR_NOT_IMPLEMENTED;
+#endif
+}
+
+
+module_init(VBoxDrvLinuxInit);
+module_exit(VBoxDrvLinuxUnload);
+
+MODULE_AUTHOR(VBOX_VENDOR);
+MODULE_DESCRIPTION(VBOX_PRODUCT " Support Driver");
+MODULE_LICENSE("GPL");
+#ifdef MODULE_VERSION
+MODULE_VERSION(VBOX_VERSION_STRING " r" RT_XSTR(VBOX_SVN_REV) " (" RT_XSTR(SUPDRV_IOC_VERSION) ")");
+#endif
+
+module_param(force_async_tsc, int, 0444);
+MODULE_PARM_DESC(force_async_tsc, "force the asynchronous TSC mode");
+
diff --git a/src/VBox/HostDrivers/Support/linux/SUPDrv-linux.mod.c b/src/VBox/HostDrivers/Support/linux/SUPDrv-linux.mod.c
new file mode 100644
index 00000000..4bb02712
--- /dev/null
+++ b/src/VBox/HostDrivers/Support/linux/SUPDrv-linux.mod.c
@@ -0,0 +1,97 @@
+/* $Id: SUPDrv-linux.mod.c $ */
+/** @file
+ * VBoxDrv - The VirtualBox Support Driver - Autogenerated Linux code.
+ *
+ * This is checked in to assist syntax checking the module.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+#include "SUPDrvInternal.h" /* for KBUILD_STR */
+#include "the-linux-kernel.h"
+#include <linux/vermagic.h>
+
+MODULE_INFO(vermagic, VERMAGIC_STRING);
+
+#undef unix
+struct module __this_module
+__attribute__((section(".gnu.linkonce.this_module"))) = {
+ .name = __stringify(KBUILD_MODNAME),
+ .init = init_module,
+#ifdef CONFIG_MODULE_UNLOAD
+ .exit = cleanup_module,
+#endif
+};
+
+static const struct modversion_info ____versions[]
+__attribute_used__
+__attribute__((section("__versions"))) = {
+ { 0, "cleanup_module" },
+ { 0, "init_module" },
+ { 0, "struct_module" },
+ { 0, "strpbrk" },
+ { 0, "__kmalloc" },
+ { 0, "mem_map" },
+ { 0, "vmalloc" },
+ { 0, "malloc_sizes" },
+ { 0, "vfree" },
+ { 0, "change_page_attr" },
+ { 0, "__might_sleep" },
+ { 0, "remap_page_range" },
+ { 0, "__alloc_pages" },
+ { 0, "printk" },
+ { 0, "__PAGE_KERNEL" },
+ { 0, "rwsem_wake" },
+ { 0, "copy_to_user" },
+ { 0, "preempt_schedule" },
+ { 0, "contig_page_data" },
+ { 0, "do_mmap_pgoff" },
+ { 0, "find_vma" },
+ { 0, "kmem_cache_alloc" },
+ { 0, "__free_pages" },
+ { 0, "do_munmap" },
+ { 0, "get_user_pages" },
+ { 0, "vsnprintf" },
+ { 0, "kfree" },
+ { 0, "memcpy" },
+ { 0, "put_page" },
+ { 0, "__up_wakeup" },
+ { 0, "__down_failed" },
+ { 0, "copy_from_user" },
+ { 0, "rwsem_down_read_failed" },
+};
+
+static const char __module_depends[]
+__attribute_used__
+__attribute__((section(".modinfo"))) =
+"depends=";
+
diff --git a/src/VBox/HostDrivers/Support/linux/SUPLib-linux.cpp b/src/VBox/HostDrivers/Support/linux/SUPLib-linux.cpp
new file mode 100644
index 00000000..5bdcda63
--- /dev/null
+++ b/src/VBox/HostDrivers/Support/linux/SUPLib-linux.cpp
@@ -0,0 +1,388 @@
+/* $Id: SUPLib-linux.cpp $ */
+/** @file
+ * VirtualBox Support Library - GNU/Linux specific parts.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#define LOG_GROUP LOG_GROUP_SUP
+#ifdef IN_SUP_HARDENED_R3
+# undef DEBUG /* Warning: disables RT_STRICT */
+# undef RT_STRICT
+# ifndef LOG_DISABLED
+# define LOG_DISABLED
+# endif
+# define RTLOG_REL_DISABLED
+# include <iprt/log.h>
+#endif
+
+#include <sys/fcntl.h>
+#include <sys/ioctl.h>
+#include <sys/mman.h>
+#include <errno.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <malloc.h>
+
+#include <VBox/log.h>
+#include <VBox/sup.h>
+#include <iprt/path.h>
+#include <iprt/assert.h>
+#include <VBox/types.h>
+#include <iprt/string.h>
+#include <iprt/system.h>
+#include <VBox/err.h>
+#include <VBox/param.h>
+#include "../SUPLibInternal.h"
+#include "../SUPDrvIOC.h"
+
+
+/*********************************************************************************************************************************
+* Defined Constants And Macros *
+*********************************************************************************************************************************/
+/** System device name. */
+#define DEVICE_NAME_SYS "/dev/vboxdrv"
+/** User device name. */
+#define DEVICE_NAME_USR "/dev/vboxdrvu"
+
+/* define MADV_DONTFORK if it's missing from the system headers. */
+#ifndef MADV_DONTFORK
+# define MADV_DONTFORK 10
+#endif
+
+
+
+DECLHIDDEN(int) suplibOsInit(PSUPLIBDATA pThis, bool fPreInited, uint32_t fFlags, SUPINITOP *penmWhat, PRTERRINFO pErrInfo)
+{
+ RT_NOREF2(penmWhat, pErrInfo);
+
+ /*
+ * Nothing to do if pre-inited.
+ */
+ if (fPreInited)
+ return VINF_SUCCESS;
+ Assert(pThis->hDevice == (intptr_t)NIL_RTFILE);
+
+ /*
+ * Check if madvise works.
+ */
+ void *pv = mmap(NULL, PAGE_SIZE, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
+ if (pv == MAP_FAILED)
+ return VERR_NO_MEMORY;
+ pThis->fSysMadviseWorks = (0 == madvise(pv, PAGE_SIZE, MADV_DONTFORK));
+ munmap(pv, PAGE_SIZE);
+
+ /*
+ * Driverless?
+ */
+ if (fFlags & SUPR3INIT_F_DRIVERLESS)
+ {
+ pThis->fDriverless = true;
+ return VINF_SUCCESS;
+ }
+
+ /*
+ * Try open the device.
+ */
+ const char *pszDeviceNm = fFlags & SUPR3INIT_F_UNRESTRICTED ? DEVICE_NAME_SYS : DEVICE_NAME_USR;
+ int hDevice = open(pszDeviceNm, O_RDWR, 0);
+ if (hDevice < 0)
+ {
+ /*
+ * Try load the device.
+ */
+ hDevice = open(pszDeviceNm, O_RDWR, 0);
+ if (hDevice < 0)
+ {
+ int rc;
+ switch (errno)
+ {
+ case ENXIO: /* see man 2 open, ENODEV is actually a kernel bug */
+ case ENODEV: rc = VERR_VM_DRIVER_LOAD_ERROR; break;
+ case EPERM:
+ case EACCES: rc = VERR_VM_DRIVER_NOT_ACCESSIBLE; break;
+ case ENOENT: rc = VERR_VM_DRIVER_NOT_INSTALLED; break;
+ default: rc = VERR_VM_DRIVER_OPEN_ERROR; break;
+ }
+ if (fFlags & SUPR3INIT_F_DRIVERLESS_MASK)
+ {
+ LogRel(("Failed to open \"%s\", errno=%d, rc=%Rrc - Switching to driverless mode.\n", pszDeviceNm, errno, rc));
+ pThis->fDriverless = true;
+ return VINF_SUCCESS;
+ }
+ LogRel(("Failed to open \"%s\", errno=%d, rc=%Rrc\n", pszDeviceNm, errno, rc));
+ return rc;
+ }
+ }
+
+ /*
+ * Mark the file handle close on exec.
+ */
+ if (fcntl(hDevice, F_SETFD, FD_CLOEXEC) == -1)
+ {
+ close(hDevice);
+#ifdef IN_SUP_HARDENED_R3
+ return VERR_INTERNAL_ERROR;
+#else
+ return RTErrConvertFromErrno(errno);
+#endif
+ }
+
+ /*
+ * We're done.
+ */
+ pThis->hDevice = hDevice;
+ pThis->fUnrestricted = RT_BOOL(fFlags & SUPR3INIT_F_UNRESTRICTED);
+ return VINF_SUCCESS;
+}
+
+
+DECLHIDDEN(int) suplibOsTerm(PSUPLIBDATA pThis)
+{
+ /*
+ * Close the device if it's actually open.
+ */
+ if (pThis->hDevice != (intptr_t)NIL_RTFILE)
+ {
+ if (close(pThis->hDevice))
+ AssertFailed();
+ pThis->hDevice = (intptr_t)NIL_RTFILE;
+ }
+
+ return 0;
+}
+
+
+#ifndef IN_SUP_HARDENED_R3
+
+DECLHIDDEN(int) suplibOsInstall(void)
+{
+ // nothing to do on Linux
+ return VERR_NOT_IMPLEMENTED;
+}
+
+
+DECLHIDDEN(int) suplibOsUninstall(void)
+{
+ // nothing to do on Linux
+ return VERR_NOT_IMPLEMENTED;
+}
+
+
+DECLHIDDEN(int) suplibOsIOCtl(PSUPLIBDATA pThis, uintptr_t uFunction, void *pvReq, size_t cbReq)
+{
+ AssertMsg(pThis->hDevice != (intptr_t)NIL_RTFILE, ("SUPLIB not initiated successfully!\n"));
+ NOREF(cbReq);
+
+ /*
+ * Issue device iocontrol.
+ */
+ if (RT_LIKELY(ioctl(pThis->hDevice, uFunction, pvReq) >= 0))
+ return VINF_SUCCESS;
+
+ /* This is the reverse operation of the one found in SUPDrv-linux.c */
+ switch (errno)
+ {
+ case EACCES: return VERR_GENERAL_FAILURE;
+ case EINVAL: return VERR_INVALID_PARAMETER;
+ case EILSEQ: return VERR_INVALID_MAGIC;
+ case ENXIO: return VERR_INVALID_HANDLE;
+ case EFAULT: return VERR_INVALID_POINTER;
+ case ENOLCK: return VERR_LOCK_FAILED;
+ case EEXIST: return VERR_ALREADY_LOADED;
+ case EPERM: return VERR_PERMISSION_DENIED;
+ case ENOSYS: return VERR_VERSION_MISMATCH;
+ case 1000: return VERR_IDT_FAILED;
+ }
+
+ return RTErrConvertFromErrno(errno);
+}
+
+
+DECLHIDDEN(int) suplibOsIOCtlFast(PSUPLIBDATA pThis, uintptr_t uFunction, uintptr_t idCpu)
+{
+ int rc = ioctl(pThis->hDevice, uFunction, idCpu);
+ if (rc == -1)
+ rc = -errno;
+ return rc;
+}
+
+
+DECLHIDDEN(int) suplibOsPageAlloc(PSUPLIBDATA pThis, size_t cPages, uint32_t fFlags, void **ppvPages)
+{
+ /*
+ * If large pages are requested, try use the MAP_HUGETBL flags. This takes
+ * pages from the reserved huge page pool (see sysctl vm.nr_hugepages) and
+ * is typically not configured. Also, when the pool is exhausted we get
+ * ENOMEM back at us. So, when it fails try again w/o MAP_HUGETLB.
+ */
+ int fMmap = MAP_PRIVATE | MAP_ANONYMOUS;
+#ifdef MAP_HUGETLB
+ if ((fFlags & SUP_PAGE_ALLOC_F_LARGE_PAGES) && !(cPages & 511))
+ fMmap |= MAP_HUGETLB;
+#endif
+
+ size_t cbMmap = cPages << PAGE_SHIFT;
+ if ( !pThis->fSysMadviseWorks
+ && (fFlags & (SUP_PAGE_ALLOC_F_FOR_LOCKING | SUP_PAGE_ALLOC_F_LARGE_PAGES)) == SUP_PAGE_ALLOC_F_FOR_LOCKING)
+ cbMmap += PAGE_SIZE * 2;
+
+ uint8_t *pbPages = (uint8_t *)mmap(NULL, cbMmap, PROT_READ | PROT_WRITE, fMmap, -1, 0);
+#ifdef MAP_HUGETLB
+ if (pbPages == MAP_FAILED && (fMmap & MAP_HUGETLB))
+ {
+ /* Try again without MAP_HUGETLB if mmap fails: */
+ fMmap &= ~MAP_HUGETLB;
+ if (!pThis->fSysMadviseWorks && (fFlags & SUP_PAGE_ALLOC_F_FOR_LOCKING))
+ cbMmap = (cPages + 2) << PAGE_SHIFT;
+ pbPages = (uint8_t *)mmap(NULL, cbMmap, PROT_READ | PROT_WRITE, fMmap, -1, 0);
+ }
+#endif
+ if (pbPages != MAP_FAILED)
+ {
+ if ( !(fFlags & SUP_PAGE_ALLOC_F_FOR_LOCKING)
+ || pThis->fSysMadviseWorks
+#ifdef MAP_HUGETLB
+ || (fMmap & MAP_HUGETLB)
+#endif
+ )
+ {
+ /*
+ * It is not fatal if we fail here but a forked child (e.g. the ALSA sound server)
+ * could crash. Linux < 2.6.16 does not implement madvise(MADV_DONTFORK) but the
+ * kernel seems to split bigger VMAs and that is all that we want -- later we set the
+ * VM_DONTCOPY attribute in supdrvOSLockMemOne().
+ */
+ if ( madvise(pbPages, cbMmap, MADV_DONTFORK)
+#ifdef MAP_HUGETLB
+ && !(fMmap & MAP_HUGETLB)
+#endif
+ )
+ LogRel(("SUPLib: madvise %p-%p failed\n", pbPages, cbMmap));
+
+#ifdef MADV_HUGEPAGE
+ /*
+ * Try enable transparent huge pages for the allocation if desired
+ * and we weren't able to use MAP_HUGETBL above.
+ * Note! KVM doesn't seem to benefit much from this.
+ */
+ if ( !(fMmap & MAP_HUGETLB)
+ && (fFlags & SUP_PAGE_ALLOC_F_LARGE_PAGES)
+ && !(cPages & 511)) /** @todo PORTME: x86 assumption */
+ madvise(pbPages, cbMmap, MADV_HUGEPAGE);
+#endif
+ }
+ else
+ {
+ /*
+ * madvise(MADV_DONTFORK) is not available (most probably Linux 2.4). Enclose any
+ * mmapped region by two unmapped pages to guarantee that there is exactly one VM
+ * area struct of the very same size as the mmap area.
+ */
+ mprotect(pbPages, PAGE_SIZE, PROT_NONE);
+ mprotect(pbPages + cbMmap - PAGE_SIZE, PAGE_SIZE, PROT_NONE);
+ pbPages += PAGE_SHIFT;
+ }
+
+ /** @todo Dunno why we do this, really. It's a waste of time. Maybe it was
+ * to try make sure the pages were allocated or something before we locked them,
+ * so I qualified it with SUP_PAGE_ALLOC_F_FOR_LOCKING (unused) for now... */
+ if (fFlags & SUP_PAGE_ALLOC_F_FOR_LOCKING)
+ memset(pbPages, 0, cPages << PAGE_SHIFT);
+
+ *ppvPages = pbPages;
+ return VINF_SUCCESS;
+ }
+ return VERR_NO_MEMORY;
+}
+
+
+DECLHIDDEN(int) suplibOsPageFree(PSUPLIBDATA pThis, void *pvPages, size_t cPages)
+{
+ NOREF(pThis);
+ munmap(pvPages, cPages << PAGE_SHIFT);
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * Check if the host kernel supports VT-x or not.
+ *
+ * Older Linux kernels clear the VMXE bit in the CR4 register (function
+ * tlb_flush_all()) leading to a host kernel panic.
+ *
+ * @returns VBox status code (no info).
+ * @param ppszWhy Where to return explanatory message.
+ */
+DECLHIDDEN(int) suplibOsQueryVTxSupported(const char **ppszWhy)
+{
+ char szBuf[256];
+ int rc = RTSystemQueryOSInfo(RTSYSOSINFO_RELEASE, szBuf, sizeof(szBuf));
+ if (RT_SUCCESS(rc))
+ {
+ char *pszNext;
+ uint32_t uA, uB, uC;
+
+ rc = RTStrToUInt32Ex(szBuf, &pszNext, 10, &uA);
+ if ( RT_SUCCESS(rc)
+ && *pszNext == '.')
+ {
+ /*
+ * new version number scheme starting with Linux 3.0
+ */
+ if (uA >= 3)
+ return VINF_SUCCESS;
+ rc = RTStrToUInt32Ex(pszNext+1, &pszNext, 10, &uB);
+ if ( RT_SUCCESS(rc)
+ && *pszNext == '.')
+ {
+ rc = RTStrToUInt32Ex(pszNext+1, &pszNext, 10, &uC);
+ if (RT_SUCCESS(rc))
+ {
+ uint32_t uLinuxVersion = (uA << 16) + (uB << 8) + uC;
+ if (uLinuxVersion >= (2 << 16) + (6 << 8) + 13)
+ return VINF_SUCCESS;
+ }
+ }
+ }
+ }
+
+ *ppszWhy = "Linux 2.6.13 or newer required!";
+ return VERR_SUPDRV_KERNEL_TOO_OLD_FOR_VTX;
+}
+
+#endif /* !IN_SUP_HARDENED_R3 */
+
diff --git a/src/VBox/HostDrivers/Support/linux/SUPR0IdcClient-linux.c b/src/VBox/HostDrivers/Support/linux/SUPR0IdcClient-linux.c
new file mode 100644
index 00000000..3a13ebc9
--- /dev/null
+++ b/src/VBox/HostDrivers/Support/linux/SUPR0IdcClient-linux.c
@@ -0,0 +1,66 @@
+/* $Id: SUPR0IdcClient-linux.c $ */
+/** @file
+ * VirtualBox Support Driver - IDC Client Lib, Linux Specific Code.
+ */
+
+/*
+ * Copyright (C) 2008-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include "../SUPR0IdcClientInternal.h"
+#include <iprt/errcore.h>
+
+
+int VBOXCALL supR0IdcNativeOpen(PSUPDRVIDCHANDLE pHandle, PSUPDRVIDCREQCONNECT pReq)
+{
+ return supR0IdcNativeCall(pHandle, SUPDRV_IDC_REQ_CONNECT, &pReq->Hdr);
+}
+
+
+int VBOXCALL supR0IdcNativeClose(PSUPDRVIDCHANDLE pHandle, PSUPDRVIDCREQHDR pReq)
+{
+ return supR0IdcNativeCall(pHandle, SUPDRV_IDC_REQ_DISCONNECT, pReq);
+}
+
+
+int VBOXCALL supR0IdcNativeCall(PSUPDRVIDCHANDLE pHandle, uint32_t iReq, PSUPDRVIDCREQHDR pReq)
+{
+ int rc = SUPDrvLinuxIDC(iReq, pReq);
+ if (RT_SUCCESS(rc))
+ rc = pReq->rc;
+
+ NOREF(pHandle);
+ return rc;
+}
+
diff --git a/src/VBox/HostDrivers/Support/linux/SUPWrapperMod-linux.c b/src/VBox/HostDrivers/Support/linux/SUPWrapperMod-linux.c
new file mode 100644
index 00000000..75ac7606
--- /dev/null
+++ b/src/VBox/HostDrivers/Support/linux/SUPWrapperMod-linux.c
@@ -0,0 +1,211 @@
+/* $Id: SUPWrapperMod-linux.c $ */
+/** @file
+ * Linux .r0 wrapper module template.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#define IPRT_WITHOUT_EFLAGS_AC_PRESERVING
+#include "the-linux-kernel.h"
+
+#include "version-generated.h"
+#include "product-generated.h"
+#include "revision-generated.h"
+
+#include <VBox/sup.h>
+
+
+/*********************************************************************************************************************************
+* Defined Constants And Macros *
+*********************************************************************************************************************************/
+/** @def WRAPPED_MODULE_FLAGS
+ * SUPLDRWRAPPEDMODULE_F_XXX or 0. Default: 0 */
+#ifndef WRAPPED_MODULE_FLAGS
+# define WRAPPED_MODULE_FLAGS 0
+#endif
+/** @def WRAPPED_MODULE_INIT
+ * The module init function or NULL. Default: ModuleInit */
+#ifndef WRAPPED_MODULE_INIT
+# define WRAPPED_MODULE_INIT ModuleInit
+#endif
+/** @def WRAPPED_MODULE_TERM
+ * The module termination function or NULL. Default: ModuleTerm */
+#ifndef WRAPPED_MODULE_TERM
+# define WRAPPED_MODULE_TERM ModuleTerm
+#endif
+/** @def WRAPPED_MODULE_SRV_REQ_HANDLER
+ * The service request handler function. Default: NULL */
+#ifndef WRAPPED_MODULE_SRV_REQ_HANDLER
+# define WRAPPED_MODULE_SRV_REQ_HANDLER NULL
+#endif
+/** @def WRAPPED_MODULE_VMMR0_ENTRY_FAST
+ * The VMMR0 fast entry point. Default: NULL */
+#ifndef WRAPPED_MODULE_VMMR0_ENTRY_FAST
+# define WRAPPED_MODULE_VMMR0_ENTRY_FAST NULL
+#endif
+/** @def WRAPPED_MODULE_VMMR0_ENTRY_EX
+ * The VMMR0 extended entry point. Default: NULL */
+#ifndef WRAPPED_MODULE_VMMR0_ENTRY_EX
+# define WRAPPED_MODULE_VMMR0_ENTRY_EX NULL
+#endif
+/** @def WRAPPED_MODULE_SRV_REQ_HANDLER
+ * The service request handler function. Default: NULL */
+#ifndef WRAPPED_MODULE_SRV_REQ_HANDLER
+# define WRAPPED_MODULE_SRV_REQ_HANDLER NULL
+#endif
+
+#ifdef DOXYGEN_RUNNING
+/** @def WRAPPED_MODULE_LINUX_EXPORTS
+ * Define to enabled linux exports. (Needed for VMMR0.r0 only at present.) */
+# define WRAPPED_MODULE_LINUX_EXPORTS
+#endif
+#ifdef DOXYGEN_RUNNING
+/** @def WRAPPED_MODULE_LICENSE_PROPRIETARY
+ * Define to select proprietary license instead of GPL. */
+# define WRAPPED_MODULE_LICENSE_PROPRIETARY
+#endif
+#ifdef DOXYGEN_RUNNING
+/** @def WRAPPED_MODULE_SYMBOL_INCLUDE
+ * The include with SYMBOL_ENTRY() invocations for all exported symbols. */
+# define WRAPPED_MODULE_SYMBOL_INCLUDE "iprt/cdefs.h"
+#endif
+
+
+/*********************************************************************************************************************************
+* Internal Functions *
+*********************************************************************************************************************************/
+static int __init VBoxWrapperModInit(void);
+static void __exit VBoxWrapperModUnload(void);
+
+
+/*
+ * Prototype the symbols:
+ */
+#undef RT_MANGLER
+#define RT_MANGLER(a_Name) a_Name /* No mangling */
+#define SYMBOL_ENTRY(a_Name) extern FNRT a_Name;
+#include WRAPPED_MODULE_SYMBOL_INCLUDE
+#undef SYMBOL_ENTRY
+
+/*
+ * Export the symbols linux style:
+ */
+#ifdef WRAPPED_MODULE_LINUX_EXPORTS
+# define SYMBOL_ENTRY(a_Name) EXPORT_SYMBOL(a_Name);
+# include WRAPPED_MODULE_SYMBOL_INCLUDE
+# undef SYMBOL_ENTRY
+#endif
+
+
+/*********************************************************************************************************************************
+* Global Variables *
+*********************************************************************************************************************************/
+extern char vboxr0mod_start[]; /**< start of text in the .r0 module. */
+extern char vboxr0mod_end[]; /**< end of bss in the .r0 module. */
+
+/** The symbol table. */
+static SUPLDRWRAPMODSYMBOL const g_aSymbols[] =
+{
+#define SYMBOL_ENTRY(a_Name) { #a_Name, &a_Name },
+#include WRAPPED_MODULE_SYMBOL_INCLUDE
+#undef SYMBOL_ENTRY
+};
+
+/** Wrapped module registration info. */
+static SUPLDRWRAPPEDMODULE const g_WrappedMod =
+{
+ /* .uMagic = */ SUPLDRWRAPPEDMODULE_MAGIC,
+ /* .uVersion = */ SUPLDRWRAPPEDMODULE_VERSION,
+ /* .fFlags = */ WRAPPED_MODULE_FLAGS,
+ /* .pvImageStart = */ &vboxr0mod_start[0],
+ /* .pvImageEnd = */ &vboxr0mod_end[0],
+
+ /* .pfnModuleInit = */ WRAPPED_MODULE_INIT,
+ /* .pfnModuleTerm = */ WRAPPED_MODULE_TERM,
+ /* .pfnVMMR0EntryFast = */ WRAPPED_MODULE_VMMR0_ENTRY_FAST,
+ /* .pfnVMMR0EntryEx = */ WRAPPED_MODULE_VMMR0_ENTRY_EX,
+ /* .pfnSrvReqHandler = */ (PFNSUPR0SERVICEREQHANDLER)WRAPPED_MODULE_SRV_REQ_HANDLER,
+
+ /* .apSymbols = */ g_aSymbols,
+ /* .cSymbols = */ RT_ELEMENTS(g_aSymbols),
+
+ /* .szName = */ WRAPPED_MODULE_NAME,
+ /* .uEndMagic = */ SUPLDRWRAPPEDMODULE_MAGIC,
+};
+
+/** The wrapped module handle. */
+static void *g_hWrappedRegistration = NULL;
+
+
+/**
+ * Initialize module.
+ *
+ * @returns appropriate status code.
+ */
+static int __init VBoxWrapperModInit(void)
+{
+ /*printk("vboxwrap/" WRAPPED_MODULE_NAME ": VBoxWrapperModInit\n");*/
+ int rc = SUPDrvLinuxLdrRegisterWrappedModule(&g_WrappedMod, KBUILD_MODNAME, &g_hWrappedRegistration);
+ if (rc == 0)
+ return 0;
+ printk("vboxwrap/" WRAPPED_MODULE_NAME ": SUPDrvLinuxRegisterWrappedModule failed: %d\n", rc);
+ return -EINVAL;
+}
+
+
+/**
+ * Unload the module.
+ */
+static void __exit VBoxWrapperModUnload(void)
+{
+ /*printk("vboxwrap/" WRAPPED_MODULE_NAME ": VBoxWrapperModUnload\n");*/
+ SUPDrvLinuxLdrDeregisterWrappedModule(&g_WrappedMod, &g_hWrappedRegistration);
+}
+
+module_init(VBoxWrapperModInit);
+module_exit(VBoxWrapperModUnload);
+
+MODULE_AUTHOR(VBOX_VENDOR);
+MODULE_DESCRIPTION(VBOX_PRODUCT " - " WRAPPED_MODULE_NAME);
+#ifndef WRAPPED_MODULE_LICENSE_PROPRIETARY
+MODULE_LICENSE("GPL");
+#else
+MODULE_LICENSE("Proprietary");
+#endif
+#ifdef MODULE_VERSION
+MODULE_VERSION(VBOX_VERSION_STRING " r" RT_XSTR(VBOX_SVN_REV));
+#endif
+
diff --git a/src/VBox/HostDrivers/Support/linux/VBoxR0-wrapped.lds b/src/VBox/HostDrivers/Support/linux/VBoxR0-wrapped.lds
new file mode 100644
index 00000000..f414cfac
--- /dev/null
+++ b/src/VBox/HostDrivers/Support/linux/VBoxR0-wrapped.lds
@@ -0,0 +1,49 @@
+/* $Id: VBoxR0-wrapped.lds $ */
+/** @file
+ * Linker script augmentations for wrapped .r0.
+ */
+
+/*
+ * Copyright (C) 2021-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/* Define a symbol up close to the start of the module: */
+SECTIONS
+{
+ vboxr0mod_start = .;
+} INSERT BEFORE .text;
+
+/* Define a symbol up close to the end of the module: */
+SECTIONS
+{
+ vboxr0mod_end = .;
+} INSERT AFTER .bss;
+
diff --git a/src/VBox/HostDrivers/Support/linux/combined-agnostic1.c b/src/VBox/HostDrivers/Support/linux/combined-agnostic1.c
new file mode 100644
index 00000000..de557f80
--- /dev/null
+++ b/src/VBox/HostDrivers/Support/linux/combined-agnostic1.c
@@ -0,0 +1,99 @@
+/* $Id: combined-agnostic1.c $ */
+/** @file
+ * SUPDrv - Combine a bunch of OS agnostic sources into one compile unit.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+#define LOG_GROUP LOG_GROUP_DEFAULT
+#include "internal/iprt.h"
+#include <VBox/log.h>
+
+#undef LOG_GROUP
+#include "r0drv/alloc-r0drv.c"
+#undef LOG_GROUP
+#include "r0drv/initterm-r0drv.c"
+#undef LOG_GROUP
+#include "r0drv/memobj-r0drv.c"
+#undef LOG_GROUP
+#include "r0drv/mpnotification-r0drv.c"
+#undef LOG_GROUP
+#include "r0drv/powernotification-r0drv.c"
+#undef LOG_GROUP
+#include "r0drv/generic/semspinmutex-r0drv-generic.c"
+#undef LOG_GROUP
+#include "common/alloc/alloc.c"
+#undef LOG_GROUP
+#include "common/checksum/crc32.c"
+#undef LOG_GROUP
+#include "common/checksum/ipv4.c"
+#undef LOG_GROUP
+#include "common/checksum/ipv6.c"
+#undef LOG_GROUP
+#include "common/err/errinfo.c"
+#undef LOG_GROUP
+#include "common/log/log.c"
+#undef LOG_GROUP
+#include "common/log/logellipsis.c"
+#undef LOG_GROUP
+#include "common/log/logrel.c"
+#undef LOG_GROUP
+#include "common/log/logrelellipsis.c"
+#undef LOG_GROUP
+#include "common/log/logcom.c"
+#undef LOG_GROUP
+#include "common/log/logformat.c"
+#undef LOG_GROUP
+#include "common/log/RTLogCreateEx.c"
+#undef LOG_GROUP
+#include "common/misc/RTAssertMsg1Weak.c"
+#undef LOG_GROUP
+#include "common/misc/RTAssertMsg2.c"
+#undef LOG_GROUP
+#include "common/misc/RTAssertMsg2Add.c"
+#undef LOG_GROUP
+#include "common/misc/RTAssertMsg2AddWeak.c"
+#undef LOG_GROUP
+#include "common/misc/RTAssertMsg2AddWeakV.c"
+#undef LOG_GROUP
+#include "common/misc/RTAssertMsg2Weak.c"
+#undef LOG_GROUP
+#include "common/misc/RTAssertMsg2WeakV.c"
+#undef LOG_GROUP
+#include "common/misc/assert.c"
+#undef LOG_GROUP
+#include "common/misc/handletable.c"
+#undef LOG_GROUP
+#include "common/misc/handletablectx.c"
+#undef LOG_GROUP
+#include "common/misc/thread.c"
+
diff --git a/src/VBox/HostDrivers/Support/linux/combined-agnostic2.c b/src/VBox/HostDrivers/Support/linux/combined-agnostic2.c
new file mode 100644
index 00000000..63da1a7e
--- /dev/null
+++ b/src/VBox/HostDrivers/Support/linux/combined-agnostic2.c
@@ -0,0 +1,116 @@
+/* $Id: combined-agnostic2.c $ */
+/** @file
+ * SUPDrv - Combine a bunch of OS agnostic sources into one compile unit.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+#define LOG_GROUP LOG_GROUP_DEFAULT
+#include "internal/iprt.h"
+#include <VBox/log.h>
+
+#undef LOG_GROUP
+#include "common/string/RTStrCat.c"
+#undef LOG_GROUP
+#include "common/string/RTStrCopy.c"
+#undef LOG_GROUP
+#include "common/string/RTStrCopyEx.c"
+#undef LOG_GROUP
+#include "common/string/RTStrCopyP.c"
+#undef LOG_GROUP
+#include "common/string/RTStrEnd.c"
+#undef LOG_GROUP
+#include "common/string/RTStrNCmp.c"
+#undef LOG_GROUP
+#include "common/string/RTStrNLen.c"
+#undef LOG_GROUP
+#include "common/string/stringalloc.c"
+#undef LOG_GROUP
+#include "common/string/strformat.c"
+#undef LOG_GROUP
+#include "common/string/RTStrFormat.c"
+#undef LOG_GROUP
+#include "common/string/strformatnum.c"
+#undef LOG_GROUP
+#include "common/string/strformattype.c"
+#undef LOG_GROUP
+#include "common/string/strprintf.c"
+#undef LOG_GROUP
+#include "common/string/strprintf-ellipsis.c"
+#undef LOG_GROUP
+#include "common/string/strprintf2.c"
+#undef LOG_GROUP
+#include "common/string/strprintf2-ellipsis.c"
+#undef LOG_GROUP
+#include "common/string/strtonum.c"
+#undef LOG_GROUP
+#include "common/table/avlpv.c"
+#undef LOG_GROUP
+#include "common/time/time.c"
+#undef LOG_GROUP
+#include "generic/RTAssertShouldPanic-generic.c"
+#undef LOG_GROUP
+#include "generic/RTLogWriteStdErr-stub-generic.c"
+#undef LOG_GROUP
+#include "generic/RTLogWriteStdOut-stub-generic.c"
+#undef LOG_GROUP
+#include "generic/RTLogWriteUser-generic.c"
+#undef LOG_GROUP
+#include "generic/RTMpGetArraySize-generic.c"
+#undef LOG_GROUP
+#include "generic/RTMpGetCoreCount-generic.c"
+#undef LOG_GROUP
+#include "generic/RTSemEventWait-2-ex-generic.c"
+#undef LOG_GROUP
+#include "generic/RTSemEventWaitNoResume-2-ex-generic.c"
+#undef LOG_GROUP
+#include "generic/RTSemEventMultiWait-2-ex-generic.c"
+#undef LOG_GROUP
+#include "generic/RTSemEventMultiWaitNoResume-2-ex-generic.c"
+#undef LOG_GROUP
+#include "generic/RTTimerCreate-generic.c"
+#undef LOG_GROUP
+#include "generic/errvars-generic.c"
+#undef LOG_GROUP
+#include "generic/mppresent-generic.c"
+#undef LOG_GROUP
+#include "generic/uuid-generic.c"
+#undef LOG_GROUP
+#include "VBox/log-vbox.c"
+#undef LOG_GROUP
+#include "VBox/RTLogWriteVmm-amd64-x86.c"
+
+#ifdef RT_ARCH_AMD64
+# undef LOG_GROUP
+# include "common/alloc/heapsimple.c"
+#endif
+
diff --git a/src/VBox/HostDrivers/Support/linux/combined-os-specific.c b/src/VBox/HostDrivers/Support/linux/combined-os-specific.c
new file mode 100644
index 00000000..e08d2800
--- /dev/null
+++ b/src/VBox/HostDrivers/Support/linux/combined-os-specific.c
@@ -0,0 +1,68 @@
+/* $Id: combined-os-specific.c $ */
+/** @file
+ * SUPDrv - Combine a bunch of OS specific sources into one compile unit.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+#include "the-linux-kernel.h"
+
+#include "r0drv/linux/alloc-r0drv-linux.c"
+#include "r0drv/linux/assert-r0drv-linux.c"
+#include "r0drv/linux/initterm-r0drv-linux.c"
+#include "r0drv/linux/memobj-r0drv-linux.c"
+#include "r0drv/linux/memuserkernel-r0drv-linux.c"
+#include "r0drv/linux/mp-r0drv-linux.c"
+#include "r0drv/linux/mpnotification-r0drv-linux.c"
+#include "r0drv/linux/process-r0drv-linux.c"
+#undef LOG_GROUP
+#include "r0drv/linux/rtStrFormatKernelAddress-r0drv-linux.c"
+#undef LOG_GROUP
+#define LOG_GROUP RTLOGGROUP_DEFAULT
+#include "r0drv/linux/semevent-r0drv-linux.c"
+#include "r0drv/linux/semeventmulti-r0drv-linux.c"
+#include "r0drv/linux/semfastmutex-r0drv-linux.c"
+#include "r0drv/linux/semmutex-r0drv-linux.c"
+#include "r0drv/linux/spinlock-r0drv-linux.c"
+#include "r0drv/linux/thread-r0drv-linux.c"
+#include "r0drv/linux/thread2-r0drv-linux.c"
+#include "r0drv/linux/threadctxhooks-r0drv-linux.c"
+#undef LOG_GROUP
+#include "r0drv/linux/time-r0drv-linux.c"
+#undef LOG_GROUP
+#define LOG_GROUP RTLOGGROUP_DEFAULT
+#include "r0drv/linux/timer-r0drv-linux.c"
+#include "r0drv/linux/RTLogWriteDebugger-r0drv-linux.c"
+#include "common/err/RTErrConvertFromErrno.c"
+#include "common/err/RTErrConvertToErrno.c"
+
diff --git a/src/VBox/HostDrivers/Support/linux/files_vboxdrv b/src/VBox/HostDrivers/Support/linux/files_vboxdrv
new file mode 100755
index 00000000..cd8b214a
--- /dev/null
+++ b/src/VBox/HostDrivers/Support/linux/files_vboxdrv
@@ -0,0 +1,242 @@
+#!/bin/sh
+# $Id: files_vboxdrv $
+## @file
+# Shared file between Makefile.kmk and export_modules.sh.
+#
+
+#
+# Copyright (C) 2007-2023 Oracle and/or its affiliates.
+#
+# This file is part of VirtualBox base platform packages, as
+# available from https://www.virtualbox.org.
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation, in version 3 of the
+# License.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, see <https://www.gnu.org/licenses>.
+#
+# The contents of this file may alternatively be used under the terms
+# of the Common Development and Distribution License Version 1.0
+# (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+# in the VirtualBox distribution, in which case the provisions of the
+# CDDL are applicable instead of those of the GPL.
+#
+# You may elect to license modified versions of this file under the
+# terms and conditions of either the GPL or the CDDL or both.
+#
+# SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+#
+
+FILES_VBOXDRV_NOBIN=" \
+ ${PATH_ROOT}/include/iprt/alloc.h=>include/iprt/alloc.h \
+ ${PATH_ROOT}/include/iprt/asm.h=>include/iprt/asm.h \
+ ${PATH_ROOT}/include/iprt/asm-amd64-x86.h=>include/iprt/asm-amd64-x86.h \
+ ${PATH_ROOT}/include/iprt/asm-math.h=>include/iprt/asm-math.h \
+ ${PATH_ROOT}/include/iprt/assert.h=>include/iprt/assert.h \
+ ${PATH_ROOT}/include/iprt/assertcompile.h=>include/iprt/assertcompile.h \
+ ${PATH_ROOT}/include/iprt/avl.h=>include/iprt/avl.h \
+ ${PATH_ROOT}/include/iprt/cdefs.h=>include/iprt/cdefs.h \
+ ${PATH_ROOT}/include/iprt/cpuset.h=>include/iprt/cpuset.h \
+ ${PATH_ROOT}/include/iprt/crc.h=>include/iprt/crc.h \
+ ${PATH_ROOT}/include/iprt/ctype.h=>include/iprt/ctype.h \
+ ${PATH_ROOT}/include/iprt/err.h=>include/iprt/err.h \
+ ${PATH_ROOT}/include/iprt/errcore.h=>include/iprt/errcore.h \
+ ${PATH_ROOT}/include/iprt/errno.h=>include/iprt/errno.h \
+ ${PATH_ROOT}/include/iprt/heap.h=>include/iprt/heap.h \
+ ${PATH_ROOT}/include/iprt/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/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/rand.h=>include/iprt/rand.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/uint128.h=>include/iprt/uint128.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/uuid.h=>include/iprt/uuid.h \
+ ${PATH_ROOT}/include/iprt/x86.h=>include/iprt/x86.h \
+ ${PATH_ROOT}/include/iprt/x86-helpers.h=>include/iprt/x86-helpers.h \
+ ${PATH_ROOT}/include/iprt/linux/version.h=>include/iprt/linux/version.h \
+ ${PATH_ROOT}/include/iprt/nocrt/limits.h=>include/iprt/nocrt/limits.h \
+ ${PATH_ROOT}/include/VBox/cdefs.h=>include/VBox/cdefs.h \
+ ${PATH_ROOT}/include/VBox/err.h=>include/VBox/err.h \
+ ${PATH_ROOT}/include/VBox/log.h=>include/VBox/log.h \
+ ${PATH_ROOT}/include/VBox/param.h=>include/VBox/param.h \
+ ${PATH_ROOT}/include/VBox/sup.h=>include/VBox/sup.h \
+ ${PATH_ROOT}/include/VBox/types.h=>include/VBox/types.h \
+ ${PATH_ROOT}/include/VBox/SUPDrvMangling.h=>include/VBox/SUPDrvMangling.h \
+ ${PATH_ROOT}/include/VBox/VBoxTpG.h=>include/VBox/VBoxTpG.h \
+ ${PATH_ROOT}/include/VBox/vmm/hm_vmx.h=>include/VBox/vmm/hm_vmx.h \
+ ${PATH_ROOT}/include/VBox/vmm/hm_svm.h=>include/VBox/vmm/hm_svm.h \
+ ${PATH_ROOT}/include/VBox/vmm/cpuidcall.h=>include/VBox/vmm/cpuidcall.h \
+ ${PATH_ROOT}/src/VBox/HostDrivers/Support/linux/SUPDrv-linux.c=>linux/SUPDrv-linux.c \
+ ${PATH_ROOT}/src/VBox/HostDrivers/Support/linux/combined-agnostic1.c=>combined-agnostic1.c \
+ ${PATH_ROOT}/src/VBox/HostDrivers/Support/linux/combined-agnostic2.c=>combined-agnostic2.c \
+ ${PATH_ROOT}/src/VBox/HostDrivers/Support/linux/combined-os-specific.c=>combined-os-specific.c \
+ ${PATH_ROOT}/src/VBox/HostDrivers/Support/SUPDrv.cpp=>SUPDrv.c \
+ ${PATH_ROOT}/src/VBox/HostDrivers/Support/SUPDrvGip.cpp=>SUPDrvGip.c \
+ ${PATH_ROOT}/src/VBox/HostDrivers/Support/SUPDrvSem.cpp=>SUPDrvSem.c \
+ ${PATH_ROOT}/src/VBox/HostDrivers/Support/SUPDrvTracer.cpp=>SUPDrvTracer.c \
+ ${PATH_ROOT}/src/VBox/HostDrivers/Support/SUPDrv-dtrace.cpp=>SUPDrvDTrace.c \
+ ${PATH_ROOT}/src/VBox/HostDrivers/Support/SUPDrvIDC.h=>SUPDrvIDC.h \
+ ${PATH_ROOT}/src/VBox/HostDrivers/Support/SUPDrvIOC.h=>SUPDrvIOC.h \
+ ${PATH_ROOT}/src/VBox/HostDrivers/Support/SUPDrvInternal.h=>SUPDrvInternal.h \
+ ${PATH_ROOT}/src/VBox/HostDrivers/Support/SUPLibAll.cpp=>SUPLibAll.c \
+ ${PATH_ROOT}/src/VBox/Installer/linux/Makefile-footer.gmk=>Makefile-footer.gmk \
+ ${PATH_ROOT}/src/VBox/Installer/linux/Makefile-header.gmk=>Makefile-header.gmk \
+ ${PATH_ROOT}/src/VBox/Runtime/common/alloc/alloc.cpp=>common/alloc/alloc.c \
+ ${PATH_ROOT}/src/VBox/Runtime/common/alloc/heapsimple.cpp=>common/alloc/heapsimple.c \
+ ${PATH_ROOT}/src/VBox/Runtime/common/checksum/crc32.cpp=>common/checksum/crc32.c \
+ ${PATH_ROOT}/src/VBox/Runtime/common/checksum/ipv4.cpp=>common/checksum/ipv4.c \
+ ${PATH_ROOT}/src/VBox/Runtime/common/checksum/ipv6.cpp=>common/checksum/ipv6.c \
+ ${PATH_ROOT}/src/VBox/Runtime/common/err/RTErrConvertFromErrno.cpp=>common/err/RTErrConvertFromErrno.c \
+ ${PATH_ROOT}/src/VBox/Runtime/common/err/RTErrConvertToErrno.cpp=>common/err/RTErrConvertToErrno.c \
+ ${PATH_ROOT}/src/VBox/Runtime/common/err/errinfo.cpp=>common/err/errinfo.c \
+ ${PATH_ROOT}/src/VBox/Runtime/common/log/log.cpp=>common/log/log.c \
+ ${PATH_ROOT}/src/VBox/Runtime/common/log/logellipsis.cpp=>common/log/logellipsis.c \
+ ${PATH_ROOT}/src/VBox/Runtime/common/log/logrel.cpp=>common/log/logrel.c \
+ ${PATH_ROOT}/src/VBox/Runtime/common/log/logrelellipsis.cpp=>common/log/logrelellipsis.c \
+ ${PATH_ROOT}/src/VBox/Runtime/common/log/logcom.cpp=>common/log/logcom.c \
+ ${PATH_ROOT}/src/VBox/Runtime/common/log/logformat.cpp=>common/log/logformat.c \
+ ${PATH_ROOT}/src/VBox/Runtime/common/log/RTLogCreateEx.cpp=>common/log/RTLogCreateEx.c \
+ ${PATH_ROOT}/src/VBox/Runtime/common/math/gcc/divdi3.c=>math/gcc/divdi3.c \
+ ${PATH_ROOT}/src/VBox/Runtime/common/math/gcc/divmoddi4.c=>math/gcc/divmoddi4.c \
+ ${PATH_ROOT}/src/VBox/Runtime/common/math/gcc/moddi3.c=>math/gcc/moddi3.c \
+ ${PATH_ROOT}/src/VBox/Runtime/common/math/gcc/qdivrem.c=>math/gcc/qdivrem.c \
+ ${PATH_ROOT}/src/VBox/Runtime/common/math/gcc/quad.h=>math/gcc/quad.h \
+ ${PATH_ROOT}/src/VBox/Runtime/common/math/gcc/udivdi3.c=>math/gcc/udivdi3.c \
+ ${PATH_ROOT}/src/VBox/Runtime/common/math/gcc/udivmoddi4.c=>math/gcc/udivmoddi4.c \
+ ${PATH_ROOT}/src/VBox/Runtime/common/math/gcc/umoddi3.c=>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/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/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/RTStrCopy.cpp=>common/string/RTStrCopy.c \
+ ${PATH_ROOT}/src/VBox/Runtime/common/string/RTStrCopyEx.cpp=>common/string/RTStrCopyEx.c \
+ ${PATH_ROOT}/src/VBox/Runtime/common/string/RTStrCopyP.cpp=>common/string/RTStrCopyP.c \
+ ${PATH_ROOT}/src/VBox/Runtime/common/string/RTStrEnd.cpp=>common/string/RTStrEnd.c \
+ ${PATH_ROOT}/src/VBox/Runtime/common/string/RTStrNCmp.cpp=>common/string/RTStrNCmp.c \
+ ${PATH_ROOT}/src/VBox/Runtime/common/string/RTStrNLen.cpp=>common/string/RTStrNLen.c \
+ ${PATH_ROOT}/src/VBox/Runtime/common/string/stringalloc.cpp=>common/string/stringalloc.c \
+ ${PATH_ROOT}/src/VBox/Runtime/common/string/strformat.cpp=>common/string/strformat.c \
+ ${PATH_ROOT}/src/VBox/Runtime/common/string/RTStrFormat.cpp=>common/string/RTStrFormat.c \
+ ${PATH_ROOT}/src/VBox/Runtime/common/string/strformatnum.cpp=>common/string/strformatnum.c \
+ ${PATH_ROOT}/src/VBox/Runtime/common/string/strformatrt.cpp=>common/string/strformatrt.c \
+ ${PATH_ROOT}/src/VBox/Runtime/common/string/strformattype.cpp=>common/string/strformattype.c \
+ ${PATH_ROOT}/src/VBox/Runtime/common/string/strprintf.cpp=>common/string/strprintf.c \
+ ${PATH_ROOT}/src/VBox/Runtime/common/string/strprintf-ellipsis.cpp=>common/string/strprintf-ellipsis.c \
+ ${PATH_ROOT}/src/VBox/Runtime/common/string/strprintf2.cpp=>common/string/strprintf2.c \
+ ${PATH_ROOT}/src/VBox/Runtime/common/string/strprintf2-ellipsis.cpp=>common/string/strprintf2-ellipsis.c \
+ ${PATH_ROOT}/src/VBox/Runtime/common/string/strtonum.cpp=>common/string/strtonum.c \
+ ${PATH_ROOT}/src/VBox/Runtime/common/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/sched.h=>include/internal/sched.h \
+ ${PATH_ROOT}/src/VBox/Runtime/include/internal/process.h=>include/internal/process.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/generic/RTAssertShouldPanic-generic.cpp=>generic/RTAssertShouldPanic-generic.c \
+ ${PATH_ROOT}/src/VBox/Runtime/generic/RTLogWriteStdErr-stub-generic.cpp=>generic/RTLogWriteStdErr-stub-generic.c \
+ ${PATH_ROOT}/src/VBox/Runtime/generic/RTLogWriteStdOut-stub-generic.cpp=>generic/RTLogWriteStdOut-stub-generic.c \
+ ${PATH_ROOT}/src/VBox/Runtime/generic/RTLogWriteUser-generic.cpp=>generic/RTLogWriteUser-generic.c \
+ ${PATH_ROOT}/src/VBox/Runtime/generic/RTMpGetArraySize-generic.cpp=>generic/RTMpGetArraySize-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/RTTimerCreate-generic.cpp=>generic/RTTimerCreate-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/generic/uuid-generic.cpp=>generic/uuid-generic.c \
+ ${PATH_ROOT}/src/VBox/Runtime/r0drv/alloc-r0drv.cpp=>r0drv/alloc-r0drv.c \
+ ${PATH_ROOT}/src/VBox/Runtime/r0drv/alloc-r0drv.h=>r0drv/alloc-r0drv.h \
+ ${PATH_ROOT}/src/VBox/Runtime/r0drv/initterm-r0drv.cpp=>r0drv/initterm-r0drv.c \
+ ${PATH_ROOT}/src/VBox/Runtime/r0drv/mp-r0drv.h=>r0drv/mp-r0drv.h \
+ ${PATH_ROOT}/src/VBox/Runtime/r0drv/mpnotification-r0drv.c=>r0drv/mpnotification-r0drv.c \
+ ${PATH_ROOT}/src/VBox/Runtime/r0drv/power-r0drv.h=>r0drv/power-r0drv.h \
+ ${PATH_ROOT}/src/VBox/Runtime/r0drv/powernotification-r0drv.c=>r0drv/powernotification-r0drv.c \
+ ${PATH_ROOT}/src/VBox/Runtime/r0drv/linux/rtStrFormatKernelAddress-r0drv-linux.c=>r0drv/linux/rtStrFormatKernelAddress-r0drv-linux.c \
+ ${PATH_ROOT}/src/VBox/Runtime/r0drv/linux/RTLogWriteDebugger-r0drv-linux.c=>r0drv/linux/RTLogWriteDebugger-r0drv-linux.c \
+ ${PATH_ROOT}/src/VBox/Runtime/r0drv/linux/assert-r0drv-linux.c=>r0drv/linux/assert-r0drv-linux.c \
+ ${PATH_ROOT}/src/VBox/Runtime/r0drv/linux/alloc-r0drv-linux.c=>r0drv/linux/alloc-r0drv-linux.c \
+ ${PATH_ROOT}/src/VBox/Runtime/r0drv/linux/initterm-r0drv-linux.c=>r0drv/linux/initterm-r0drv-linux.c \
+ ${PATH_ROOT}/src/VBox/Runtime/r0drv/linux/memobj-r0drv-linux.c=>r0drv/linux/memobj-r0drv-linux.c \
+ ${PATH_ROOT}/src/VBox/Runtime/r0drv/linux/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/threadctxhooks-r0drv-linux.c=>r0drv/linux/threadctxhooks-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/generic/semspinmutex-r0drv-generic.c=>r0drv/generic/semspinmutex-r0drv-generic.c \
+ ${PATH_ROOT}/src/VBox/Runtime/r0drv/generic/threadctxhooks-r0drv-generic.cpp=>r0drv/generic/threadctxhooks-r0drv-generic.cpp \
+ ${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/RTLogWriteVmm-amd64-x86.cpp=>VBox/RTLogWriteVmm-amd64-x86.c \
+ ${PATH_OUT}/version-generated.h=>version-generated.h \
+ ${PATH_OUT}/revision-generated.h=>revision-generated.h \
+ ${PATH_OUT}/product-generated.h=>product-generated.h \
+"
+
+FILES_VBOXDRV_BIN=" \
+"
diff --git a/src/VBox/HostDrivers/Support/os2/Makefile.kup b/src/VBox/HostDrivers/Support/os2/Makefile.kup
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/src/VBox/HostDrivers/Support/os2/Makefile.kup
diff --git a/src/VBox/HostDrivers/Support/os2/SUPDrv-os2.cpp b/src/VBox/HostDrivers/Support/os2/SUPDrv-os2.cpp
new file mode 100644
index 00000000..9379f4ac
--- /dev/null
+++ b/src/VBox/HostDrivers/Support/os2/SUPDrv-os2.cpp
@@ -0,0 +1,570 @@
+/* $Id: SUPDrv-os2.cpp $ */
+/** @file
+ * VBoxDrv - The VirtualBox Support Driver - OS/2 specifics.
+ */
+
+/*
+ * Copyright (c) 2007 knut st. osmundsen <bird-src-spam@anduin.net>
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use,
+ * copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following
+ * conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+ * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#define LOG_GROUP LOG_GROUP_SUP_DRV
+#define __STDC_CONSTANT_MACROS
+#define __STDC_LIMIT_MACROS
+
+#include <os2ddk/bsekee.h>
+#undef RT_MAX
+
+#include "SUPDrvInternal.h"
+#include <VBox/version.h>
+#include <iprt/initterm.h>
+#include <iprt/string.h>
+#include <iprt/spinlock.h>
+#include <iprt/process.h>
+#include <iprt/assert.h>
+#include <VBox/log.h>
+#include <iprt/param.h>
+#include <VBox/version.h>
+
+
+/*********************************************************************************************************************************
+* Global Variables *
+*********************************************************************************************************************************/
+/**
+ * Device extention & session data association structure.
+ */
+static SUPDRVDEVEXT g_DevExt;
+/** Spinlock protecting g_apSessionHashTab. */
+static RTSPINLOCK g_Spinlock = NIL_RTSPINLOCK;
+/** Hash table */
+static PSUPDRVSESSION g_apSessionHashTab[19];
+/** Calculates the index into g_apSessionHashTab.*/
+#define SESSION_HASH(sfn) ((sfn) % RT_ELEMENTS(g_apSessionHashTab))
+
+RT_C_DECLS_BEGIN
+/* Defined in SUPDrvA-os2.asm */
+extern uint16_t g_offLogHead;
+extern uint16_t volatile g_offLogTail;
+extern uint16_t const g_cchLogMax;
+extern char g_szLog[];
+/* (init only:) */
+extern char g_szInitText[];
+extern uint16_t g_cchInitText;
+extern uint16_t g_cchInitTextMax;
+RT_C_DECLS_END
+
+
+/*********************************************************************************************************************************
+* Internal Functions *
+*********************************************************************************************************************************/
+
+
+
+/**
+ * 32-bit Ring-0 initialization.
+ *
+ * @returns 0 on success, non-zero on failure.
+ * @param pszArgs Pointer to the device arguments.
+ */
+DECLASM(int) VBoxDrvInit(const char *pszArgs)
+{
+ /*
+ * Initialize the runtime.
+ */
+ int rc = RTR0Init(0);
+ if (RT_SUCCESS(rc))
+ {
+ Log(("VBoxDrvInit: pszArgs=%s\n", pszArgs));
+
+ /*
+ * Initialize the device extension.
+ */
+ rc = supdrvInitDevExt(&g_DevExt, sizeof(SUPDRVSESSION));
+ if (RT_SUCCESS(rc))
+ {
+ /*
+ * Initialize the session hash table.
+ */
+ rc = RTSpinlockCreate(&g_Spinlock, RTSPINLOCK_FLAGS_INTERRUPT_SAFE, "VBoxDrvOS2");
+ if (RT_SUCCESS(rc))
+ {
+ /*
+ * Process the commandline. Later.
+ */
+ bool fVerbose = true;
+
+ /*
+ * Success
+ */
+ if (fVerbose)
+ {
+ strcpy(&g_szInitText[0],
+ "\r\n"
+ "VirtualBox.org Support Driver for OS/2 version " VBOX_VERSION_STRING "\r\n"
+ "Copyright (C) 2007 Knut St. Osmundsen\r\n"
+ "Copyright (C) 2007-" VBOX_C_YEAR " Oracle Corporation\r\n");
+ g_cchInitText = strlen(&g_szInitText[0]);
+ }
+ return VINF_SUCCESS;
+ }
+ g_cchInitText = RTStrPrintf(&g_szInitText[0], g_cchInitTextMax, "VBoxDrv.sys: RTSpinlockCreate failed, rc=%Rrc\n", rc);
+ supdrvDeleteDevExt(&g_DevExt);
+ }
+ else
+ g_cchInitText = RTStrPrintf(&g_szInitText[0], g_cchInitTextMax, "VBoxDrv.sys: supdrvInitDevExt failed, rc=%Rrc\n", rc);
+ RTR0Term();
+ }
+ else
+ g_cchInitText = RTStrPrintf(&g_szInitText[0], g_cchInitTextMax, "VBoxDrv.sys: RTR0Init failed, rc=%Rrc\n", rc);
+ return rc;
+}
+
+
+DECLASM(int) VBoxDrvOpen(uint16_t sfn)
+{
+ int rc;
+ PSUPDRVSESSION pSession;
+
+ /*
+ * Create a new session.
+ */
+ rc = supdrvCreateSession(&g_DevExt, true /* fUser */, true /*fUnrestricted*/, &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(("VBoxDrvOpen: g_DevExt=%p pSession=%p rc=%d pid=%d\n", &g_DevExt, pSession, rc, (int)RTProcSelf()));
+ return rc;
+}
+
+
+DECLASM(int) VBoxDrvClose(uint16_t sfn)
+{
+ Log(("VBoxDrvClose: pid=%d sfn=%d\n", (int)RTProcSelf(), sfn));
+
+ /*
+ * Remove from the hash table.
+ */
+ PSUPDRVSESSION pSession;
+ const RTPROCESS Process = RTProcSelf();
+ const unsigned iHash = SESSION_HASH(sfn);
+ 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
+ {
+ PSUPDRVSESSION pPrev = pSession;
+ pSession = pSession->pNextHash;
+ while (pSession)
+ {
+ if ( pSession->sfn == sfn
+ && pSession->Process == Process)
+ {
+ pPrev->pNextHash = pSession->pNextHash;
+ pSession->pNextHash = NULL;
+ break;
+ }
+
+ /* next */
+ pPrev = pSession;
+ pSession = pSession->pNextHash;
+ }
+ }
+ }
+ RTSpinlockRelease(g_Spinlock);
+ if (!pSession)
+ {
+ OSDBGPRINT(("VBoxDrvIoctl: WHUT?!? pSession == NULL! This must be a mistake... pid=%d sfn=%d\n", (int)Process, sfn));
+ return VERR_INVALID_PARAMETER;
+ }
+
+ /*
+ * Close the session.
+ */
+ supdrvSessionRelease(pSession);
+ return 0;
+}
+
+
+DECLASM(int) VBoxDrvIOCtlFast(uint16_t sfn, uint8_t iFunction)
+{
+ /*
+ * Find the session.
+ */
+ const RTPROCESS Process = RTProcSelf();
+ const unsigned iHash = SESSION_HASH(sfn);
+ PSUPDRVSESSION 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));
+
+ if (RT_LIKELY(pSession))
+ supdrvSessionRetain(pSession);
+ }
+ RTSpinlockRelease(g_Spinlock);
+ if (RT_UNLIKELY(!pSession))
+ {
+ OSDBGPRINT(("VBoxDrvIoctl: WHUT?!? pSession == NULL! This must be a mistake... pid=%d\n", (int)Process));
+ return VERR_INVALID_PARAMETER;
+ }
+
+ /*
+ * Dispatch the fast IOCtl.
+ */
+ int rc;
+ if ((unsigned)(iFunction - SUP_IOCTL_FAST_DO_FIRST) < (unsigned)32)
+ rc = supdrvIOCtlFast(iFunction, 0, &g_DevExt, pSession);
+ else
+ rc = VERR_INVALID_FUNCTION;
+ supdrvSessionRelease(pSession);
+ return rc;
+}
+
+
+DECLASM(int) VBoxDrvIOCtl(uint16_t sfn, uint8_t iCat, uint8_t iFunction, void *pvParm, void *pvData, uint16_t *pcbParm, uint16_t *pcbData)
+{
+ /*
+ * Find the session.
+ */
+ const RTPROCESS Process = RTProcSelf();
+ const unsigned iHash = SESSION_HASH(sfn);
+ PSUPDRVSESSION 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));
+
+ if (RT_LIKELY(pSession))
+ supdrvSessionRetain(pSession);
+ }
+ RTSpinlockRelease(g_Spinlock);
+ if (!pSession)
+ {
+ OSDBGPRINT(("VBoxDrvIoctl: WHUT?!? pSession == NULL! This must be a mistake... pid=%d\n", (int)Process));
+ return VERR_INVALID_PARAMETER;
+ }
+
+ /*
+ * Verify the category and dispatch the IOCtl.
+ */
+ int rc;
+ if (RT_LIKELY(iCat == SUP_CTL_CATEGORY))
+ {
+ Log(("VBoxDrvIOCtl: pSession=%p iFunction=%#x pvParm=%p pvData=%p *pcbParm=%d *pcbData=%d\n", pSession, iFunction, pvParm, pvData, *pcbParm, *pcbData));
+ Assert(pvParm);
+ Assert(!pvData);
+
+ /*
+ * Lock the header.
+ */
+ PSUPREQHDR pHdr = (PSUPREQHDR)pvParm;
+ AssertReturn(*pcbParm == sizeof(*pHdr), VERR_INVALID_PARAMETER);
+ KernVMLock_t Lock;
+ rc = KernVMLock(VMDHL_WRITE, pHdr, *pcbParm, &Lock, (KernPageList_t *)-1, NULL);
+ AssertMsgReturn(!rc, ("KernVMLock(VMDHL_WRITE, %p, %#x, &p, NULL, NULL) -> %d\n", pHdr, *pcbParm, &Lock, rc), VERR_LOCK_FAILED);
+
+ /*
+ * Validate the header.
+ */
+ if (RT_LIKELY((pHdr->fFlags & SUPREQHDR_FLAGS_MAGIC_MASK) == SUPREQHDR_FLAGS_MAGIC))
+ {
+ uint32_t cbReq = RT_MAX(pHdr->cbIn, pHdr->cbOut);
+ if (RT_LIKELY( pHdr->cbIn >= sizeof(*pHdr)
+ && pHdr->cbOut >= sizeof(*pHdr)
+ && cbReq <= _1M*16))
+ {
+ /*
+ * Lock the rest of the buffer if necessary.
+ */
+ if (((uintptr_t)pHdr & PAGE_OFFSET_MASK) + cbReq > PAGE_SIZE)
+ {
+ rc = KernVMUnlock(&Lock);
+ AssertMsgReturn(!rc, ("KernVMUnlock(Lock) -> %#x\n", rc), VERR_LOCK_FAILED);
+
+ rc = KernVMLock(VMDHL_WRITE, pHdr, cbReq, &Lock, (KernPageList_t *)-1, NULL);
+ AssertMsgReturn(!rc, ("KernVMLock(VMDHL_WRITE, %p, %#x, &p, NULL, NULL) -> %d\n", pHdr, cbReq, &Lock, rc), VERR_LOCK_FAILED);
+ }
+
+ /*
+ * Process the IOCtl.
+ */
+ rc = supdrvIOCtl(iFunction, &g_DevExt, pSession, pHdr, cbReq);
+ }
+ else
+ {
+ OSDBGPRINT(("VBoxDrvIOCtl: max(%#x,%#x); iCmd=%#x\n", pHdr->cbIn, pHdr->cbOut, iFunction));
+ rc = VERR_INVALID_PARAMETER;
+ }
+ }
+ else
+ {
+ OSDBGPRINT(("VBoxDrvIOCtl: bad magic fFlags=%#x; iCmd=%#x\n", pHdr->fFlags, iFunction));
+ rc = VERR_INVALID_PARAMETER;
+ }
+
+ /*
+ * Unlock and return.
+ */
+ int rc2 = KernVMUnlock(&Lock);
+ AssertMsg(!rc2, ("rc2=%d\n", rc2)); NOREF(rc2);
+ }
+ else
+ rc = VERR_NOT_SUPPORTED;
+
+ supdrvSessionRelease(pSession);
+ Log2(("VBoxDrvIOCtl: returns %d\n", rc));
+ return rc;
+}
+
+
+void VBOXCALL supdrvOSCleanupSession(PSUPDRVDEVEXT pDevExt, PSUPDRVSESSION pSession)
+{
+ NOREF(pDevExt);
+ NOREF(pSession);
+}
+
+
+void VBOXCALL supdrvOSSessionHashTabInserted(PSUPDRVDEVEXT pDevExt, PSUPDRVSESSION pSession, void *pvUser)
+{
+ NOREF(pDevExt); NOREF(pSession); NOREF(pvUser);
+}
+
+
+void VBOXCALL supdrvOSSessionHashTabRemoved(PSUPDRVDEVEXT pDevExt, PSUPDRVSESSION pSession, void *pvUser)
+{
+ NOREF(pDevExt); NOREF(pSession); NOREF(pvUser);
+}
+
+
+void VBOXCALL supdrvOSObjInitCreator(PSUPDRVOBJ pObj, PSUPDRVSESSION pSession)
+{
+ NOREF(pObj);
+ NOREF(pSession);
+}
+
+
+bool VBOXCALL supdrvOSObjCanAccess(PSUPDRVOBJ pObj, PSUPDRVSESSION pSession, const char *pszObjName, int *prc)
+{
+ NOREF(pObj);
+ NOREF(pSession);
+ NOREF(pszObjName);
+ NOREF(prc);
+ return false;
+}
+
+
+bool VBOXCALL supdrvOSGetForcedAsyncTscMode(PSUPDRVDEVEXT pDevExt)
+{
+ NOREF(pDevExt);
+ return false;
+}
+
+
+bool VBOXCALL supdrvOSAreCpusOfflinedOnSuspend(void)
+{
+ return false;
+}
+
+
+bool VBOXCALL supdrvOSAreTscDeltasInSync(void)
+{
+ return false;
+}
+
+
+int VBOXCALL supdrvOSLdrOpen(PSUPDRVDEVEXT pDevExt, PSUPDRVLDRIMAGE pImage, const char *pszFilename)
+{
+ NOREF(pDevExt); NOREF(pImage); NOREF(pszFilename);
+ return VERR_NOT_SUPPORTED;
+}
+
+
+int VBOXCALL supdrvOSLdrValidatePointer(PSUPDRVDEVEXT pDevExt, PSUPDRVLDRIMAGE pImage, void *pv,
+ const uint8_t *pbImageBits, const char *pszSymbol)
+{
+ NOREF(pDevExt); NOREF(pImage); NOREF(pv); NOREF(pbImageBits); NOREF(pszSymbol);
+ return VERR_NOT_SUPPORTED;
+}
+
+
+int VBOXCALL supdrvOSLdrLoad(PSUPDRVDEVEXT pDevExt, PSUPDRVLDRIMAGE pImage, const uint8_t *pbImageBits, PSUPLDRLOAD pReq)
+{
+ NOREF(pDevExt); NOREF(pImage); NOREF(pbImageBits); NOREF(pReq);
+ return VERR_NOT_SUPPORTED;
+}
+
+
+void VBOXCALL supdrvOSLdrUnload(PSUPDRVDEVEXT pDevExt, PSUPDRVLDRIMAGE pImage)
+{
+ NOREF(pDevExt); NOREF(pImage);
+}
+
+
+void VBOXCALL supdrvOSLdrNotifyOpened(PSUPDRVDEVEXT pDevExt, PSUPDRVLDRIMAGE pImage, const char *pszFilename)
+{
+ NOREF(pDevExt); NOREF(pImage); NOREF(pszFilename);
+}
+
+
+void VBOXCALL supdrvOSLdrNotifyUnloaded(PSUPDRVDEVEXT pDevExt, PSUPDRVLDRIMAGE pImage)
+{
+ NOREF(pDevExt); NOREF(pImage);
+}
+
+
+int VBOXCALL supdrvOSLdrQuerySymbol(PSUPDRVDEVEXT pDevExt, PSUPDRVLDRIMAGE pImage,
+ const char *pszSymbol, size_t cchSymbol, void **ppvSymbol)
+{
+ RT_NOREF(pDevExt, pImage, pszSymbol, cchSymbol, ppvSymbol);
+ return VERR_WRONG_ORDER;
+}
+
+
+void VBOXCALL supdrvOSLdrRetainWrapperModule(PSUPDRVDEVEXT pDevExt, PSUPDRVLDRIMAGE pImage)
+{
+ RT_NOREF(pDevExt, pImage);
+ AssertFailed();
+}
+
+
+void VBOXCALL supdrvOSLdrReleaseWrapperModule(PSUPDRVDEVEXT pDevExt, PSUPDRVLDRIMAGE pImage)
+{
+ RT_NOREF(pDevExt, pImage);
+ AssertFailed();
+}
+
+#ifdef SUPDRV_WITH_MSR_PROBER
+
+int VBOXCALL supdrvOSMsrProberRead(uint32_t uMsr, RTCPUID idCpu, uint64_t *puValue)
+{
+ NOREF(uMsr); NOREF(idCpu); NOREF(puValue);
+ return VERR_NOT_SUPPORTED;
+}
+
+
+int VBOXCALL supdrvOSMsrProberWrite(uint32_t uMsr, RTCPUID idCpu, uint64_t uValue)
+{
+ NOREF(uMsr); NOREF(idCpu); NOREF(uValue);
+ return VERR_NOT_SUPPORTED;
+}
+
+
+int VBOXCALL supdrvOSMsrProberModify(RTCPUID idCpu, PSUPMSRPROBER pReq)
+{
+ NOREF(idCpu); NOREF(pReq);
+ return VERR_NOT_SUPPORTED;
+}
+
+#endif /* SUPDRV_WITH_MSR_PROBER */
+
+
+/**
+ * Callback for writing to the log buffer.
+ *
+ * @returns number of bytes written.
+ * @param pvArg Unused.
+ * @param pachChars Pointer to an array of utf-8 characters.
+ * @param cbChars Number of bytes in the character array pointed to by pachChars.
+ */
+static DECLCALLBACK(size_t) VBoxDrvLogOutput(void *pvArg, const char *pachChars, size_t cbChars)
+{
+ size_t cchWritten = 0;
+ while (cbChars-- > 0)
+ {
+ const uint16_t offLogHead = g_offLogHead;
+ const uint16_t offLogHeadNext = (offLogHead + 1) & (g_cchLogMax - 1);
+ if (offLogHeadNext == g_offLogTail)
+ break; /* no */
+ g_szLog[offLogHead] = *pachChars++;
+ g_offLogHead = offLogHeadNext;
+ cchWritten++;
+ }
+ return cchWritten;
+}
+
+
+SUPR0DECL(int) SUPR0PrintfV(const char *pszFormat, va_list va)
+{
+#if 0 //def DEBUG_bird
+ va_list va2;
+ va_copy(va2, va);
+ RTLogComPrintfV(pszFormat, va2);
+ va_end(va2);
+#endif
+
+ RTLogFormatV(VBoxDrvLogOutput, NULL, pszFormat, va);
+ return 0;
+}
+
+
+SUPR0DECL(uint32_t) SUPR0GetKernelFeatures(void)
+{
+ return 0;
+}
+
+
+SUPR0DECL(bool) SUPR0FpuBegin(bool fCtxHook)
+{
+ RT_NOREF(fCtxHook);
+ return false;
+}
+
+
+SUPR0DECL(void) SUPR0FpuEnd(bool fCtxHook)
+{
+ RT_NOREF(fCtxHook);
+}
+
diff --git a/src/VBox/HostDrivers/Support/os2/SUPDrv-os2.def b/src/VBox/HostDrivers/Support/os2/SUPDrv-os2.def
new file mode 100644
index 00000000..ae815d5e
--- /dev/null
+++ b/src/VBox/HostDrivers/Support/os2/SUPDrv-os2.def
@@ -0,0 +1,55 @@
+; $Id: SUPDrv-os2.def $
+;; @file
+; VBoxDrv - OS/2 definition file.
+;
+
+;
+; Copyright (C) 2007-2023 Oracle and/or its affiliates.
+;
+; This file is part of VirtualBox base platform packages, as
+; available from https://www.virtualbox.org.
+;
+; This program is free software; you can redistribute it and/or
+; modify it under the terms of the GNU General Public License
+; as published by the Free Software Foundation, in version 3 of the
+; License.
+;
+; This program is distributed in the hope that it will be useful, but
+; WITHOUT ANY WARRANTY; without even the implied warranty of
+; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+; General Public License for more details.
+;
+; You should have received a copy of the GNU General Public License
+; along with this program; if not, see <https://www.gnu.org/licenses>.
+;
+; The contents of this file may alternatively be used under the terms
+; of the Common Development and Distribution License Version 1.0
+; (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+; in the VirtualBox distribution, in which case the provisions of the
+; CDDL are applicable instead of those of the GPL.
+;
+; You may elect to license modified versions of this file under the
+; terms and conditions of either the GPL or the CDDL or both.
+;
+; SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+;
+
+
+PHYSICAL DEVICE VBoxDrv
+DESCRIPTION 'VirtualBox.org Support Driver for OS/2.'
+CODE PRELOAD EXECUTEREAD
+DATA PRELOAD
+; We're using wlink.exe, so this doesn't work.
+;SEGMENTS
+; DATA16 class 'FAR_DATA'
+; DATA16_INIT class 'FAR_DATA'
+;
+; CODE16 class 'CODE'
+; CODE16_INIT class 'CODE'
+;
+; CODE32 class 'CODE'
+; TEXT32 class 'CODE'
+;
+; DATA32 class 'DATA'
+; BSS32 class 'BSS'
+
diff --git a/src/VBox/HostDrivers/Support/os2/SUPDrvA-os2.asm b/src/VBox/HostDrivers/Support/os2/SUPDrvA-os2.asm
new file mode 100644
index 00000000..b27fcd0f
--- /dev/null
+++ b/src/VBox/HostDrivers/Support/os2/SUPDrvA-os2.asm
@@ -0,0 +1,973 @@
+; $Id: SUPDrvA-os2.asm $
+;; @file
+; VBoxDrv - OS/2 assembly file, the first file in the link.
+;
+
+;
+; Copyright (c) 2007 knut st. osmundsen <bird-src-spam@anduin.net>
+;
+; Permission is hereby granted, free of charge, to any person
+; obtaining a copy of this software and associated documentation
+; files (the "Software"), to deal in the Software without
+; restriction, including without limitation the rights to use,
+; copy, modify, merge, publish, distribute, sublicense, and/or sell
+; copies of the Software, and to permit persons to whom the
+; Software is furnished to do so, subject to the following
+; conditions:
+;
+; The above copyright notice and this permission notice shall be
+; included in all copies or substantial portions of the Software.
+;
+; THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+; EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+; OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+; NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+; HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+; WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+; FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+; OTHER DEALINGS IN THE SOFTWARE.
+;
+
+
+;*******************************************************************************
+;* Header Files *
+;*******************************************************************************
+%define RT_INCL_16BIT_SEGMENTS
+%include "iprt/asmdefs.mac"
+
+
+;*******************************************************************************
+;* Structures and Typedefs *
+;*******************************************************************************
+;;
+; Request packet header.
+struc PKTHDR
+ .cb resb 1
+ .unit resb 1
+ .cmd resb 1
+ .status resw 1
+ .res1 resd 1
+ .link resd 1
+endstruc
+
+
+;;
+; Init request packet - input.
+struc PKTINITIN
+ .cb resb 1
+ .unit resb 1
+ .cmd resb 1
+ .status resw 1
+ .res1 resd 1
+ .link resd 1
+
+ .data_1 resb 1
+ .fpfnDevHlp resd 1
+ .fpszArgs resd 1
+ .data_2 resb 1
+endstruc
+
+;;
+; Init request packet - output.
+struc PKTINITOUT
+ .cb resb 1
+ .unit resb 1
+ .cmd resb 1
+ .status resw 1
+ .res1 resd 1
+ .link resd 1
+
+ .cUnits resb 1 ; block devs only.
+ .cbCode16 resw 1
+ .cbData16 resw 1
+ .fpaBPBs resd 1 ; block devs only.
+ .data_2 resb 1
+endstruc
+
+;;
+; Open request packet.
+struc PKTOPEN
+ .cb resb 1
+ .unit resb 1
+ .cmd resb 1
+ .status resw 1
+ .res1 resd 1
+ .link resd 1
+ .sfn resw 1
+endstruc
+
+;;
+; Close request packet.
+struc PKTCLOSE
+ .cb resb 1
+ .unit resb 1
+ .cmd resb 1
+ .status resw 1
+ .res1 resd 1
+ .link resd 1
+ .sfn resw 1
+endstruc
+
+;;
+; IOCtl request packet.
+struc PKTIOCTL
+ .cb resb 1
+ .unit resb 1
+ .cmd resb 1
+ .status resw 1
+ .res1 resd 1
+ .link resd 1
+
+ .cat resb 1
+ .fun resb 1
+ .pParm resd 1
+ .pData resd 1
+ .sfn resw 1
+ .cbParm resw 1
+ .cbData resw 1
+endstruc
+
+;;
+; Read/Write request packet
+struc PKTRW
+ .cb resb 1
+ .unit resb 1
+ .cmd resb 1
+ .status resw 1
+ .res1 resd 1
+ .link resd 1
+
+ .media resb 1
+ .PhysTrans resd 1
+ .cbTrans resw 1
+ .start resd 1
+ .sfn resw 1
+endstruc
+
+
+
+;;
+; The two device headers.
+segment DATA16
+
+; Some devhdr.inc stuff.
+%define DEVLEV_3 0180h
+%define DEV_30 0800h
+%define DEV_CHAR_DEV 8000h
+%define DEV_16MB 0002h
+%define DEV_IOCTL2 0001h
+
+; Some dhcalls.h stuff.
+%define DevHlp_VirtToLin 05bh
+%define DevHlp_SAVE_MESSAGE 03dh
+%define DevHlp_PhysToVirt 015h
+
+; Fast IOCtl category, also defined in SUPDrvIOC.h
+%define SUP_CTL_CATEGORY_FAST 0c1h
+
+
+;*******************************************************************************
+;* External Symbols *
+;*******************************************************************************
+extern KernThunkStackTo32
+extern KernThunkStackTo16
+extern DOS16OPEN
+extern DOS16CLOSE
+extern DOS16WRITE
+extern NAME(VBoxDrvInit)
+extern NAME(VBoxDrvOpen)
+extern NAME(VBoxDrvClose)
+extern NAME(VBoxDrvIOCtl)
+extern NAME(VBoxDrvIOCtlFast)
+
+
+;;
+; Device headers. The first one is the one we'll be opening and the
+; latter is only used for 32-bit initialization.
+GLOBALNAME g_VBoxDrvHdr1
+ dw NAME(g_VBoxDrvHdr2) wrt DATA16 ; NextHeader.off
+ dw DATA16 ; NextHeader.sel
+ dw DEVLEV_3 | DEV_30 | DEV_CHAR_DEV; SDevAtt
+ dw NAME(VBoxDrvEP) wrt CODE16 ; StrategyEP
+ dw 0 ; InterruptEP
+ db 'vboxdrv$' ; DevName
+ dw 0 ; SDevProtCS
+ dw 0 ; SDevProtDS
+ dw 0 ; SDevRealCS
+ dw 0 ; SDevRealDS
+ dd DEV_16MB | DEV_IOCTL2 ; SDevCaps
+
+align 4
+GLOBALNAME g_VBoxDrvHdr2
+ dd 0ffffffffh ; NextHeader (NIL)
+ dw DEVLEV_3 | DEV_30 | DEV_CHAR_DEV; SDevAtt
+ dw NAME(VBoxDrvInitEP) wrt CODE16 ; StrategyEP
+ dw 0 ; InterruptEP
+ db 'vboxdr1$' ; DevName
+ dw 0 ; SDevProtCS
+ dw 0 ; SDevProtDS
+ dw 0 ; SDevRealCS
+ dw 0 ; SDevRealDS
+ dd DEV_16MB | DEV_IOCTL2 ; SDevCaps
+
+
+;; Tristate 32-bit initialization indicator [0 = need init, -1 = init failed, 1 init succeeded].
+; Check in the open path of the primary driver. The secondary driver will
+; open the primary one during it's init and thereby trigger the 32-bit init.
+GLOBALNAME g_fInitialized
+ db 0
+
+align 4
+;; Pointer to the device helper service routine
+; This is set during the initialization of the 2nd device driver.
+GLOBALNAME g_fpfnDevHlp
+ dd 0
+
+
+;; Where we write to the log.
+GLOBALNAME g_offLogHead
+ dw 0
+;; Where we read from the log.
+GLOBALNAME g_offLogTail
+ dw 0
+;; The size of the log. (power of two!)
+%define LOG_SIZE 16384
+GLOBALNAME g_cchLogMax
+ dw LOG_SIZE
+;; The log buffer.
+GLOBALNAME g_szLog
+ times LOG_SIZE db 0
+
+
+;
+; The init data.
+;
+segment DATA16_INIT
+GLOBALNAME g_InitDataStart
+
+;; Far pointer to the device argument.
+g_fpszArgs:
+ dd 0
+
+%if 0
+;; Message table for the Save_Message device helper.
+GLOBALNAME g_MsgTab
+ dw 1178 ; MsgId - 'MSG_REPLACEMENT_STRING'.
+ dw 1 ; cMsgStrings
+ dw NAME(g_szInitText) ; MsgStrings[0]
+ dw seg NAME(g_szInitText)
+%else
+;; Far pointer to DOS16WRITE (corrected set before called).
+; Just a temporary hack to work around a wlink issue.
+GLOBALNAME g_fpfnDos16Write
+ dw DOS16WRITE
+ dw seg DOS16WRITE
+%endif
+
+;; Size of the text currently in the g_szInitText buffer.
+GLOBALNAME g_cchInitText
+ dw 0
+;; The max size of text that can fit into the g_szInitText buffer.
+GLOBALNAME g_cchInitTextMax
+ dw 512
+;; The init text buffer.
+GLOBALNAME g_szInitText
+ times 512 db 0
+
+;
+; The 16-bit code segment.
+;
+segment CODE16
+
+
+;;
+; The strategy entry point (vboxdrv$).
+;
+; ss:bx -> request packet
+; ds:si -> device header
+;
+; Can clobber any registers it likes except SP.
+;
+BEGINPROC VBoxDrvEP
+ push ebp
+ mov ebp, esp
+ push es ; bp - 2
+ push bx ; bp - 4
+ and sp, 0fffch
+
+ ;
+ ; Check for the most frequent first.
+ ;
+ cmp byte [es:bx + PKTHDR.cmd], 10h ; Generic IOCtl
+ jne near VBoxDrvEP_NotGenIOCtl
+
+
+ ;
+ ; Generic I/O Control Request.
+ ;
+VBoxDrvEP_GenIOCtl:
+
+ ; Fast IOCtl?
+ cmp byte [es:bx + PKTIOCTL.cat], SUP_CTL_CATEGORY_FAST
+ jne VBoxDrvEP_GenIOCtl_Other
+
+ ;
+ ; Fast IOCtl.
+ ; DECLASM(int) VBoxDrvIOCtlFast(uint16_t sfn, uint8_t iFunction)
+ ;
+VBoxDrvEP_GenIOCtl_Fast:
+ ; function.
+ movzx edx, byte [es:bx + PKTIOCTL.fun]
+ push edx ; 04h
+
+ ; system file number.
+ movzx eax, word [es:bx + PKTIOCTL.sfn]
+ push eax ; 00h
+
+ ; go to the 32-bit code
+ ;jmp far dword NAME(VBoxDrvEP_GenIOCtl_Fast_32) wrt FLAT
+ db 066h
+ db 0eah
+ dd NAME(VBoxDrvEP_GenIOCtl_Fast_32) ;wrt FLAT
+ dw TEXT32 wrt FLAT
+segment TEXT32
+GLOBALNAME VBoxDrvEP_GenIOCtl_Fast_32
+
+ ; switch stack to 32-bit.
+ mov ax, DATA32 wrt FLAT
+ mov ds, ax
+ mov es, ax
+ call KernThunkStackTo32
+
+ ; call the C code (don't cleanup the stack).
+ call NAME(VBoxDrvIOCtlFast)
+
+ ; switch back the stack.
+ push eax
+ call KernThunkStackTo16
+ pop eax
+
+ ; jump back to the 16-bit code.
+ ;jmp far dword NAME(VBoxDrvEP_GenIOCtl_Fast_16) wrt CODE16
+ db 066h
+ db 0eah
+ dw NAME(VBoxDrvEP_GenIOCtl_Fast_16) wrt CODE16
+ dw CODE16
+segment CODE16
+GLOBALNAME VBoxDrvEP_GenIOCtl_Fast_16
+ les bx, [bp - 4] ; Reload the packet pointer.
+ or eax, eax
+ jnz near VBoxDrvEP_GeneralFailure
+
+ ; setup output stuff.
+ xor eax, eax
+ mov [es:bx + PKTIOCTL.cbParm], eax ; update cbParm and cbData.
+ mov word [es:bx + PKTHDR.status], 00100h ; done, ok.
+
+ mov sp, bp
+ pop ebp
+ retf
+
+ ;
+ ; Other IOCtl (slow)
+ ;
+VBoxDrvEP_GenIOCtl_Other:
+ mov eax, [es:bx + PKTIOCTL.cbParm] ; Load cbParm and cbData
+ push eax ; 1eh - in/out data size.
+ ; 1ch - in/out parameter size.
+ push edx ; 18h - pointer to data size (filled in later).
+ push ecx ; 14h - pointer to param size (filled in later).
+
+ ; pData (convert to flat 32-bit)
+ mov ax, word [es:bx + PKTIOCTL.pData + 2] ; selector
+ cmp ax, 3 ; <= 3 -> nil selector...
+ jbe .no_data
+ movzx esi, word [es:bx + PKTIOCTL.pData] ; offset
+ mov dl, DevHlp_VirtToLin
+ call far [NAME(g_fpfnDevHlp)]
+ jc near VBoxDrvEP_GeneralFailure
+ jmp .finish_data
+.no_data:
+ xor eax, eax
+.finish_data:
+ push eax ; 10h
+
+ ; pParm (convert to flat 32-bit)
+ mov ax, word [es:bx + PKTIOCTL.pParm + 2] ; selector
+ cmp ax, 3 ; <= 3 -> nil selector...
+ jbe .no_parm
+ movzx esi, word [es:bx + PKTIOCTL.pParm] ; offset
+ mov dl, DevHlp_VirtToLin
+ call far [NAME(g_fpfnDevHlp)]
+ jc near VBoxDrvEP_GeneralFailure
+ jmp .finish_parm
+.no_parm:
+ xor eax, eax
+.finish_parm:
+ push eax ; 0ch
+
+ ; function.
+ movzx edx, byte [es:bx + PKTIOCTL.fun]
+ push edx ; 08h
+
+ ; category.
+ movzx ecx, byte [es:bx + PKTIOCTL.cat]
+ push ecx ; 04h
+
+ ; system file number.
+ movzx eax, word [es:bx + PKTIOCTL.sfn]
+ push eax ; 00h
+
+ ; go to the 32-bit code
+ ;jmp far dword NAME(VBoxDrvEP_GenIOCtl_Other_32) wrt FLAT
+ db 066h
+ db 0eah
+ dd NAME(VBoxDrvEP_GenIOCtl_Other_32) ;wrt FLAT
+ dw TEXT32 wrt FLAT
+segment TEXT32
+GLOBALNAME VBoxDrvEP_GenIOCtl_Other_32
+
+ ; switch stack to 32-bit.
+ mov ax, DATA32 wrt FLAT
+ mov ds, ax
+ mov es, ax
+ call KernThunkStackTo32
+
+ ; update in/out parameter pointers
+ lea eax, [esp + 1ch]
+ mov [esp + 14h], eax
+ lea edx, [esp + 1eh]
+ mov [esp + 18h], edx
+
+ ; call the C code (don't cleanup the stack).
+ call NAME(VBoxDrvIOCtl)
+
+ ; switch back the stack.
+ push eax
+ call KernThunkStackTo16
+ pop eax
+
+ ; jump back to the 16-bit code.
+ ;jmp far dword NAME(VBoxDrvEP_GenIOCtl_Other_16) wrt CODE16
+ db 066h
+ db 0eah
+ dw NAME(VBoxDrvEP_GenIOCtl_Other_16) wrt CODE16
+ dw CODE16
+segment CODE16
+GLOBALNAME VBoxDrvEP_GenIOCtl_Other_16
+ les bx, [bp - 4] ; Reload the packet pointer.
+ or eax, eax
+ jnz near VBoxDrvEP_GeneralFailure
+
+ ; setup output stuff.
+ mov edx, esp
+ mov eax, [ss:edx + 1ch] ; output sizes.
+ mov [es:bx + PKTIOCTL.cbParm], eax ; update cbParm and cbData.
+ mov word [es:bx + PKTHDR.status], 00100h ; done, ok.
+
+ mov sp, bp
+ pop ebp
+ retf
+
+
+ ;
+ ; Less Performance Critical Requests.
+ ;
+VBoxDrvEP_NotGenIOCtl:
+ cmp byte [es:bx + PKTHDR.cmd], 0dh ; Open
+ je VBoxDrvEP_Open
+ cmp byte [es:bx + PKTHDR.cmd], 0eh ; Close
+ je VBoxDrvEP_Close
+ cmp byte [es:bx + PKTHDR.cmd], 00h ; Init
+ je VBoxDrvEP_Init
+ cmp byte [es:bx + PKTHDR.cmd], 04h ; Read
+ je near VBoxDrvEP_Read
+ jmp near VBoxDrvEP_NotSupported
+
+
+ ;
+ ; Open Request. w/ ring-0 init.
+ ;
+VBoxDrvEP_Open:
+ cmp byte [NAME(g_fInitialized)], 1
+ jne VBoxDrvEP_OpenOther
+
+ ; First argument, the system file number.
+ movzx eax, word [es:bx + PKTOPEN.sfn]
+ push eax
+
+ ; go to the 32-bit code
+ ;jmp far dword NAME(VBoxDrvEP_Open_32) wrt FLAT
+ db 066h
+ db 0eah
+ dd NAME(VBoxDrvEP_Open_32) ;wrt FLAT
+ dw TEXT32 wrt FLAT
+segment TEXT32
+GLOBALNAME VBoxDrvEP_Open_32
+
+ ; switch stack to 32-bit.
+ mov ax, DATA32 wrt FLAT
+ mov ds, ax
+ mov es, ax
+ call KernThunkStackTo32
+
+ ; call the C code.
+ call NAME(VBoxDrvOpen)
+
+ ; switch back the stack.
+ push eax
+ call KernThunkStackTo16
+ pop eax
+
+ ; jump back to the 16-bit code.
+ ;jmp far dword NAME(VBoxDrvEP_Open_32) wrt CODE16
+ db 066h
+ db 0eah
+ dw NAME(VBoxDrvEP_Open_16) wrt CODE16
+ dw CODE16
+segment CODE16
+GLOBALNAME VBoxDrvEP_Open_16
+ les bx, [bp - 4] ; Reload the packet pointer.
+ or eax, eax
+ jnz near VBoxDrvEP_GeneralFailure
+ mov word [es:bx + PKTHDR.status], 00100h ; done, ok.
+ jmp near VBoxDrvEP_Done
+
+ ; Initializing or failed init?
+VBoxDrvEP_OpenOther:
+ cmp byte [NAME(g_fInitialized)], 0
+ jne VBoxDrvEP_OpenFailed
+
+ mov byte [NAME(g_fInitialized)], -1
+ call NAME(VBoxDrvRing0Init)
+ cmp byte [NAME(g_fInitialized)], 1
+ je VBoxDrvEP_Open
+
+VBoxDrvEP_OpenFailed:
+ mov word [es:bx + PKTHDR.status], 0810fh ; error, done, init failed.
+ jmp near VBoxDrvEP_Done
+
+
+ ;
+ ; Close Request.
+ ;
+VBoxDrvEP_Close:
+ ; First argument, the system file number.
+ movzx eax, word [es:bx + PKTOPEN.sfn]
+ push eax
+
+ ; go to the 32-bit code
+ ;jmp far dword NAME(VBoxDrvEP_Close_32) wrt FLAT
+ db 066h
+ db 0eah
+ dd NAME(VBoxDrvEP_Close_32) ;wrt FLAT
+ dw TEXT32 wrt FLAT
+segment TEXT32
+GLOBALNAME VBoxDrvEP_Close_32
+
+ ; switch stack to 32-bit.
+ mov ax, DATA32 wrt FLAT
+ mov ds, ax
+ mov es, ax
+ call KernThunkStackTo32
+
+ ; call the C code.
+ call NAME(VBoxDrvClose)
+
+ ; switch back the stack.
+ push eax
+ call KernThunkStackTo16
+ pop eax
+
+ ; jump back to the 16-bit code.
+ ;jmp far dword NAME(VBoxDrvEP_Close_32) wrt CODE16
+ db 066h
+ db 0eah
+ dw NAME(VBoxDrvEP_Close_16) wrt CODE16
+ dw CODE16
+segment CODE16
+GLOBALNAME VBoxDrvEP_Close_16
+ les bx, [bp - 4] ; Reload the packet pointer.
+ or eax, eax
+ jnz near VBoxDrvEP_GeneralFailure
+ mov word [es:bx + PKTHDR.status], 00100h ; done, ok.
+ jmp near VBoxDrvEP_Done
+
+
+ ;
+ ; Init Request.
+ ; The other driver header will do this.
+ ;
+VBoxDrvEP_Init:
+ mov word [es:bx + PKTHDR.status], 00100h ; done, ok.
+ mov byte [es:bx + PKTINITOUT.cUnits], 0
+ mov word [es:bx + PKTINITOUT.cbCode16], NAME(g_InitCodeStart) wrt CODE16
+ mov word [es:bx + PKTINITOUT.cbData16], NAME(g_InitDataStart) wrt DATA16
+ mov dword [es:bx + PKTINITOUT.fpaBPBs], 0
+ jmp near VBoxDrvEP_Done
+
+
+ ;
+ ; Read Request.
+ ; Return log data.
+ ;
+VBoxDrvEP_Read:
+ ; Any log data available?
+ xor dx, dx
+ mov ax, [NAME(g_offLogTail)]
+ cmp ax, [NAME(g_offLogHead)]
+ jz near .log_done
+
+ ; create a temporary mapping of the physical buffer. Docs claims it trashes nearly everything...
+ push ebp
+ mov cx, [es:bx + PKTRW.cbTrans]
+ push cx
+ mov ax, [es:bx + PKTRW.PhysTrans + 2]
+ mov bx, [es:bx + PKTRW.PhysTrans]
+ mov dh, 1
+ mov dl, DevHlp_PhysToVirt
+ call far [NAME(g_fpfnDevHlp)]
+ pop bx ; bx = cbTrans
+ pop ebp
+ jc near .log_phystovirt_failed
+ ; es:di -> the output buffer.
+
+ ; setup the copy operation.
+ mov ax, [NAME(g_offLogTail)]
+ xor dx, dx ; dx tracks the number of bytes copied.
+.log_loop:
+ mov cx, [NAME(g_offLogHead)]
+ cmp ax, cx
+ je .log_done
+ jb .log_loop_before
+ mov cx, LOG_SIZE
+.log_loop_before: ; cx = end offset
+ sub cx, ax ; cx = sequential bytes to copy.
+ cmp cx, bx
+ jbe .log_loop_min
+ mov cx, bx ; output buffer is smaller than available data.
+.log_loop_min:
+ mov si, NAME(g_szLog)
+ add si, ax ; ds:si -> the log buffer.
+ add dx, cx ; update output counter
+ add ax, cx ; calc new offLogTail
+ and ax, LOG_SIZE - 1
+ rep movsb ; do the copy
+ mov [NAME(g_offLogTail)], ax ; commit the read.
+ jmp .log_loop
+
+.log_done:
+ les bx, [bp - 4] ; Reload the packet pointer.
+ mov word [es:bx + PKTRW.cbTrans], dx
+ mov word [es:bx + PKTHDR.status], 00100h ; done, ok.
+ jmp near VBoxDrvEP_Done
+
+.log_phystovirt_failed:
+ les bx, [bp - 4] ; Reload the packet pointer.
+ jmp VBoxDrvEP_GeneralFailure
+
+
+ ;
+ ; Return 'unknown command' error.
+ ;
+VBoxDrvEP_NotSupported:
+ mov word [es:bx + PKTHDR.status], 08103h ; error, done, unknown command.
+ jmp VBoxDrvEP_Done
+
+ ;
+ ; Return 'general failure' error.
+ ;
+VBoxDrvEP_GeneralFailure:
+ mov word [es:bx + PKTHDR.status], 0810ch ; error, done, general failure.
+ jmp VBoxDrvEP_Done
+
+ ;
+ ; Non-optimized return path.
+ ;
+VBoxDrvEP_Done:
+ mov sp, bp
+ pop ebp
+ retf
+ENDPROC VBoxDrvEP
+
+
+;;
+; The helper device entry point.
+;
+; This is only used to do the DosOpen on the main driver so we can
+; do ring-3 init and report failures.
+;
+GLOBALNAME VBoxDrvInitEP
+ ; The only request we're servicing is the 'init' one.
+ cmp word [es:bx + PKTHDR.cmd], 0
+ je near NAME(VBoxDrvInitEPServiceInitReq)
+
+ ; Ok, it's not the init request, just fail it.
+ mov word [es:bx + PKTHDR.status], 08103h ; error, done, unknown command.
+ retf
+
+
+;
+; The 16-bit init code.
+;
+segment CODE16_INIT
+GLOBALNAME g_InitCodeStart
+
+;; The device name for DosOpen.
+g_szDeviceName:
+ db '\DEV\vboxdrv$', 0
+
+; icsdebug can't see where stuff starts otherwise. (kDevTest)
+int3
+int3
+int3
+int3
+int3
+int3
+
+;;
+; The Ring-3 init code.
+;
+BEGINPROC VBoxDrvInitEPServiceInitReq
+ push ebp
+ mov ebp, esp
+ push es ; bp - 2
+ push sp ; bp - 4
+ push -1 ; bp - 6: hfOpen
+ push 0 ; bp - 8: usAction
+ and sp, 0fffch
+
+ ; check for the init package.
+ cmp word [es:bx + PKTHDR.cmd], 0
+ jne near .not_init
+
+ ;
+ ; Copy the data out of the init packet.
+ ;
+ mov eax, [es:bx + PKTINITIN.fpfnDevHlp]
+ mov [NAME(g_fpfnDevHlp)], eax
+ mov edx, [es:bx + PKTINITIN.fpszArgs]
+ mov [g_fpszArgs], edx
+
+ ;
+ ; Open the first driver, close it, and check status.
+ ;
+
+ ; APIRET _Pascal DosOpen(PSZ pszFname, PHFILE phfOpen, PUSHORT pusAction,
+ ; ULONG ulFSize, USHORT usAttr, USHORT fsOpenFlags,
+ ; USHORT fsOpenMode, ULONG ulReserved);
+ push seg g_szDeviceName ; pszFname
+ push g_szDeviceName
+ push ss ; phfOpen
+ lea dx, [bp - 6]
+ push dx
+ push ss ; pusAction
+ lea dx, [bp - 8]
+ push dx
+ push dword 0 ; ulFSize
+ push 0 ; usAttr = FILE_NORMAL
+ push 1 ; fsOpenFlags = FILE_OPEN
+ push 00040h ; fsOpenMode = OPEN_SHARE_DENYNONE | OPEN_ACCESS_READONLY
+ push dword 0 ; ulReserved
+ call far DOS16OPEN
+
+ push ax ; Quickly flush any text.
+ call NAME(VBoxDrvInitFlushText)
+ pop ax
+
+ or ax, ax
+ jnz .done_err
+
+ ; APIRET APIENTRY DosClose(HFILE hf);
+ mov cx, [bp - 6]
+ push cx
+ call far DOS16CLOSE
+ or ax, ax
+ jnz .done_err ; This can't happen (I hope).
+
+ ;
+ ; Ok, we're good.
+ ;
+ mov word [es:bx + PKTHDR.status], 00100h ; done, ok.
+ mov byte [es:bx + PKTINITOUT.cUnits], 0
+ mov word [es:bx + PKTINITOUT.cbCode16], NAME(g_InitCodeStart) wrt CODE16
+ mov word [es:bx + PKTINITOUT.cbData16], NAME(g_InitDataStart) wrt DATA16
+ mov dword [es:bx + PKTINITOUT.fpaBPBs], 0
+ jmp .done
+
+ ;
+ ; Init failure.
+ ;
+.done_err:
+ mov word [es:bx + PKTHDR.status], 0810fh ; error, done, init failed.
+ mov byte [es:bx + PKTINITOUT.cUnits], 0
+ mov word [es:bx + PKTINITOUT.cbCode16], 0
+ mov word [es:bx + PKTINITOUT.cbData16], 0
+ mov dword [es:bx + PKTINITOUT.fpaBPBs], 0
+ jmp .done
+
+ ;
+ ; Not init, return 'unknown command'.
+ ;
+.not_init:
+ mov word [es:bx + PKTHDR.status], 08103h ; error, done, unknown command.
+ jmp .done
+
+ ;
+ ; Request done.
+ ;
+.done:
+ mov sp, bp
+ pop ebp
+ retf
+ENDPROC VBoxDrvInitEPServiceInitReq
+
+
+;;
+; The Ring-0 init code.
+;
+BEGINPROC VBoxDrvRing0Init
+ push es
+ push esi
+ push ebp
+ mov ebp, esp
+ and sp, 0fffch
+
+ ;
+ ; Thunk the argument string pointer first.
+ ;
+ movzx esi, word [g_fpszArgs] ; offset
+ mov ax, [g_fpszArgs + 2] ; selector
+ mov dl, DevHlp_VirtToLin
+ call far [NAME(g_fpfnDevHlp)]
+ jc near VBoxDrvRing0Init_done ; eax is non-zero on failure (can't happen)
+ push eax ; 00h - pszArgs (for VBoxDrvInit).
+
+ ;
+ ; Do 16-bit init?
+ ;
+
+
+ ;
+ ; Do 32-bit init
+ ;
+ ;jmp far dword NAME(VBoxDrvRing0Init_32) wrt FLAT
+ db 066h
+ db 0eah
+ dd NAME(VBoxDrvRing0Init_32) ;wrt FLAT
+ dw TEXT32 wrt FLAT
+segment TEXT32
+GLOBALNAME VBoxDrvRing0Init_32
+
+ ; switch stack to 32-bit.
+ mov ax, DATA32 wrt FLAT
+ mov ds, ax
+ mov es, ax
+ call KernThunkStackTo32
+
+ ; call the C code.
+ call NAME(VBoxDrvInit)
+
+ ; switch back the stack and reload ds.
+ push eax
+ call KernThunkStackTo16
+ pop eax
+
+ mov dx, seg NAME(g_fInitialized)
+ mov ds, dx
+
+ ; jump back to the 16-bit code.
+ ;jmp far dword NAME(VBoxDrvRing0Init_16) wrt CODE16
+ db 066h
+ db 0eah
+ dw NAME(VBoxDrvRing0Init_16) wrt CODE16
+ dw CODE16_INIT
+segment CODE16_INIT
+GLOBALNAME VBoxDrvRing0Init_16
+
+ ; check the result and set g_fInitialized on success.
+ or eax, eax
+ jnz VBoxDrvRing0Init_done
+ mov byte [NAME(g_fInitialized)], 1
+
+VBoxDrvRing0Init_done:
+ mov sp, bp
+ pop ebp
+ pop esi
+ pop es
+ ret
+ENDPROC VBoxDrvRing0Init
+
+
+;;
+; Flush any text in the text buffer.
+;
+BEGINPROC VBoxDrvInitFlushText
+ push bp
+ mov bp, sp
+
+ ; Anything in the buffer?
+ mov ax, [NAME(g_cchInitText)]
+ or ax, ax
+ jz .done
+
+%if 1
+ ; Write it to STDOUT.
+ ; APIRET _Pascal DosWrite(HFILE hf, PVOID pvBuf, USHORT cbBuf, PUSHORT pcbBytesWritten);
+ push ax ; bp - 2 : cbBytesWritten
+ mov cx, sp
+ push 1 ; STDOUT
+ push seg NAME(g_szInitText) ; pvBuf
+ push NAME(g_szInitText)
+ push ax ; cbBuf
+ push ss ; pcbBytesWritten
+ push cx
+%if 0 ; wlink generates a non-aliased fixup here which results in 16-bit offset with the flat 32-bit selector.
+ call far DOS16WRITE
+%else
+ ; convert flat pointer to a far pointer using the tiled algorithm.
+ push ds
+ mov ax, DATA32 wrt FLAT
+ mov ds, ax
+ mov eax, g_pfnDos16Write wrt FLAT
+ movzx eax, word [eax + 2] ; High word of the flat address (in DATA32).
+ shl ax, 3
+ or ax, 0007h
+ pop ds
+ mov [NAME(g_fpfnDos16Write) + 2], ax ; Update the selector (in DATA16_INIT).
+ ; do the call
+ call far [NAME(g_fpfnDos16Write)]
+%endif
+
+%else ; alternative workaround for the wlink issue.
+ ; Use the save message devhlp.
+ push esi
+ push ebx
+ xor bx, bx
+ mov si, NAME(g_MsgTab)
+ mov dx, seg NAME(g_MsgTab)
+ mov ds, dx
+ mov dl, DevHlp_SAVE_MESSAGE
+ call far [NAME(g_fpfnDevHlp)]
+ pop ebx
+ pop esi
+%endif
+
+ ; Empty the buffer.
+ mov word [NAME(g_cchInitText)], 0
+ mov byte [NAME(g_szInitText)], 0
+
+.done:
+ mov sp, bp
+ pop bp
+ ret
+ENDPROC VBoxDrvInitFlushText
+
+
+
+;;
+; This must be present
+segment DATA32
+g_pfnDos16Write:
+ dd DOS16WRITE ; flat
+
diff --git a/src/VBox/HostDrivers/Support/os2/SUPLib-os2.cpp b/src/VBox/HostDrivers/Support/os2/SUPLib-os2.cpp
new file mode 100644
index 00000000..f391f2ad
--- /dev/null
+++ b/src/VBox/HostDrivers/Support/os2/SUPLib-os2.cpp
@@ -0,0 +1,204 @@
+/* $Id: SUPLib-os2.cpp $ */
+/** @file
+ * VirtualBox Support Library - OS/2 specific parts.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#define INCL_BASE
+#define INCL_ERRORS
+#include <os2.h>
+#undef RT_MAX
+
+#ifdef IN_SUP_HARDENED_R3
+# undef DEBUG /* Warning: disables RT_STRICT */
+# define LOG_DISABLED
+# define RTLOG_REL_DISABLED
+# include <iprt/log.h>
+#endif
+
+#include <VBox/types.h>
+#include <VBox/sup.h>
+#include <VBox/param.h>
+#include <VBox/err.h>
+#include <VBox/log.h>
+#include <iprt/path.h>
+#include <iprt/assert.h>
+#include <iprt/err.h>
+#include "../SUPLibInternal.h"
+#include "../SUPDrvIOC.h"
+
+#include <errno.h>
+#include <unistd.h>
+#include <stdlib.h>
+
+
+/*********************************************************************************************************************************
+* Defined Constants And Macros *
+*********************************************************************************************************************************/
+/** OS/2 Device name. */
+#define DEVICE_NAME "/dev/vboxdrv$"
+
+
+
+DECLHIDDEN(int) suplibOsInit(PSUPLIBDATA pThis, bool fPreInited, uint32_t fFlags, SUPINITOP *penmWhat, PRTERRINFO pErrInfo)
+{
+ /*
+ * Nothing to do if pre-inited.
+ */
+ if (fPreInited)
+ return VINF_SUCCESS;
+
+ /*
+ * Try open the device.
+ */
+ ULONG ulAction = 0;
+ HFILE hDevice = (HFILE)-1;
+ APIRET rc = DosOpen((PCSZ)DEVICE_NAME,
+ &hDevice,
+ &ulAction,
+ 0,
+ FILE_NORMAL,
+ OPEN_ACTION_FAIL_IF_NEW | OPEN_ACTION_OPEN_IF_EXISTS,
+ OPEN_FLAGS_NOINHERIT | OPEN_SHARE_DENYNONE | OPEN_ACCESS_READWRITE,
+ NULL);
+ if (rc)
+ {
+ int vrc;
+ switch (rc)
+ {
+ case ERROR_FILE_NOT_FOUND:
+ case ERROR_PATH_NOT_FOUND: vrc = VERR_VM_DRIVER_NOT_INSTALLED; break;
+ default: vrc = VERR_VM_DRIVER_OPEN_ERROR; break;
+ }
+ LogRel(("Failed to open \"%s\", rc=%d, vrc=%Rrc\n", DEVICE_NAME, rc, vrc));
+ return vrc;
+ }
+
+ pThis->hDevice = hDevice;
+ pThis->fUnrestricted = true;
+ RT_NOREF(fFlags);
+ return VINF_SUCCESS;
+}
+
+
+DECLHIDDEN(int) suplibOsTerm(PSUPLIBDATA pThis)
+{
+ /*
+ * Check if we're inited at all.
+ */
+ if (pThis->hDevice != (intptr_t)NIL_RTFILE)
+ {
+ APIRET rc = DosClose((HFILE)pThis->hDevice);
+ AssertMsg(rc == NO_ERROR, ("%d\n", rc)); NOREF(rc);
+ pThis->hDevice = (intptr_t)NIL_RTFILE;
+ }
+
+ return 0;
+}
+
+
+#ifndef IN_SUP_HARDENED_R3
+
+DECLHIDDEN(int) suplibOsInstall(void)
+{
+ /** @remark OS/2: Not supported */
+ return VERR_NOT_SUPPORTED;
+}
+
+
+DECLHIDDEN(int) suplibOsUninstall(void)
+{
+ /** @remark OS/2: Not supported */
+ return VERR_NOT_SUPPORTED;
+}
+
+
+DECLHIDDEN(int) suplibOsIOCtl(PSUPLIBDATA pThis, uintptr_t uFunction, void *pvReq, size_t cbReq)
+{
+ ULONG cbReturned = sizeof(SUPREQHDR);
+ int rc = DosDevIOCtl((HFILE)pThis->hDevice, SUP_CTL_CATEGORY, uFunction,
+ pvReq, cbReturned, &cbReturned,
+ NULL, 0, NULL);
+ if (RT_LIKELY(rc == NO_ERROR))
+ return VINF_SUCCESS;
+ return RTErrConvertFromOS2(rc);
+}
+
+
+DECLHIDDEN(int) suplibOsIOCtlFast(PSUPLIBDATA pThis, uintptr_t uFunction, uintptr_t idCpu)
+{
+ NOREF(idCpu);
+ int32_t rcRet = VERR_INTERNAL_ERROR;
+ int rc = DosDevIOCtl((HFILE)pThis->hDevice, SUP_CTL_CATEGORY_FAST, uFunction,
+ NULL, 0, NULL,
+ NULL, 0, NULL);
+ if (RT_LIKELY(rc == NO_ERROR))
+ rc = rcRet;
+ else
+ rc = RTErrConvertFromOS2(rc);
+ return rc;
+}
+
+
+DECLHIDDEN(int) suplibOsPageAlloc(PSUPLIBDATA pThis, size_t cPages, uint32_t fFlags, void **ppvPages)
+{
+ RT_NOREF(pThis, fFlags);
+ *ppvPages = NULL;
+ int rc = DosAllocMem(ppvPages, cPages << PAGE_SHIFT, PAG_READ | PAG_WRITE | PAG_EXECUTE | PAG_COMMIT | OBJ_ANY);
+ if (rc == ERROR_INVALID_PARAMETER)
+ rc = DosAllocMem(ppvPages, cPages << PAGE_SHIFT, PAG_READ | PAG_WRITE | PAG_EXECUTE | PAG_COMMIT | OBJ_ANY);
+ if (!rc)
+ rc = VINF_SUCCESS;
+ else
+ rc = RTErrConvertFromOS2(rc);
+ return rc;
+}
+
+
+DECLHIDDEN(int) suplibOsPageFree(PSUPLIBDATA pThis, void *pvPages, size_t /* cPages */)
+{
+ NOREF(pThis);
+ if (pvPages)
+ {
+ int rc = DosFreeMem(pvPages);
+ Assert(!rc); NOREF(rc);
+ }
+ return VINF_SUCCESS;
+}
+
+#endif /* !IN_SUP_HARDENED_R3 */
+
diff --git a/src/VBox/HostDrivers/Support/os2/SUPR0IdcClient-os2.c b/src/VBox/HostDrivers/Support/os2/SUPR0IdcClient-os2.c
new file mode 100644
index 00000000..034986e4
--- /dev/null
+++ b/src/VBox/HostDrivers/Support/os2/SUPR0IdcClient-os2.c
@@ -0,0 +1,61 @@
+/* $Id: SUPR0IdcClient-os2.c $ */
+/** @file
+ * VirtualBox Support Driver - IDC Client Lib, OS/2 Specific Code.
+ */
+
+/*
+ * Copyright (C) 2008-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include "../SUPR0IdcClientInternal.h"
+#include <iprt/errcore.h>
+
+
+int VBOXCALL supR0IdcNativeOpen(PSUPDRVIDCHANDLE pHandle, PSUPDRVIDCREQCONNECT pReq)
+{
+ return VERR_NOT_SUPPORTED;
+}
+
+
+int VBOXCALL supR0IdcNativeClose(PSUPDRVIDCHANDLE pHandle, PSUPDRVIDCREQHDR pReq)
+{
+ return VERR_NOT_SUPPORTED;
+}
+
+
+int VBOXCALL supR0IdcNativeCall(PSUPDRVIDCHANDLE pHandle, uint32_t iReq, PSUPDRVIDCREQHDR pReq)
+{
+ return VERR_NOT_SUPPORTED;
+}
+
diff --git a/src/VBox/HostDrivers/Support/posix/SUPR3HardenedMain-posix.cpp b/src/VBox/HostDrivers/Support/posix/SUPR3HardenedMain-posix.cpp
new file mode 100644
index 00000000..fe55c4da
--- /dev/null
+++ b/src/VBox/HostDrivers/Support/posix/SUPR3HardenedMain-posix.cpp
@@ -0,0 +1,708 @@
+/* $Id: SUPR3HardenedMain-posix.cpp $ */
+/** @file
+ * VirtualBox Support Library - Hardened main(), posix bits.
+ */
+
+/*
+ * Copyright (C) 2017-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include <VBox/err.h>
+#include <VBox/dis.h>
+#include <VBox/sup.h>
+
+#include <iprt/path.h>
+#include <iprt/string.h>
+#include <iprt/x86.h>
+
+#include <dlfcn.h>
+#include <sys/mman.h>
+#if defined(RT_OS_SOLARIS)
+# include <link.h>
+#endif
+#include <stdio.h>
+#include <stdint.h>
+
+#include "SUPLibInternal.h"
+
+
+/*********************************************************************************************************************************
+* Defined Constants And Macros *
+*********************************************************************************************************************************/
+
+/**
+ * Memory for code patching.
+ */
+#define DLOPEN_PATCH_MEMORY_SIZE _4K
+
+
+/*********************************************************************************************************************************
+* Structures and Typedefs *
+*********************************************************************************************************************************/
+#ifndef SUP_HARDENED_WITHOUT_DLOPEN_PATCHING
+/**
+ * Callback (SUPHARDENEDPOSIXHOOK::pfnResolv) for triggering lazy GOT resolver.
+ *
+ * This generally just calls the API in a harmless manner and triggers the lazy
+ * resolving of the symbol, ensuring a proper address in the GOT/PLT entry.
+ *
+ * On Solaris dlsym() will return the value in the GOT/PLT entry. We don't wish
+ * to patch the lazy loader trampoline function, but rather the real function!
+ */
+typedef DECLCALLBACKTYPE(void, FNSUPHARDENEDSYMRESOLVE,(void));
+/** Pointer to FNSUPHARDENEDSYMRESOLVE. */
+typedef FNSUPHARDENEDSYMRESOLVE *PFNSUPHARDENEDSYMRESOLVE;
+
+/**
+ * A hook descriptor.
+ */
+typedef struct SUPHARDENEDPOSIXHOOK
+{
+ /** The symbol to hook. */
+ const char *pszSymbol;
+ /** The intercepting wrapper doing additional checks. */
+ PFNRT pfnHook;
+ /** Where to store the pointer to the code into patch memory
+ * which resumes the original call.
+ * @note uintptr_t instead of PFNRT is for Clang 11. */
+ uintptr_t *ppfnRealResume;
+ /** Pointer to the resolver method used on Solaris. */
+ PFNSUPHARDENEDSYMRESOLVE pfnResolve;
+} SUPHARDENEDPOSIXHOOK;
+/** Pointer to a hook descriptor. */
+typedef SUPHARDENEDPOSIXHOOK *PSUPHARDENEDPOSIXHOOK;
+/** Pointer to a const hook descriptor. */
+typedef const SUPHARDENEDPOSIXHOOK *PCSUPHARDENEDPOSIXHOOK;
+
+/** dlopen() declaration. */
+typedef void *FNDLOPEN(const char *pszFilename, int fFlags);
+/** Pointer to dlopen. */
+typedef FNDLOPEN *PFNDLOPEN;
+
+#ifdef SUP_HARDENED_WITH_DLMOPEN
+/** dlmopen() declaration */
+typedef void *FNDLMOPEN(Lmid_t idLm, const char *pszFilename, int fFlags);
+/** Pointer to dlmopen. */
+typedef FNDLMOPEN *PFNDLMOPEN;
+#endif
+
+#endif /* SUP_HARDENED_WITHOUT_DLOPEN_PATCHING */
+
+
+/*********************************************************************************************************************************
+* Internal Functions *
+*********************************************************************************************************************************/
+#ifndef SUP_HARDENED_WITHOUT_DLOPEN_PATCHING
+static FNSUPHARDENEDSYMRESOLVE supR3HardenedPosixMonitorDlopenResolve;
+#ifdef SUP_HARDENED_WITH_DLMOPEN
+static FNSUPHARDENEDSYMRESOLVE supR3HardenedPosixMonitorDlmopenResolve;
+#endif
+
+/* SUPR3HardenedMainA-posix.asm: */
+DECLASM(void) supR3HardenedPosixMonitor_Dlopen(const char *pszFilename, int fFlags);
+#ifdef SUP_HARDENED_WITH_DLMOPEN
+DECLASM(void) supR3HardenedPosixMonitor_Dlmopen(Lmid_t idLm, const char *pszFilename, int fFlags);
+#endif
+#endif /* SUP_HARDENED_WITHOUT_DLOPEN_PATCHING */
+
+
+/*********************************************************************************************************************************
+* Global Variables *
+*********************************************************************************************************************************/
+#ifndef SUP_HARDENED_WITHOUT_DLOPEN_PATCHING
+
+RT_C_DECLS_BEGIN
+/** Resume patch for dlopen(), jumped to form assembly stub. */
+DECL_HIDDEN_DATA(PFNDLOPEN) g_pfnDlopenReal = NULL;
+#ifdef SUP_HARDENED_WITH_DLMOPEN
+/** Resume patch for dlmopen(), jumped to form assembly stub. */
+DECL_HIDDEN_DATA(PFNDLMOPEN) g_pfnDlmopenReal = NULL;
+#endif
+RT_C_DECLS_END
+
+/** Memory allocated for the patches. */
+static uint8_t *g_pbExecMemory = NULL;
+/** Offset into the patch memory which is not used. */
+static uint32_t g_offExecMemory = 0;
+
+/**
+ * Array of hooks to install.
+ */
+static SUPHARDENEDPOSIXHOOK const g_aHooks[] =
+{
+ /* pszSymbol, pfnHook, ppfnRealResume, pfnResolve */
+ { "dlopen", (PFNRT)supR3HardenedPosixMonitor_Dlopen, (uintptr_t *)&g_pfnDlopenReal, supR3HardenedPosixMonitorDlopenResolve },
+#ifdef SUP_HARDENED_WITH_DLMOPEN
+ { "dlmopen", (PFNRT)supR3HardenedPosixMonitor_Dlmopen, (uintptr_t *)&g_pfnDlmopenReal, supR3HardenedPosixMonitorDlmopenResolve }
+#endif
+};
+
+
+
+/**
+ * Verifies the given library for proper access rights for further loading
+ * into the process.
+ *
+ * @returns Flag whether the access rights of the library look sane and loading
+ * it is not considered a security risk. Returns true if the library
+ * looks sane, false otherwise.
+ * @param pszFilename The library to load, this can be an absolute or relative path
+ * or just the filename of the library when the default paths should
+ * be searched. NULL is allowed too to indicate opening the main
+ * binary.
+ */
+DECLASM(bool) supR3HardenedPosixMonitor_VerifyLibrary(const char *pszFilename)
+{
+ /*
+ * Giving NULL as the filename indicates opening the main program which is fine
+ * We are already loaded and executing after all.
+ *
+ * Filenames without any path component (whether absolute or relative) are allowed
+ * unconditionally too as the loader will only search the default paths configured by root.
+ */
+ bool fAllow = true;
+
+ if ( pszFilename
+ && strchr(pszFilename, '/') != NULL)
+ {
+#if defined(RT_OS_LINUX)
+ int rc = supR3HardenedVerifyFileFollowSymlinks(pszFilename, RTHCUINTPTR_MAX, true /* fMaybe3rdParty */,
+ NULL /* pErrInfo */);
+#else
+ int rc = supR3HardenedVerifyFile(pszFilename, RTHCUINTPTR_MAX, true /* fMaybe3rdParty */,
+ NULL /* pErrInfo */);
+#endif
+
+ if (RT_FAILURE(rc))
+ fAllow = false;
+ }
+
+ return fAllow;
+}
+
+
+/**
+ * Returns the start address of the given symbol if found or NULL otherwise.
+ *
+ * @returns Start address of the symbol or NULL if not found.
+ * @param pszSymbol The symbol name.
+ * @param pfnResolve The resolver to call before trying to query the start address.
+ */
+static void *supR3HardenedMainPosixGetStartBySymbol(const char *pszSymbol, PFNSUPHARDENEDSYMRESOLVE pfnResolve)
+{
+#ifndef RT_OS_SOLARIS
+ RT_NOREF(pfnResolve);
+ return dlsym(RTLD_DEFAULT, pszSymbol);
+
+#else /* RT_OS_SOLARIS */
+ /*
+ * Solaris is tricky as dlsym doesn't return the actual start address of
+ * the symbol but the start of the trampoline in the PLT of the caller.
+ *
+ * Disassemble the first jmp instruction to get at the entry in the global
+ * offset table where the actual address is stored.
+ *
+ * To counter lazy symbol resolving, we first have to call the API before
+ * trying to resolve and disassemble it.
+ */
+ pfnResolve();
+
+ uint8_t *pbSym = (uint8_t *)dlsym(RTLD_DEFAULT, pszSymbol);
+
+# ifdef RT_ARCH_AMD64
+ DISSTATE Dis;
+ uint32_t cbInstr = 1;
+ int rc = DISInstr(pbSym, DISCPUMODE_64BIT, &Dis, &cbInstr);
+ if ( RT_FAILURE(rc)
+ || Dis.pCurInstr->uOpcode != OP_JMP
+ || !(Dis.ModRM.Bits.Mod == 0 && Dis.ModRM.Bits.Rm == 5 /* wrt RIP */))
+ return NULL;
+
+ /* Extract start address. */
+ pbSym = (pbSym + cbInstr + Dis.Param1.uDisp.i32);
+ pbSym = (uint8_t *)*((uintptr_t *)pbSym);
+# else
+# error "Unsupported architecture"
+# endif
+
+ return pbSym;
+#endif /* RT_OS_SOLARIS */
+}
+
+
+/**
+ * Allocates executable patch memory with the given constraints.
+ *
+ * @returns VBox status code.
+ * @param cb Size of the patch memory in bytes.
+ * @param pvHint Where to try allocating nearby.
+ * @param fRipRelAddr Flag whether the executable memory must be within
+ * 2GB before or after the hint as it will contain
+ * instructions using RIP relative addressing
+ */
+static uint8_t *supR3HardenedMainPosixExecMemAlloc(size_t cb, void *pvHint, bool fRipRelAddr)
+{
+ AssertReturn(cb < _1K, NULL);
+
+ /* Lazy allocation of exectuable memory. */
+ if (!g_pbExecMemory)
+ {
+ g_pbExecMemory = (uint8_t *)mmap(pvHint, DLOPEN_PATCH_MEMORY_SIZE, PROT_READ | PROT_WRITE | PROT_EXEC,
+ MAP_SHARED | MAP_ANONYMOUS, -1, 0);
+ g_offExecMemory = 0;
+ if (g_pbExecMemory == MAP_FAILED)
+ return NULL;
+
+ memset(g_pbExecMemory, 0xcc, DLOPEN_PATCH_MEMORY_SIZE);
+ }
+
+ if (g_offExecMemory + cb >= DLOPEN_PATCH_MEMORY_SIZE)
+ return NULL;
+
+ uint8_t *pb = &g_pbExecMemory[g_offExecMemory];
+
+ if (fRipRelAddr)
+ {
+ /* Check that we allocated within 2GB of the hint. */
+ uintptr_t uPtrHint = (uintptr_t)pvHint;
+ uintptr_t uPtrPatchMem = (uintptr_t)pb;
+ uintptr_t cbDistance = uPtrHint < uPtrPatchMem
+ ? uPtrPatchMem - uPtrHint
+ : uPtrHint - uPtrPatchMem;
+
+ if (cbDistance >= _2G - _4K)
+ return NULL;
+ }
+
+ g_offExecMemory = RT_ALIGN_32(g_offExecMemory + cb, 16);
+ return pb;
+}
+
+
+/**
+ * Hooks the given method to execute the given one first.
+ *
+ * @returns VBox status code.
+ * @param pszSymbol The symbol to hook.
+ * @param pfnHook The hook to install.
+ * @param ppfnReal Where to store the pointer to entry point of the real method
+ * (somewhere in patch memory).
+ * @param pfnResolve The resolver to call before trying to query the start address.
+ */
+static int supR3HardenedMainPosixHookOne(const char *pszSymbol, PFNRT pfnHook, uintptr_t /*PFNRT*/ *ppfnReal,
+ PFNSUPHARDENEDSYMRESOLVE pfnResolve)
+{
+ void *pfnTarget = supR3HardenedMainPosixGetStartBySymbol(pszSymbol, pfnResolve);
+ if (!pfnTarget)
+ return VERR_NOT_FOUND;
+
+ /*
+ * Make the target memory writeable to be able to insert the patch.
+ * Unprotect two pages in case the code crosses a page boundary.
+ */
+ void *pvTargetBase = (void *)(((uintptr_t)pfnTarget) & ~(uintptr_t)(_4K - 1));
+ int rcPsx = mprotect(pvTargetBase, 2 * _4K, PROT_WRITE | PROT_READ | PROT_EXEC);
+ if (rcPsx == -1)
+ return VERR_SUPLIB_TEXT_NOT_WRITEABLE;
+
+ uint8_t * const pbTarget = (uint8_t *)(uintptr_t)pfnTarget;
+
+ DISSTATE Dis;
+ uint32_t cbInstr;
+ uint32_t offJmpBack = 0;
+ uint32_t cbPatchMem = 0;
+
+#ifdef RT_ARCH_AMD64
+ /*
+ * Patch 64-bit hosts.
+ */
+ uint32_t cRipRelMovs = 0;
+ uint32_t cRelCalls = 0;
+
+ /* Just use the disassembler to skip 12 bytes or more, we might need to
+ rewrite mov instructions using RIP relative addressing. */
+ while (offJmpBack < 12)
+ {
+ cbInstr = 1;
+ int rc = DISInstr(pbTarget + offJmpBack, DISCPUMODE_64BIT, &Dis, &cbInstr);
+ if ( RT_FAILURE(rc)
+ || ( Dis.pCurInstr->fOpType & DISOPTYPE_CONTROLFLOW
+ && Dis.pCurInstr->uOpcode != OP_CALL)
+ || ( Dis.ModRM.Bits.Mod == 0
+ && Dis.ModRM.Bits.Rm == 5 /* wrt RIP */
+ && Dis.pCurInstr->uOpcode != OP_MOV))
+ return VERR_SUPLIB_UNEXPECTED_INSTRUCTION;
+
+ if (Dis.ModRM.Bits.Mod == 0 && Dis.ModRM.Bits.Rm == 5 /* wrt RIP */)
+ cRipRelMovs++;
+ if ( Dis.pCurInstr->uOpcode == OP_CALL
+ && (Dis.pCurInstr->fOpType & DISOPTYPE_RELATIVE_CONTROLFLOW))
+ cRelCalls++;
+
+ offJmpBack += cbInstr;
+ cbPatchMem += cbInstr;
+ }
+
+ /*
+ * Each relative call requires extra bytes as it is converted to a pushq imm32
+ * + mov [RSP+4], imm32 + a jmp qword [$+8 wrt RIP] to avoid clobbering registers.
+ */
+ cbPatchMem += cRelCalls * RT_ALIGN_32(13 + 6 + 8, 8);
+ cbPatchMem += 14; /* jmp qword [$+8 wrt RIP] + 8 byte address to jump to. */
+ cbPatchMem = RT_ALIGN_32(cbPatchMem, 8);
+
+ /* Allocate suitable executable memory available. */
+ bool fConvRipRelMovs = false;
+ uint8_t *pbPatchMem = supR3HardenedMainPosixExecMemAlloc(cbPatchMem, pbTarget, cRipRelMovs > 0);
+ if (!pbPatchMem)
+ {
+ /*
+ * Try to allocate memory again without the RIP relative mov addressing constraint
+ * Makes it a bit more difficult for us later on but there is no way around it.
+ * We need to increase the patch memory because we create two instructions for one
+ * (7 bytes for the RIP relative mov vs. 13 bytes for the two instructions replacing it ->
+ * need to allocate 6 bytes more per RIP relative mov).
+ */
+ fConvRipRelMovs = true;
+ if (cRipRelMovs > 0)
+ pbPatchMem = supR3HardenedMainPosixExecMemAlloc(cbPatchMem + cRipRelMovs * 6,
+ pbTarget, false /*fRipRelAddr*/);
+
+ if (!pbPatchMem)
+ return VERR_NO_MEMORY;
+ }
+
+ /* Assemble the code for resuming the call.*/
+ *ppfnReal = (uintptr_t)pbPatchMem;
+
+ /* Go through the instructions to patch and fixup any rip relative mov instructions. */
+ uint32_t offInsn = 0;
+ while (offInsn < offJmpBack)
+ {
+ cbInstr = 1;
+ int rc = DISInstr(pbTarget + offInsn, DISCPUMODE_64BIT, &Dis, &cbInstr);
+ if ( RT_FAILURE(rc)
+ || ( Dis.pCurInstr->fOpType & DISOPTYPE_CONTROLFLOW
+ && Dis.pCurInstr->uOpcode != OP_CALL))
+ return VERR_SUPLIB_UNEXPECTED_INSTRUCTION;
+
+ if ( Dis.ModRM.Bits.Mod == 0
+ && Dis.ModRM.Bits.Rm == 5 /* wrt RIP */
+ && Dis.pCurInstr->uOpcode == OP_MOV)
+ {
+ /* Deduce destination register and write out new instruction. */
+ if (RT_UNLIKELY(!( (Dis.Param1.fUse & (DISUSE_BASE | DISUSE_REG_GEN64))
+ && (Dis.Param2.fUse & DISUSE_RIPDISPLACEMENT32))))
+ return VERR_SUPLIB_UNEXPECTED_INSTRUCTION;
+
+ uintptr_t uAddr = (uintptr_t)&pbTarget[offInsn + cbInstr] + (intptr_t)Dis.Param2.uDisp.i32;
+
+ if (fConvRipRelMovs)
+ {
+ /*
+ * Create two instructions, first one moves the address as a constant to the destination register
+ * and the second one loads the data from the memory into the destination register.
+ */
+
+ *pbPatchMem++ = 0x48;
+ *pbPatchMem++ = 0xb8 + Dis.Param1.Base.idxGenReg;
+ *(uintptr_t *)pbPatchMem = uAddr;
+ pbPatchMem += sizeof(uintptr_t);
+
+ *pbPatchMem++ = 0x48;
+ *pbPatchMem++ = 0x8b;
+ *pbPatchMem++ = (Dis.Param1.Base.idxGenReg << X86_MODRM_REG_SHIFT) | Dis.Param1.Base.idxGenReg;
+ }
+ else
+ {
+ intptr_t iDispNew = uAddr - (uintptr_t)&pbPatchMem[3 + sizeof(int32_t)];
+ Assert(iDispNew == (int32_t)iDispNew);
+
+ /* Assemble the mov to register instruction with the updated rip relative displacement. */
+ *pbPatchMem++ = 0x48;
+ *pbPatchMem++ = 0x8b;
+ *pbPatchMem++ = (Dis.Param1.Base.idxGenReg << X86_MODRM_REG_SHIFT) | 5;
+ *(int32_t *)pbPatchMem = (int32_t)iDispNew;
+ pbPatchMem += sizeof(int32_t);
+ }
+ }
+ else if ( Dis.pCurInstr->uOpcode == OP_CALL
+ && (Dis.pCurInstr->fOpType & DISOPTYPE_RELATIVE_CONTROLFLOW))
+ {
+ /* Convert to absolute jump. */
+ uintptr_t uAddr = (uintptr_t)&pbTarget[offInsn + cbInstr] + (intptr_t)Dis.Param1.uValue;
+
+ /* Skip the push instructions till the return address is known. */
+ uint8_t *pbPatchMemPush = pbPatchMem;
+ pbPatchMem += 13;
+
+ *pbPatchMem++ = 0xff; /* jmp qword [$+8 wrt RIP] */
+ *pbPatchMem++ = 0x25;
+ *(uint32_t *)pbPatchMem = (uint32_t)(RT_ALIGN_PT(pbPatchMem + 4, 8, uint8_t *) - (pbPatchMem + 4));
+ pbPatchMem = RT_ALIGN_PT(pbPatchMem + 4, 8, uint8_t *);
+ *(uint64_t *)pbPatchMem = uAddr;
+ pbPatchMem += sizeof(uint64_t);
+
+ /* Push the return address onto stack. Difficult on amd64 without clobbering registers... */
+ uintptr_t uAddrReturn = (uintptr_t)pbPatchMem;
+ *pbPatchMemPush++ = 0x68; /* push imm32 sign-extended as 64-bit*/
+ *(uint32_t *)pbPatchMemPush = RT_LO_U32(uAddrReturn);
+ pbPatchMemPush += sizeof(uint32_t);
+ *pbPatchMemPush++ = 0xc7;
+ *pbPatchMemPush++ = 0x44;
+ *pbPatchMemPush++ = 0x24;
+ *pbPatchMemPush++ = 0x04; /* movl [RSP+4], imm32 */
+ *(uint32_t *)pbPatchMemPush = RT_HI_U32(uAddrReturn);
+ }
+ else
+ {
+ memcpy(pbPatchMem, pbTarget + offInsn, cbInstr);
+ pbPatchMem += cbInstr;
+ }
+
+ offInsn += cbInstr;
+ }
+
+ *pbPatchMem++ = 0xff; /* jmp qword [$+8 wrt RIP] */
+ *pbPatchMem++ = 0x25;
+ *(uint32_t *)pbPatchMem = (uint32_t)(RT_ALIGN_PT(pbPatchMem + 4, 8, uint8_t *) - (pbPatchMem + 4));
+ pbPatchMem = RT_ALIGN_PT(pbPatchMem + 4, 8, uint8_t *);
+ *(uint64_t *)pbPatchMem = (uintptr_t)&pbTarget[offJmpBack];
+
+ /* Assemble the patch. */
+ Assert(offJmpBack >= 12);
+ pbTarget[0] = 0x48; /* mov rax, qword */
+ pbTarget[1] = 0xb8;
+ *(uintptr_t *)&pbTarget[2] = (uintptr_t)pfnHook;
+ pbTarget[10] = 0xff; /* jmp rax */
+ pbTarget[11] = 0xe0;
+
+#else /* !RT_ARCH_AMD64 */
+ /*
+ * Patch 32-bit hosts.
+ */
+ /* Just use the disassembler to skip 5 bytes or more. */
+ while (offJmpBack < 5)
+ {
+ cbInstr = 1;
+ int rc = DISInstr(pbTarget + offJmpBack, DISCPUMODE_32BIT, &Dis, &cbInstr);
+ if ( RT_FAILURE(rc)
+ || ( (Dis.pCurInstr->fOpType & DISOPTYPE_CONTROLFLOW)
+ && Dis.pCurInstr->uOpcode != OP_CALL))
+ return VERR_SUPLIB_UNEXPECTED_INSTRUCTION;
+
+ if ( Dis.pCurInstr->uOpcode == OP_CALL
+ && (Dis.pCurInstr->fOpType & DISOPTYPE_RELATIVE_CONTROLFLOW))
+ cbPatchMem += 10; /* push imm32 + jmp rel32 */
+ else
+ cbPatchMem += cbInstr;
+
+ offJmpBack += cbInstr;
+ }
+
+ /* Allocate suitable exectuable memory available. */
+ uint8_t *pbPatchMem = supR3HardenedMainPosixExecMemAlloc(cbPatchMem, pbTarget, false /* fRipRelAddr */);
+ if (!pbPatchMem)
+ return VERR_NO_MEMORY;
+
+ /* Assemble the code for resuming the call.*/
+ *ppfnReal = (uintptr_t)pbPatchMem;
+
+ /* Go through the instructions to patch and fixup any relative call instructions. */
+ uint32_t offInsn = 0;
+ while (offInsn < offJmpBack)
+ {
+ cbInstr = 1;
+ int rc = DISInstr(pbTarget + offInsn, DISCPUMODE_32BIT, &Dis, &cbInstr);
+ if ( RT_FAILURE(rc)
+ || ( (Dis.pCurInstr->fOpType & DISOPTYPE_CONTROLFLOW)
+ && Dis.pCurInstr->uOpcode != OP_CALL))
+ return VERR_SUPLIB_UNEXPECTED_INSTRUCTION;
+
+ if ( Dis.pCurInstr->uOpcode == OP_CALL
+ && (Dis.pCurInstr->fOpType & DISOPTYPE_RELATIVE_CONTROLFLOW))
+ {
+ /*
+ * Don't use a call instruction directly but push the original return address
+ * onto the stack and use a relative jump to the call target.
+ * The reason here is that on Linux the called method saves the return
+ * address from the stack which will be different from the original because
+ * the code is executed from our patch memory.
+ *
+ * Luckily the call instruction is 5 bytes long which means it is always the
+ * last instruction to patch and we don't need to return from the call
+ * to patch memory anyway but can use this method to resume the original call.
+ */
+ AssertReturn(offInsn + cbInstr >= offJmpBack, VERR_SUPLIB_UNEXPECTED_INSTRUCTION); /* Must be last instruction! */
+
+ /* push return address */
+ uint32_t const uAddrReturn = (uintptr_t)&pbTarget[offInsn + cbInstr]; /* The return address to push to the stack. */
+
+ *pbPatchMem++ = 0x68; /* push dword */
+ *(uint32_t *)pbPatchMem = uAddrReturn;
+ pbPatchMem += sizeof(uint32_t);
+
+ /* jmp rel32 to the call target */
+ uintptr_t const uAddr = uAddrReturn + (int32_t)Dis.Param1.uValue;
+ int32_t const i32DispNew = uAddr - (uintptr_t)&pbPatchMem[5];
+
+ *pbPatchMem++ = 0xe9; /* jmp rel32 */
+ *(int32_t *)pbPatchMem = i32DispNew;
+ pbPatchMem += sizeof(int32_t);
+ }
+ else
+ {
+ memcpy(pbPatchMem, pbTarget + offInsn, cbInstr);
+ pbPatchMem += cbInstr;
+ }
+
+ offInsn += cbInstr;
+ }
+
+ *pbPatchMem++ = 0xe9; /* jmp rel32 */
+ *(uint32_t *)pbPatchMem = (uintptr_t)&pbTarget[offJmpBack] - ((uintptr_t)pbPatchMem + 4);
+
+ /* Assemble the patch. */
+ Assert(offJmpBack >= 5);
+ pbTarget[0] = 0xe9;
+ *(uint32_t *)&pbTarget[1] = (uintptr_t)pfnHook - (uintptr_t)&pbTarget[1+4];
+#endif /* !RT_ARCH_AMD64 */
+
+ /*
+ * Re-seal target (ASSUMING that the shared object either has page aligned
+ * section or that the patch target is far enough from the writable parts).
+ */
+ rcPsx = mprotect(pvTargetBase, 2 * _4K, PROT_READ | PROT_EXEC);
+ if (rcPsx == -1)
+ return VERR_SUPLIB_TEXT_NOT_SEALED;
+
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * @callback_method_impl{FNSUPHARDENEDSYMRESOLVE, dlopen}
+ */
+static DECLCALLBACK(void) supR3HardenedPosixMonitorDlopenResolve(void)
+{
+ /* Make harmless dlopen call. */
+ void *pv = dlopen(NULL, RTLD_LAZY);
+ if (pv)
+ dlclose(pv);
+}
+
+
+#ifdef SUP_HARDENED_WITH_DLMOPEN
+/**
+ * @callback_method_impl{FNSUPHARDENEDSYMRESOLVE, dlmopen}
+ */
+static DECLCALLBACK(void) supR3HardenedPosixMonitorDlmopenResolve(void)
+{
+ /* Make harmless dlmopen call. */
+ void *pv = dlmopen(LM_ID_BASE, NULL, RTLD_LAZY);
+ if (pv)
+ dlclose(pv);
+}
+#endif
+
+#endif /* SUP_HARDENED_WITHOUT_DLOPEN_PATCHING */
+
+
+/**
+ * Hardening initialization for POSIX compatible hosts.
+ *
+ * @note Doesn't return on error.
+ */
+DECLHIDDEN(void) supR3HardenedPosixInit(void)
+{
+#ifndef SUP_HARDENED_WITHOUT_DLOPEN_PATCHING
+ for (unsigned i = 0; i < RT_ELEMENTS(g_aHooks); i++)
+ {
+ PCSUPHARDENEDPOSIXHOOK pHook = &g_aHooks[i];
+ int rc = supR3HardenedMainPosixHookOne(pHook->pszSymbol, pHook->pfnHook, pHook->ppfnRealResume, pHook->pfnResolve);
+ if (RT_FAILURE(rc))
+ supR3HardenedFatalMsg("supR3HardenedPosixInit", kSupInitOp_Integrity, rc,
+ "Failed to hook the %s interface", pHook->pszSymbol);
+ }
+#endif
+}
+
+
+
+/*
+ * assert.cpp
+ *
+ * ASSUMES working DECLHIDDEN or there will be symbol confusion!
+ */
+
+RTDATADECL(char) g_szRTAssertMsg1[1024];
+RTDATADECL(char) g_szRTAssertMsg2[4096];
+RTDATADECL(const char * volatile) g_pszRTAssertExpr;
+RTDATADECL(const char * volatile) g_pszRTAssertFile;
+RTDATADECL(uint32_t volatile) g_u32RTAssertLine;
+RTDATADECL(const char * volatile) g_pszRTAssertFunction;
+
+RTDECL(bool) RTAssertMayPanic(void)
+{
+ return true;
+}
+
+
+RTDECL(void) RTAssertMsg1(const char *pszExpr, unsigned uLine, const char *pszFile, const char *pszFunction)
+{
+ /*
+ * Fill in the globals.
+ */
+ g_pszRTAssertExpr = pszExpr;
+ g_pszRTAssertFile = pszFile;
+ g_pszRTAssertFunction = pszFunction;
+ g_u32RTAssertLine = uLine;
+ snprintf(g_szRTAssertMsg1, sizeof(g_szRTAssertMsg1),
+ "\n!!Assertion Failed!!\n"
+ "Expression: %s\n"
+ "Location : %s(%u) %s\n",
+ pszExpr, pszFile, uLine, pszFunction);
+}
+
+
+RTDECL(void) RTAssertMsg2V(const char *pszFormat, va_list va)
+{
+ vsnprintf(g_szRTAssertMsg2, sizeof(g_szRTAssertMsg2), pszFormat, va);
+ if (g_enmSupR3HardenedMainState < SUPR3HARDENEDMAINSTATE_CALLED_TRUSTED_MAIN)
+ supR3HardenedFatalMsg(g_pszRTAssertExpr, kSupInitOp_Misc, VERR_INTERNAL_ERROR,
+ "%s%s", g_szRTAssertMsg1, g_szRTAssertMsg2);
+ else
+ supR3HardenedError(VERR_INTERNAL_ERROR, false/*fFatal*/, "%s%s", g_szRTAssertMsg1, g_szRTAssertMsg2);
+}
+
diff --git a/src/VBox/HostDrivers/Support/posix/SUPR3HardenedMainA-posix.asm b/src/VBox/HostDrivers/Support/posix/SUPR3HardenedMainA-posix.asm
new file mode 100644
index 00000000..e64b0efe
--- /dev/null
+++ b/src/VBox/HostDrivers/Support/posix/SUPR3HardenedMainA-posix.asm
@@ -0,0 +1,170 @@
+; $Id: SUPR3HardenedMainA-posix.asm $
+;; @file
+; VirtualBox Support Library - Hardened main(), Posix assembly bits.
+;
+
+;
+; Copyright (C) 2017-2023 Oracle and/or its affiliates.
+;
+; This file is part of VirtualBox base platform packages, as
+; available from https://www.virtualbox.org.
+;
+; This program is free software; you can redistribute it and/or
+; modify it under the terms of the GNU General Public License
+; as published by the Free Software Foundation, in version 3 of the
+; License.
+;
+; This program is distributed in the hope that it will be useful, but
+; WITHOUT ANY WARRANTY; without even the implied warranty of
+; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+; General Public License for more details.
+;
+; You should have received a copy of the GNU General Public License
+; along with this program; if not, see <https://www.gnu.org/licenses>.
+;
+; The contents of this file may alternatively be used under the terms
+; of the Common Development and Distribution License Version 1.0
+; (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+; in the VirtualBox distribution, in which case the provisions of the
+; CDDL are applicable instead of those of the GPL.
+;
+; You may elect to license modified versions of this file under the
+; terms and conditions of either the GPL or the CDDL or both.
+;
+; SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+;
+
+
+;*******************************************************************************
+;* Header Files *
+;*******************************************************************************
+%include "iprt/asmdefs.mac"
+
+
+;*********************************************************************************************************************************
+;* External Symbols *
+;*********************************************************************************************************************************
+; External code.
+BEGINCODE
+extern NAME(supR3HardenedPosixMonitor_VerifyLibrary)
+
+; External data
+BEGINDATA
+extern NAME(g_pfnDlopenReal)
+%ifdef SUP_HARDENED_WITH_DLMOPEN
+extern NAME(g_pfnDlmopenReal)
+%endif
+
+
+
+BEGINCODE
+
+;;
+; Wrapper for dlopen() handing the call over to the file verification code
+; and resuming the call if we get a green light to load the library.
+;
+align 16
+BEGINPROC supR3HardenedPosixMonitor_Dlopen
+ push xBP
+ mov xBP, xSP
+
+%ifdef RT_ARCH_AMD64
+ ; Save parameters on the stack
+ push rdi
+ push rsi
+%else
+ sub esp, 4 ; 16-byte stack alignment before call.
+ push dword [xBP + 08h] ; first parameter.
+%endif
+
+ ;
+ ; Call the verification method.
+ ;
+ call NAME(supR3HardenedPosixMonitor_VerifyLibrary)
+
+ ;
+ ; Restore parameters for the next call and get the stack back to the
+ ; original state.
+ ;
+%ifdef RT_ARCH_AMD64
+ pop rsi
+ pop rdi
+%endif
+ leave
+
+ ; Check the result and resume the call if the result is positive,
+ ; otherwise clean up and return NULL
+ test al, al
+ je short .failed
+
+ ; Resume the original dlopen call by jumping into the saved code.
+ jmp [NAME(g_pfnDlopenReal) xWrtRIP]
+
+.failed:
+ ;
+ ; Don't use leave here as we didn't use the enter instruction. Just clear
+ ; xAX and return
+ ;
+ xor xAX, xAX
+ ret
+ENDPROC supR3HardenedPosixMonitor_Dlopen
+
+
+%ifdef SUP_HARDENED_WITH_DLMOPEN
+;;
+; Wrapper for dlmopen() handing the call over to the file verification code
+; and resuming the call if we get a green light to load the library.
+;
+align 16
+BEGINPROC supR3HardenedPosixMonitor_Dlmopen
+ push xBP
+ mov xBP, xSP
+
+%ifdef RT_ARCH_AMD64
+ sub rsp, 8 ; 16-byte stack alignment before call.
+
+ ; Save parameters on the stack
+ push rdi
+ push rsi
+ push rdx
+
+ mov rdi, rsi ; Move the second parameter to the front
+%else
+ sub esp, 4 ; 16-byte stack alignment before call.
+ push dword [xBP + 0ch] ; Move the second parameter to the front
+%endif
+
+ ;
+ ; Call the verification method.
+ ;
+ call NAME(supR3HardenedPosixMonitor_VerifyLibrary)
+
+ ;
+ ; Restore parameters for the next call and get the stack back to the
+ ; original state.
+ ;
+%ifdef RT_ARCH_AMD64
+ pop rdx
+ pop rsi
+ pop rdi
+%endif
+ leave
+
+ ; Check the result and resume the call if the result is positive,
+ ; otherwise clean up and return NULL
+ test al, al
+ je short .failed
+
+ ; Resume the original dlopen call by jumping into the saved code.
+ jmp [NAME(g_pfnDlmopenReal) xWrtRIP]
+
+.failed:
+ ;
+ ; Don't use leave here as we didn't use the enter instruction. Just clear
+ ; xAX and return
+ ;
+ xor xAX, xAX
+ ret
+ENDPROC supR3HardenedPosixMonitor_Dlmopen
+%endif
+
diff --git a/src/VBox/HostDrivers/Support/solaris/Makefile.kup b/src/VBox/HostDrivers/Support/solaris/Makefile.kup
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/src/VBox/HostDrivers/Support/solaris/Makefile.kup
diff --git a/src/VBox/HostDrivers/Support/solaris/SUPDrv-solaris.c b/src/VBox/HostDrivers/Support/solaris/SUPDrv-solaris.c
new file mode 100644
index 00000000..5e29b0a8
--- /dev/null
+++ b/src/VBox/HostDrivers/Support/solaris/SUPDrv-solaris.c
@@ -0,0 +1,1347 @@
+/* $Id: SUPDrv-solaris.c $ */
+/** @file
+ * VBoxDrv - The VirtualBox Support Driver - Solaris specifics.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#define LOG_GROUP LOG_GROUP_SUP_DRV
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/errno.h>
+#include <sys/uio.h>
+#include <sys/buf.h>
+#include <sys/modctl.h>
+#include <sys/kobj.h>
+#include <sys/kobj_impl.h>
+#include <sys/open.h>
+#include <sys/conf.h>
+#include <sys/cmn_err.h>
+#include <sys/stat.h>
+#include <sys/ddi.h>
+#include <sys/sunddi.h>
+#include <sys/file.h>
+#include <sys/priv_names.h>
+#include <vm/hat.h>
+#undef u /* /usr/include/sys/user.h:249:1 is where this is defined to (curproc->p_user). very cool. */
+
+#include "../SUPDrvInternal.h"
+#include <VBox/log.h>
+#include <VBox/param.h>
+#include <VBox/version.h>
+#include <iprt/semaphore.h>
+#include <iprt/spinlock.h>
+#include <iprt/mp.h>
+#include <iprt/path.h>
+#include <iprt/power.h>
+#include <iprt/process.h>
+#include <iprt/thread.h>
+#include <iprt/initterm.h>
+#include <iprt/alloc.h>
+#include <iprt/string.h>
+#include <iprt/err.h>
+
+#include "dtrace/SUPDrv.h"
+
+extern caddr_t hat_kpm_pfn2va(pfn_t); /* Found in vm/hat.h on solaris 11.3, but not on older like 10u7. */
+
+
+/*********************************************************************************************************************************
+* Defined Constants And Macros *
+*********************************************************************************************************************************/
+/** The system device name. */
+#define DEVICE_NAME_SYS "vboxdrv"
+/** The user device name. */
+#define DEVICE_NAME_USR "vboxdrvu"
+/** The module description as seen in 'modinfo'. */
+#define DEVICE_DESC "VirtualBox HostDrv"
+/** Maximum number of driver instances. */
+#define DEVICE_MAXINSTANCES 16
+
+
+/*********************************************************************************************************************************
+* Internal Functions *
+*********************************************************************************************************************************/
+static int VBoxDrvSolarisOpen(dev_t *pDev, int fFlag, int fType, cred_t *pCred);
+static int VBoxDrvSolarisClose(dev_t Dev, int fFlag, int fType, cred_t *pCred);
+static int VBoxDrvSolarisRead(dev_t Dev, struct uio *pUio, cred_t *pCred);
+static int VBoxDrvSolarisWrite(dev_t Dev, struct uio *pUio, cred_t *pCred);
+static int VBoxDrvSolarisIOCtl(dev_t Dev, int Cmd, intptr_t pArgs, int mode, cred_t *pCred, int *pVal);
+
+static int VBoxDrvSolarisAttach(dev_info_t *pDip, ddi_attach_cmd_t Cmd);
+static int VBoxDrvSolarisDetach(dev_info_t *pDip, ddi_detach_cmd_t Cmd);
+static int VBoxDrvSolarisQuiesceNotNeeded(dev_info_t *pDip);
+
+static int VBoxSupDrvErr2SolarisErr(int rc);
+static int VBoxDrvSolarisIOCtlSlow(PSUPDRVSESSION pSession, int Cmd, int Mode, intptr_t pArgs);
+
+
+/*********************************************************************************************************************************
+* Global Variables *
+*********************************************************************************************************************************/
+/**
+ * cb_ops: for drivers that support char/block entry points
+ */
+static struct cb_ops g_VBoxDrvSolarisCbOps =
+{
+ VBoxDrvSolarisOpen,
+ VBoxDrvSolarisClose,
+ nodev, /* b strategy */
+ nodev, /* b dump */
+ nodev, /* b print */
+ VBoxDrvSolarisRead,
+ VBoxDrvSolarisWrite,
+ VBoxDrvSolarisIOCtl,
+ nodev, /* c devmap */
+ nodev, /* c mmap */
+ nodev, /* c segmap */
+ nochpoll, /* c poll */
+ ddi_prop_op, /* property ops */
+ NULL, /* streamtab */
+ D_NEW | D_MP, /* compat. flag */
+ CB_REV /* revision */
+};
+
+/**
+ * dev_ops: for driver device operations
+ */
+static struct dev_ops g_VBoxDrvSolarisDevOps =
+{
+ DEVO_REV, /* driver build revision */
+ 0, /* ref count */
+ nulldev, /* get info */
+ nulldev, /* identify */
+ nulldev, /* probe */
+ VBoxDrvSolarisAttach,
+ VBoxDrvSolarisDetach,
+ nodev, /* reset */
+ &g_VBoxDrvSolarisCbOps,
+ (struct bus_ops *)0,
+ nodev, /* power */
+ VBoxDrvSolarisQuiesceNotNeeded
+};
+
+/**
+ * modldrv: export driver specifics to the kernel
+ */
+static struct modldrv g_VBoxDrvSolarisModule =
+{
+ &mod_driverops, /* extern from kernel */
+ DEVICE_DESC " " VBOX_VERSION_STRING "r" RT_XSTR(VBOX_SVN_REV),
+ &g_VBoxDrvSolarisDevOps
+};
+
+/**
+ * modlinkage: export install/remove/info to the kernel
+ */
+static struct modlinkage g_VBoxDrvSolarisModLinkage =
+{
+ MODREV_1, /* loadable module system revision */
+ {
+ &g_VBoxDrvSolarisModule,
+ NULL /* terminate array of linkage structures */
+ }
+};
+
+#ifndef USE_SESSION_HASH
+/**
+ * State info for each open file handle.
+ */
+typedef struct
+{
+ /**< Pointer to the session data. */
+ PSUPDRVSESSION pSession;
+} vbox_devstate_t;
+#else
+/** State info. for each driver instance. */
+typedef struct
+{
+ dev_info_t *pDip; /* Device handle */
+} vbox_devstate_t;
+#endif
+
+/** Opaque pointer to list of state */
+static void *g_pVBoxDrvSolarisState;
+
+/** Device extention & session data association structure */
+static SUPDRVDEVEXT g_DevExt;
+
+/** Hash table */
+static PSUPDRVSESSION g_apSessionHashTab[19];
+/** Spinlock protecting g_apSessionHashTab. */
+static RTSPINLOCK g_Spinlock = NIL_RTSPINLOCK;
+/** Calculates bucket index into g_apSessionHashTab.*/
+#define SESSION_HASH(sfn) ((sfn) % RT_ELEMENTS(g_apSessionHashTab))
+
+/**
+ * Kernel entry points
+ */
+int _init(void)
+{
+#if 0 /* No IPRT logging before RTR0Init() is done! */
+ LogFlowFunc(("vboxdrv:_init\n"));
+#endif
+
+ /*
+ * Prevent module autounloading.
+ */
+ modctl_t *pModCtl = mod_getctl(&g_VBoxDrvSolarisModLinkage);
+ if (pModCtl)
+ pModCtl->mod_loadflags |= MOD_NOAUTOUNLOAD;
+ else
+ cmn_err(CE_NOTE, "vboxdrv: failed to disable autounloading!\n");
+
+ /*
+ * Initialize IPRT R0 driver, which internally calls OS-specific r0 init.
+ */
+ int rc = RTR0Init(0);
+ if (RT_SUCCESS(rc))
+ {
+ /*
+ * Initialize the device extension
+ */
+ rc = supdrvInitDevExt(&g_DevExt, sizeof(SUPDRVSESSION));
+ if (RT_SUCCESS(rc))
+ {
+ cmn_err(CE_CONT, "!tsc::mode %s @ tentative %lu Hz\n", SUPGetGIPModeName(g_DevExt.pGip), g_DevExt.pGip->u64CpuHz);
+
+ /*
+ * Initialize the session hash table.
+ */
+ memset(g_apSessionHashTab, 0, sizeof(g_apSessionHashTab));
+ rc = RTSpinlockCreate(&g_Spinlock, RTSPINLOCK_FLAGS_INTERRUPT_SAFE, "VBoxDrvSol");
+ if (RT_SUCCESS(rc))
+ {
+ rc = ddi_soft_state_init(&g_pVBoxDrvSolarisState, sizeof(vbox_devstate_t), 8);
+ if (!rc)
+ {
+ rc = mod_install(&g_VBoxDrvSolarisModLinkage);
+ if (!rc)
+ return rc; /* success */
+
+ ddi_soft_state_fini(&g_pVBoxDrvSolarisState);
+ LogRel(("vboxdrv: mod_install failed! rc=%d\n", rc));
+ }
+ else
+ LogRel(("vboxdrv: failed to initialize soft state.\n"));
+
+ RTSpinlockDestroy(g_Spinlock);
+ g_Spinlock = NIL_RTSPINLOCK;
+ }
+ else
+ {
+ LogRel(("VBoxDrvSolarisAttach: RTSpinlockCreate failed\n"));
+ rc = RTErrConvertToErrno(rc);
+ }
+ supdrvDeleteDevExt(&g_DevExt);
+ }
+ else
+ {
+ LogRel(("VBoxDrvSolarisAttach: supdrvInitDevExt failed\n"));
+ rc = EINVAL;
+ }
+ RTR0TermForced();
+ }
+ else
+ {
+ LogRel(("VBoxDrvSolarisAttach: failed to init R0Drv\n"));
+ rc = RTErrConvertToErrno(rc);
+ }
+ memset(&g_DevExt, 0, sizeof(g_DevExt));
+
+ return rc;
+}
+
+
+int _fini(void)
+{
+ LogFlowFunc(("vboxdrv:_fini\n"));
+
+ /*
+ * Undo the work we did at start (in the reverse order).
+ */
+ int rc = mod_remove(&g_VBoxDrvSolarisModLinkage);
+ if (rc != 0)
+ return rc;
+
+ supdrvDeleteDevExt(&g_DevExt);
+
+ rc = RTSpinlockDestroy(g_Spinlock);
+ AssertRC(rc);
+ g_Spinlock = NIL_RTSPINLOCK;
+
+ RTR0TermForced();
+
+ memset(&g_DevExt, 0, sizeof(g_DevExt));
+
+ ddi_soft_state_fini(&g_pVBoxDrvSolarisState);
+ return 0;
+}
+
+
+int _info(struct modinfo *pModInfo)
+{
+#if 0 /* No IPRT logging before RTR0Init() is done! And yes this is called before _init()!*/
+ LogFlowFunc(("vboxdrv:_init\n"));
+#endif
+ int e = mod_info(&g_VBoxDrvSolarisModLinkage, pModInfo);
+ return e;
+}
+
+
+/**
+ * Attach entry point, to attach a device to the system or resume it.
+ *
+ * @param pDip The module structure instance.
+ * @param enmCmd Operation type (attach/resume).
+ *
+ * @return corresponding solaris error code.
+ */
+static int VBoxDrvSolarisAttach(dev_info_t *pDip, ddi_attach_cmd_t enmCmd)
+{
+ LogFlowFunc(("VBoxDrvSolarisAttach\n"));
+
+ switch (enmCmd)
+ {
+ case DDI_ATTACH:
+ {
+ int rc;
+#ifdef USE_SESSION_HASH
+ int instance = ddi_get_instance(pDip);
+ vbox_devstate_t *pState;
+
+ if (ddi_soft_state_zalloc(g_pVBoxDrvSolarisState, instance) != DDI_SUCCESS)
+ {
+ LogRel(("VBoxDrvSolarisAttach: state alloc failed\n"));
+ return DDI_FAILURE;
+ }
+
+ pState = ddi_get_soft_state(g_pVBoxDrvSolarisState, instance);
+#endif
+
+ /*
+ * Register for suspend/resume notifications
+ */
+ rc = ddi_prop_create(DDI_DEV_T_NONE, pDip, DDI_PROP_CANSLEEP /* kmem alloc can sleep */,
+ "pm-hardware-state", "needs-suspend-resume", sizeof("needs-suspend-resume"));
+ if (rc != DDI_PROP_SUCCESS)
+ LogRel(("vboxdrv: Suspend/Resume notification registration failed.\n"));
+
+ /*
+ * Register ourselves as a character device, pseudo-driver
+ */
+#ifdef VBOX_WITH_HARDENING
+ rc = ddi_create_priv_minor_node(pDip, DEVICE_NAME_SYS, S_IFCHR, 0 /*minor*/, DDI_PSEUDO,
+ 0, NULL, NULL, 0600);
+#else
+ rc = ddi_create_priv_minor_node(pDip, DEVICE_NAME_SYS, S_IFCHR, 0 /*minor*/, DDI_PSEUDO,
+ 0, "none", "none", 0666);
+#endif
+ if (rc == DDI_SUCCESS)
+ {
+ rc = ddi_create_priv_minor_node(pDip, DEVICE_NAME_USR, S_IFCHR, 1 /*minor*/, DDI_PSEUDO,
+ 0, "none", "none", 0666);
+ if (rc == DDI_SUCCESS)
+ {
+#ifdef USE_SESSION_HASH
+ pState->pDip = pDip;
+#endif
+ ddi_report_dev(pDip);
+ return DDI_SUCCESS;
+ }
+ ddi_remove_minor_node(pDip, NULL);
+ }
+
+ return DDI_FAILURE;
+ }
+
+ case DDI_RESUME:
+ {
+#if 0
+ RTSemFastMutexRequest(g_DevExt.mtxGip);
+ if (g_DevExt.pGipTimer)
+ RTTimerStart(g_DevExt.pGipTimer, 0);
+
+ RTSemFastMutexRelease(g_DevExt.mtxGip);
+#endif
+ RTPowerSignalEvent(RTPOWEREVENT_RESUME);
+ LogFlow(("vboxdrv: Awakened from suspend.\n"));
+ return DDI_SUCCESS;
+ }
+
+ default:
+ return DDI_FAILURE;
+ }
+
+ return DDI_FAILURE;
+}
+
+
+/**
+ * Detach entry point, to detach a device to the system or suspend it.
+ *
+ * @param pDip The module structure instance.
+ * @param enmCmd Operation type (detach/suspend).
+ *
+ * @return corresponding solaris error code.
+ */
+static int VBoxDrvSolarisDetach(dev_info_t *pDip, ddi_detach_cmd_t enmCmd)
+{
+ LogFlowFunc(("VBoxDrvSolarisDetach\n"));
+ switch (enmCmd)
+ {
+ case DDI_DETACH:
+ {
+#ifndef USE_SESSION_HASH
+ ddi_remove_minor_node(pDip, NULL);
+#else
+ int instance = ddi_get_instance(pDip);
+ vbox_devstate_t *pState = ddi_get_soft_state(g_pVBoxDrvSolarisState, instance);
+ ddi_remove_minor_node(pDip, NULL);
+ ddi_soft_state_free(g_pVBoxDrvSolarisState, instance);
+#endif
+ ddi_prop_remove_all(pDip);
+ return DDI_SUCCESS;
+ }
+
+ case DDI_SUSPEND:
+ {
+#if 0
+ RTSemFastMutexRequest(g_DevExt.mtxGip);
+ if (g_DevExt.pGipTimer && g_DevExt.cGipUsers > 0)
+ RTTimerStop(g_DevExt.pGipTimer);
+
+ RTSemFastMutexRelease(g_DevExt.mtxGip);
+#endif
+ RTPowerSignalEvent(RTPOWEREVENT_SUSPEND);
+ LogFlow(("vboxdrv: Falling to suspend mode.\n"));
+ return DDI_SUCCESS;
+
+ }
+
+ default:
+ return DDI_FAILURE;
+ }
+}
+
+
+/**
+ * Quiesce not-needed entry point, as Solaris 10 doesn't have any
+ * ddi_quiesce_not_needed() function.
+ *
+ * @param pDip The module structure instance.
+ *
+ * @return corresponding solaris error code.
+ */
+static int VBoxDrvSolarisQuiesceNotNeeded(dev_info_t *pDip)
+{
+ return DDI_SUCCESS;
+}
+
+
+/**
+ * open() worker.
+ */
+static int VBoxDrvSolarisOpen(dev_t *pDev, int fFlag, int fType, cred_t *pCred)
+{
+ const bool fUnrestricted = getminor(*pDev) == 0;
+ PSUPDRVSESSION pSession;
+ int rc;
+
+ LogFlowFunc(("VBoxDrvSolarisOpen: pDev=%p:%#x\n", pDev, *pDev));
+
+ /*
+ * Validate input
+ */
+ if ( (getminor(*pDev) != 0 && getminor(*pDev) != 1)
+ || fType != OTYP_CHR)
+ return EINVAL; /* See mmopen for precedent. */
+
+#ifndef USE_SESSION_HASH
+ /*
+ * Locate a new device open instance.
+ *
+ * For each open call we'll allocate an item in the soft state of the device.
+ * The item index is stored in the dev_t. I hope this is ok...
+ */
+ vbox_devstate_t *pState = NULL;
+ unsigned iOpenInstance;
+ for (iOpenInstance = 0; iOpenInstance < 4096; iOpenInstance++)
+ {
+ if ( !ddi_get_soft_state(g_pVBoxDrvSolarisState, iOpenInstance) /* faster */
+ && ddi_soft_state_zalloc(g_pVBoxDrvSolarisState, iOpenInstance) == DDI_SUCCESS)
+ {
+ pState = ddi_get_soft_state(g_pVBoxDrvSolarisState, iOpenInstance);
+ break;
+ }
+ }
+ if (!pState)
+ {
+ LogRel(("VBoxDrvSolarisOpen: too many open instances.\n"));
+ return ENXIO;
+ }
+
+ /*
+ * Create a new session.
+ */
+ rc = supdrvCreateSession(&g_DevExt, true /* fUser */, fUnrestricted, &pSession);
+ if (RT_SUCCESS(rc))
+ {
+ pSession->Uid = crgetruid(pCred);
+ pSession->Gid = crgetrgid(pCred);
+
+ pState->pSession = pSession;
+ *pDev = makedevice(getmajor(*pDev), iOpenInstance);
+ LogFlow(("VBoxDrvSolarisOpen: Dev=%#x pSession=%p pid=%d r0proc=%p thread=%p\n",
+ *pDev, pSession, RTProcSelf(), RTR0ProcHandleSelf(), RTThreadNativeSelf() ));
+ return 0;
+ }
+
+ /* failed - clean up */
+ ddi_soft_state_free(g_pVBoxDrvSolarisState, iOpenInstance);
+
+#else
+ /*
+ * Create a new session.
+ * Sessions in Solaris driver are mostly useless. It's however needed
+ * in VBoxDrvSolarisIOCtlSlow() while calling supdrvIOCtl()
+ */
+ rc = supdrvCreateSession(&g_DevExt, true /* fUser */, fUnrestricted, &pSession);
+ if (RT_SUCCESS(rc))
+ {
+ unsigned iHash;
+
+ pSession->Uid = crgetruid(pCred);
+ pSession->Gid = crgetrgid(pCred);
+
+ /*
+ * Insert it into the hash table.
+ */
+# error "Only one entry per process!"
+ iHash = SESSION_HASH(pSession->Process);
+ RTSpinlockAcquire(g_Spinlock);
+ pSession->pNextHash = g_apSessionHashTab[iHash];
+ g_apSessionHashTab[iHash] = pSession;
+ RTSpinlockRelease(g_Spinlock);
+ LogFlow(("VBoxDrvSolarisOpen success\n"));
+ }
+
+ int instance;
+ for (instance = 0; instance < DEVICE_MAXINSTANCES; instance++)
+ {
+ vbox_devstate_t *pState = ddi_get_soft_state(g_pVBoxDrvSolarisState, instance);
+ if (pState)
+ break;
+ }
+
+ if (instance >= DEVICE_MAXINSTANCES)
+ {
+ LogRel(("VBoxDrvSolarisOpen: All instances exhausted\n"));
+ return ENXIO;
+ }
+
+ *pDev = makedevice(getmajor(*pDev), instance);
+#endif
+
+ return VBoxSupDrvErr2SolarisErr(rc);
+}
+
+
+static int VBoxDrvSolarisClose(dev_t Dev, int flag, int otyp, cred_t *cred)
+{
+ LogFlowFunc(("VBoxDrvSolarisClose: Dev=%#x\n", Dev));
+
+#ifndef USE_SESSION_HASH
+ /*
+ * Get the session and free the soft state item.
+ */
+ vbox_devstate_t *pState = ddi_get_soft_state(g_pVBoxDrvSolarisState, getminor(Dev));
+ if (!pState)
+ {
+ LogRel(("VBoxDrvSolarisClose: no state data for %#x (%d)\n", Dev, getminor(Dev)));
+ return EFAULT;
+ }
+
+ PSUPDRVSESSION pSession = pState->pSession;
+ pState->pSession = NULL;
+ ddi_soft_state_free(g_pVBoxDrvSolarisState, getminor(Dev));
+
+ if (!pSession)
+ {
+ LogRel(("VBoxDrvSolarisClose: no session in state data for %#x (%d)\n", Dev, getminor(Dev)));
+ return EFAULT;
+ }
+ LogFlow(("VBoxDrvSolarisClose: Dev=%#x pSession=%p pid=%d r0proc=%p thread=%p\n",
+ Dev, pSession, RTProcSelf(), RTR0ProcHandleSelf(), RTThreadNativeSelf() ));
+
+#else
+ const RTPROCESS Process = RTProcSelf();
+ const unsigned iHash = SESSION_HASH(Process);
+ PSUPDRVSESSION pSession;
+
+ /*
+ * Remove from the hash table.
+ */
+ RTSpinlockAcquire(g_Spinlock);
+ pSession = g_apSessionHashTab[iHash];
+ if (pSession)
+ {
+ if (pSession->Process == Process)
+ {
+ g_apSessionHashTab[iHash] = pSession->pNextHash;
+ pSession->pNextHash = NULL;
+ }
+ else
+ {
+ PSUPDRVSESSION pPrev = pSession;
+ pSession = pSession->pNextHash;
+ while (pSession)
+ {
+ if (pSession->Process == Process)
+ {
+ pPrev->pNextHash = pSession->pNextHash;
+ pSession->pNextHash = NULL;
+ break;
+ }
+
+ /* next */
+ pPrev = pSession;
+ pSession = pSession->pNextHash;
+ }
+ }
+ }
+ RTSpinlockRelease(g_Spinlock);
+ if (!pSession)
+ {
+ LogRel(("VBoxDrvSolarisClose: WHAT?!? pSession == NULL! This must be a mistake... pid=%d (close)\n", (int)Process));
+ return EFAULT;
+ }
+#endif
+
+ /*
+ * Close the session.
+ */
+ supdrvSessionRelease(pSession);
+ return 0;
+}
+
+
+static int VBoxDrvSolarisRead(dev_t Dev, struct uio *pUio, cred_t *pCred)
+{
+ LogFlowFunc(("VBoxDrvSolarisRead"));
+ return 0;
+}
+
+
+static int VBoxDrvSolarisWrite(dev_t Dev, struct uio *pUio, cred_t *pCred)
+{
+ LogFlowFunc(("VBoxDrvSolarisWrite"));
+ return 0;
+}
+
+
+/**
+ * Driver ioctl, an alternate entry point for this character driver.
+ *
+ * @param Dev Device number
+ * @param iCmd Operation identifier
+ * @param pArgs Arguments from user to driver
+ * @param Mode Information bitfield (read/write, address space etc.)
+ * @param pCred User credentials
+ * @param pVal Return value for calling process.
+ *
+ * @return corresponding solaris error code.
+ */
+static int VBoxDrvSolarisIOCtl(dev_t Dev, int iCmd, intptr_t pArgs, int Mode, cred_t *pCred, int *pVal)
+{
+#ifndef USE_SESSION_HASH
+ /*
+ * Get the session from the soft state item.
+ */
+ vbox_devstate_t *pState = ddi_get_soft_state(g_pVBoxDrvSolarisState, getminor(Dev));
+ if (!pState)
+ {
+ LogRel(("VBoxDrvSolarisIOCtl: no state data for %#x (%d)\n", Dev, getminor(Dev)));
+ return EINVAL;
+ }
+
+ PSUPDRVSESSION pSession = pState->pSession;
+ if (!pSession)
+ {
+ LogRel(("VBoxDrvSolarisIOCtl: no session in state data for %#x (%d)\n", Dev, getminor(Dev)));
+ return DDI_SUCCESS;
+ }
+#else
+ const RTPROCESS Process = RTProcSelf();
+ const unsigned iHash = SESSION_HASH(Process);
+ PSUPDRVSESSION pSession;
+ const bool fUnrestricted = getminor(Dev) == 0;
+
+ /*
+ * Find the session.
+ */
+ RTSpinlockAcquire(g_Spinlock);
+ pSession = g_apSessionHashTab[iHash];
+ while (pSession && pSession->Process != Process && pSession->fUnrestricted == fUnrestricted);
+ pSession = pSession->pNextHash;
+ RTSpinlockRelease(g_Spinlock);
+ if (!pSession)
+ {
+ LogRel(("VBoxSupDrvIOCtl: WHAT?!? pSession == NULL! This must be a mistake... pid=%d iCmd=%#x Dev=%#x\n",
+ (int)Process, iCmd, (int)Dev));
+ return EINVAL;
+ }
+#endif
+
+ /*
+ * Deal with the two high-speed IOCtl that takes it's arguments from
+ * the session and iCmd, and only returns a VBox status code.
+ */
+ AssertCompile((SUP_IOCTL_FAST_DO_FIRST & 0xff) == (SUP_IOCTL_FLAG | 64));
+ if ( (unsigned)(iCmd - SUP_IOCTL_FAST_DO_FIRST) < (unsigned)32
+ && pSession->fUnrestricted)
+ {
+ *pVal = supdrvIOCtlFast(iCmd - SUP_IOCTL_FAST_DO_FIRST, pArgs, &g_DevExt, pSession);
+ return 0;
+ }
+
+ return VBoxDrvSolarisIOCtlSlow(pSession, iCmd, Mode, pArgs);
+}
+
+
+/** @def IOCPARM_LEN
+ * Gets the length from the ioctl number.
+ * This is normally defined by sys/ioccom.h on BSD systems...
+ */
+#ifndef IOCPARM_LEN
+# define IOCPARM_LEN(x) ( ((x) >> 16) & IOCPARM_MASK )
+#endif
+
+
+/**
+ * Worker for VBoxSupDrvIOCtl that takes the slow IOCtl functions.
+ *
+ * @returns Solaris errno.
+ *
+ * @param pSession The session.
+ * @param iCmd The IOCtl command.
+ * @param Mode Information bitfield (for specifying ownership of data)
+ * @param iArg User space address of the request buffer.
+ */
+static int VBoxDrvSolarisIOCtlSlow(PSUPDRVSESSION pSession, int iCmd, int Mode, intptr_t iArg)
+{
+ int rc;
+ uint32_t cbBuf = 0;
+ union
+ {
+ SUPREQHDR Hdr;
+ uint8_t abBuf[64];
+ } StackBuf;
+ PSUPREQHDR pHdr;
+
+
+ /*
+ * Read the header.
+ */
+ if (RT_UNLIKELY(IOCPARM_LEN(iCmd) != sizeof(StackBuf.Hdr)))
+ {
+ LogRel(("VBoxDrvSolarisIOCtlSlow: 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(("VBoxDrvSolarisIOCtlSlow: ddi_copyin(,%#lx,) failed; iCmd=%#x. rc=%d\n", iArg, iCmd, rc));
+ return EFAULT;
+ }
+ if (RT_UNLIKELY((StackBuf.Hdr.fFlags & SUPREQHDR_FLAGS_MAGIC_MASK) != SUPREQHDR_FLAGS_MAGIC))
+ {
+ LogRel(("VBoxDrvSolarisIOCtlSlow: bad header magic %#x; iCmd=%#x\n", StackBuf.Hdr.fFlags & SUPREQHDR_FLAGS_MAGIC_MASK, 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)
+ || cbBuf > _1M*16))
+ {
+ LogRel(("VBoxDrvSolarisIOCtlSlow: max(%#x,%#x); iCmd=%#x\n", StackBuf.Hdr.cbIn, StackBuf.Hdr.cbOut, iCmd));
+ return EINVAL;
+ }
+
+ /*
+ * Buffer the request.
+ */
+ if (cbBuf <= sizeof(StackBuf))
+ pHdr = &StackBuf.Hdr;
+ else
+ {
+ pHdr = RTMemTmpAlloc(cbBuf);
+ if (RT_UNLIKELY(!pHdr))
+ {
+ LogRel(("VBoxDrvSolarisIOCtlSlow: failed to allocate buffer of %d bytes for iCmd=%#x.\n", cbBuf, iCmd));
+ return ENOMEM;
+ }
+ }
+ rc = ddi_copyin((void *)iArg, pHdr, cbBuf, Mode);
+ if (RT_UNLIKELY(rc))
+ {
+ LogRel(("VBoxDrvSolarisIOCtlSlow: 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 = supdrvIOCtl(iCmd, &g_DevExt, pSession, pHdr, cbBuf);
+
+ /*
+ * Copy ioctl data and output buffer back to user space.
+ */
+ if (RT_LIKELY(!rc))
+ {
+ uint32_t cbOut = pHdr->cbOut;
+ if (RT_UNLIKELY(cbOut > cbBuf))
+ {
+ LogRel(("VBoxDrvSolarisIOCtlSlow: too much output! %#x > %#x; iCmd=%#x!\n", cbOut, cbBuf, iCmd));
+ cbOut = cbBuf;
+ }
+ rc = ddi_copyout(pHdr, (void *)iArg, cbOut, Mode);
+ if (RT_UNLIKELY(rc != 0))
+ {
+ /* this is really bad */
+ LogRel(("VBoxDrvSolarisIOCtlSlow: 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;
+}
+
+
+/**
+ * The SUPDRV IDC entry point.
+ *
+ * @returns VBox status code, see supdrvIDC.
+ * @param uReq The request code.
+ * @param pReq The request.
+ */
+int VBOXCALL SUPDrvSolarisIDC(uint32_t uReq, PSUPDRVIDCREQHDR pReq)
+{
+ PSUPDRVSESSION pSession;
+
+ /*
+ * Some quick validations.
+ */
+ if (RT_UNLIKELY(!RT_VALID_PTR(pReq)))
+ return VERR_INVALID_POINTER;
+
+ pSession = pReq->pSession;
+ if (pSession)
+ {
+ if (RT_UNLIKELY(!RT_VALID_PTR(pSession)))
+ return VERR_INVALID_PARAMETER;
+ if (RT_UNLIKELY(pSession->pDevExt != &g_DevExt))
+ return VERR_INVALID_PARAMETER;
+ }
+ else if (RT_UNLIKELY(uReq != SUPDRV_IDC_REQ_CONNECT))
+ return VERR_INVALID_PARAMETER;
+
+ /*
+ * Do the job.
+ */
+ return supdrvIDC(uReq, &g_DevExt, pSession, pReq);
+}
+
+
+/**
+ * Converts an supdrv error code to a solaris error code.
+ *
+ * @returns corresponding solaris error code.
+ * @param rc IPRT status code.
+ */
+static int VBoxSupDrvErr2SolarisErr(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;
+}
+
+
+void VBOXCALL supdrvOSCleanupSession(PSUPDRVDEVEXT pDevExt, PSUPDRVSESSION pSession)
+{
+ NOREF(pDevExt);
+ NOREF(pSession);
+}
+
+
+void VBOXCALL supdrvOSSessionHashTabInserted(PSUPDRVDEVEXT pDevExt, PSUPDRVSESSION pSession, void *pvUser)
+{
+ NOREF(pDevExt); NOREF(pSession); NOREF(pvUser);
+}
+
+
+void VBOXCALL supdrvOSSessionHashTabRemoved(PSUPDRVDEVEXT pDevExt, PSUPDRVSESSION pSession, void *pvUser)
+{
+ NOREF(pDevExt); NOREF(pSession); NOREF(pvUser);
+}
+
+
+/**
+ * Initializes any OS specific object creator fields.
+ */
+void VBOXCALL supdrvOSObjInitCreator(PSUPDRVOBJ pObj, PSUPDRVSESSION pSession)
+{
+ NOREF(pObj);
+ NOREF(pSession);
+}
+
+
+/**
+ * Checks if the session can access the object.
+ *
+ * @returns true if a decision has been made.
+ * @returns false if the default access policy should be applied.
+ *
+ * @param pObj The object in question.
+ * @param pSession The session wanting to access the object.
+ * @param pszObjName The object name, can be NULL.
+ * @param prc Where to store the result when returning true.
+ */
+bool VBOXCALL supdrvOSObjCanAccess(PSUPDRVOBJ pObj, PSUPDRVSESSION pSession, const char *pszObjName, int *prc)
+{
+ NOREF(pObj);
+ NOREF(pSession);
+ NOREF(pszObjName);
+ NOREF(prc);
+ return false;
+}
+
+
+bool VBOXCALL supdrvOSGetForcedAsyncTscMode(PSUPDRVDEVEXT pDevExt)
+{
+ return false;
+}
+
+
+bool VBOXCALL supdrvOSAreCpusOfflinedOnSuspend(void)
+{
+ /** @todo verify this. */
+ return false;
+}
+
+
+bool VBOXCALL supdrvOSAreTscDeltasInSync(void)
+{
+ return false;
+}
+
+
+#if defined(VBOX_WITH_NATIVE_SOLARIS_LOADING) \
+ && !defined(VBOX_WITHOUT_NATIVE_R0_LOADER)
+
+int VBOXCALL supdrvOSLdrOpen(PSUPDRVDEVEXT pDevExt, PSUPDRVLDRIMAGE pImage, const char *pszFilename)
+{
+ pImage->idSolMod = -1;
+ pImage->pSolModCtl = NULL;
+
+# if 1 /* This approach requires _init/_fini/_info stubs. */
+ /*
+ * Construct a filename that escapes the module search path and let us
+ * specify a root path.
+ */
+ /** @todo change this to use modctl and use_path=0. */
+ const char *pszName = RTPathFilename(pszFilename);
+ AssertReturn(pszName, VERR_INVALID_PARAMETER);
+ char *pszSubDir = RTStrAPrintf2("../../../../../../../../../../..%.*s", pszName - pszFilename - 1, pszFilename);
+ if (!pszSubDir)
+ return VERR_NO_STR_MEMORY;
+ int idMod = modload(pszSubDir, pszName);
+ if (idMod == -1)
+ {
+ /* This is an horrible hack for avoiding the mod-present check in
+ modrload on S10. Fortunately, nobody else seems to be using that
+ variable... */
+ extern int swaploaded;
+ int saved_swaploaded = swaploaded;
+ swaploaded = 0;
+ idMod = modload(pszSubDir, pszName);
+ swaploaded = saved_swaploaded;
+ }
+ RTStrFree(pszSubDir);
+ if (idMod == -1)
+ {
+ LogRel(("modload(,%s): failed, could be anything...\n", pszFilename));
+ return VERR_LDR_GENERAL_FAILURE;
+ }
+
+ modctl_t *pModCtl = mod_hold_by_id(idMod);
+ if (!pModCtl)
+ {
+ LogRel(("mod_hold_by_id(,%s): failed, weird.\n", pszFilename));
+ /* No point in calling modunload. */
+ return VERR_LDR_GENERAL_FAILURE;
+ }
+ pModCtl->mod_loadflags |= MOD_NOAUTOUNLOAD | MOD_NOUNLOAD; /* paranoia */
+
+# else
+
+ const int idMod = -1;
+ modctl_t *pModCtl = mod_hold_by_name(pszFilename);
+ if (!pModCtl)
+ {
+ LogRel(("mod_hold_by_name failed for '%s'\n", pszFilename));
+ return VERR_LDR_GENERAL_FAILURE;
+ }
+
+ int rc = kobj_load_module(pModCtl, 0 /*use_path*/);
+ if (rc != 0)
+ {
+ LogRel(("kobj_load_module failed with rc=%d for '%s'\n", rc, pszFilename));
+ mod_release_mod(pModCtl);
+ return RTErrConvertFromErrno(rc);
+ }
+# endif
+
+ /*
+ * Get the module info.
+ *
+ * Note! The text section is actually not at mi_base, but and the next
+ * alignment boundrary and there seems to be no easy way of
+ * getting at this address. This sabotages supdrvOSLdrLoad.
+ * Bastards!
+ */
+ struct modinfo ModInfo;
+ kobj_getmodinfo(pModCtl->mod_mp, &ModInfo);
+ pImage->pvImage = ModInfo.mi_base;
+ pImage->idSolMod = idMod;
+ pImage->pSolModCtl = pModCtl;
+
+ mod_release_mod(pImage->pSolModCtl);
+ LogRel(("supdrvOSLdrOpen: succeeded for '%s' (mi_base=%p mi_size=%#x), id=%d ctl=%p\n",
+ pszFilename, ModInfo.mi_base, ModInfo.mi_size, idMod, pModCtl));
+ return VINF_SUCCESS;
+}
+
+
+int VBOXCALL supdrvOSLdrValidatePointer(PSUPDRVDEVEXT pDevExt, PSUPDRVLDRIMAGE pImage, void *pv,
+ const uint8_t *pbImageBits, const char *pszSymbol)
+{
+ NOREF(pDevExt); NOREF(pImage); NOREF(pv); NOREF(pbImageBits); NOREF(pszSymbol);
+ if (kobj_addrcheck(pImage->pSolModCtl->mod_mp, pv))
+ return VERR_INVALID_PARAMETER;
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * Resolves a module entry point address.
+ *
+ * @returns VBox status code.
+ * @param pImage The image.
+ * @param pszSymbol The symbol name.
+ * @param ppvValue Where to store the value. On input this holds
+ * the symbol value SUPLib calculated.
+ */
+static int supdrvSolLdrResolvEp(PSUPDRVLDRIMAGE pImage, const char *pszSymbol, void **ppvValue)
+{
+ /* Don't try resolve symbols which, according to SUPLib, aren't there. */
+ if (!*ppvValue)
+ return VINF_SUCCESS;
+
+ uintptr_t uValue = modlookup_by_modctl(pImage->pSolModCtl, pszSymbol);
+ if (!uValue)
+ {
+ LogRel(("supdrvOSLdrLoad on %s failed to resolve %s\n", pImage->szName, pszSymbol));
+ return VERR_SYMBOL_NOT_FOUND;
+ }
+ *ppvValue = (void *)uValue;
+ return VINF_SUCCESS;
+}
+
+
+int VBOXCALL supdrvOSLdrLoad(PSUPDRVDEVEXT pDevExt, PSUPDRVLDRIMAGE pImage, const uint8_t *pbImageBits, PSUPLDRLOAD pReq)
+{
+#if 0 /* This doesn't work because of text alignment. */
+ /*
+ * Comparing is very very difficult since text and data may be allocated
+ * separately.
+ */
+ size_t cbCompare = RT_MIN(pImage->cbImageBits, 64);
+ if (memcmp(pImage->pvImage, pbImageBits, cbCompare))
+ {
+ LogRel(("Image mismatch: %s (%p)\n", pImage->szName, pImage->pvImage));
+ LogRel(("Native: %.*Rhxs\n", cbCompare, pImage->pvImage));
+ LogRel(("SUPLib: %.*Rhxs\n", cbCompare, pbImageBits));
+ return VERR_LDR_MISMATCH_NATIVE;
+ }
+#endif
+
+ /*
+ * Get the exported symbol addresses.
+ */
+ int rc;
+ modctl_t *pModCtl = mod_hold_by_id(pImage->idSolMod);
+ if (pModCtl && pModCtl == pImage->pSolModCtl)
+ {
+ uint32_t iSym = pImage->cSymbols;
+ while (iSym-- > 0)
+ {
+ const char *pszSymbol = &pImage->pachStrTab[pImage->paSymbols[iSym].offName];
+ uintptr_t uValue = modlookup_by_modctl(pImage->pSolModCtl, pszSymbol);
+ if (!uValue)
+ {
+ LogRel(("supdrvOSLdrLoad on %s failed to resolve the exported symbol: '%s'\n", pImage->szName, pszSymbol));
+ break;
+ }
+ uintptr_t offSymbol = uValue - (uintptr_t)pImage->pvImage;
+ pImage->paSymbols[iSym].offSymbol = offSymbol;
+ if (pImage->paSymbols[iSym].offSymbol != (int32_t)offSymbol)
+ {
+ LogRel(("supdrvOSLdrLoad on %s symbol out of range: %p (%s) \n", pImage->szName, offSymbol, pszSymbol));
+ break;
+ }
+ }
+
+ rc = iSym == UINT32_MAX ? VINF_SUCCESS : VERR_LDR_GENERAL_FAILURE;
+
+ /*
+ * Get the standard module entry points.
+ */
+ if (RT_SUCCESS(rc))
+ {
+ rc = supdrvSolLdrResolvEp(pImage, "ModuleInit", (void **)&pImage->pfnModuleInit);
+ if (RT_SUCCESS(rc))
+ rc = supdrvSolLdrResolvEp(pImage, "ModuleTerm", (void **)&pImage->pfnModuleTerm);
+
+ switch (pReq->u.In.eEPType)
+ {
+ case SUPLDRLOADEP_VMMR0:
+ {
+ if (RT_SUCCESS(rc))
+ rc = supdrvSolLdrResolvEp(pImage, "VMMR0EntryFast", (void **)&pReq->u.In.EP.VMMR0.pvVMMR0EntryFast);
+ if (RT_SUCCESS(rc))
+ rc = supdrvSolLdrResolvEp(pImage, "VMMR0EntryEx", (void **)&pReq->u.In.EP.VMMR0.pvVMMR0EntryEx);
+ break;
+ }
+
+ case SUPLDRLOADEP_SERVICE:
+ {
+ /** @todo we need the name of the entry point. */
+ return VERR_NOT_SUPPORTED;
+ }
+ }
+ }
+
+ mod_release_mod(pImage->pSolModCtl);
+ }
+ else
+ {
+ LogRel(("mod_hold_by_id failed in supdrvOSLdrLoad on %s: %p\n", pImage->szName, pModCtl));
+ rc = VERR_LDR_MISMATCH_NATIVE;
+ }
+ return rc;
+}
+
+
+void VBOXCALL supdrvOSLdrUnload(PSUPDRVDEVEXT pDevExt, PSUPDRVLDRIMAGE pImage)
+{
+# if 1
+ pImage->pSolModCtl->mod_loadflags &= ~MOD_NOUNLOAD;
+ int rc = modunload(pImage->idSolMod);
+ if (rc)
+ LogRel(("modunload(%u (%s)) failed: %d\n", pImage->idSolMod, pImage->szName, rc));
+# else
+ kobj_unload_module(pImage->pSolModCtl);
+# endif
+ pImage->pSolModCtl = NULL;
+ pImage->idSolMod = NULL;
+}
+
+#else /* !VBOX_WITH_NATIVE_SOLARIS_LOADING */
+
+int VBOXCALL supdrvOSLdrOpen(PSUPDRVDEVEXT pDevExt, PSUPDRVLDRIMAGE pImage, const char *pszFilename)
+{
+ NOREF(pDevExt); NOREF(pImage); NOREF(pszFilename);
+ return VERR_NOT_SUPPORTED;
+}
+
+
+int VBOXCALL supdrvOSLdrValidatePointer(PSUPDRVDEVEXT pDevExt, PSUPDRVLDRIMAGE pImage, void *pv,
+ const uint8_t *pbImageBits, const char *pszSymbol)
+{
+ NOREF(pDevExt); NOREF(pImage); NOREF(pv); NOREF(pbImageBits); NOREF(pszSymbol);
+ return VERR_NOT_SUPPORTED;
+}
+
+
+int VBOXCALL supdrvOSLdrLoad(PSUPDRVDEVEXT pDevExt, PSUPDRVLDRIMAGE pImage, const uint8_t *pbImageBits, PSUPLDRLOAD pReq)
+{
+ NOREF(pDevExt); NOREF(pImage); NOREF(pbImageBits); NOREF(pReq);
+ return VERR_NOT_SUPPORTED;
+}
+
+
+void VBOXCALL supdrvOSLdrUnload(PSUPDRVDEVEXT pDevExt, PSUPDRVLDRIMAGE pImage)
+{
+ NOREF(pDevExt); NOREF(pImage);
+}
+
+#endif /* !VBOX_WITH_NATIVE_SOLARIS_LOADING */
+
+
+void VBOXCALL supdrvOSLdrNotifyOpened(PSUPDRVDEVEXT pDevExt, PSUPDRVLDRIMAGE pImage, const char *pszFilename)
+{
+ NOREF(pDevExt); NOREF(pImage); NOREF(pszFilename);
+}
+
+
+void VBOXCALL supdrvOSLdrNotifyUnloaded(PSUPDRVDEVEXT pDevExt, PSUPDRVLDRIMAGE pImage)
+{
+ NOREF(pDevExt); NOREF(pImage);
+}
+
+
+int VBOXCALL supdrvOSLdrQuerySymbol(PSUPDRVDEVEXT pDevExt, PSUPDRVLDRIMAGE pImage,
+ const char *pszSymbol, size_t cchSymbol, void **ppvSymbol)
+{
+ RT_NOREF(pDevExt, pImage, pszSymbol, cchSymbol, ppvSymbol);
+ return VERR_WRONG_ORDER;
+}
+
+
+void VBOXCALL supdrvOSLdrRetainWrapperModule(PSUPDRVDEVEXT pDevExt, PSUPDRVLDRIMAGE pImage)
+{
+ RT_NOREF(pDevExt, pImage);
+ AssertFailed();
+}
+
+
+void VBOXCALL supdrvOSLdrReleaseWrapperModule(PSUPDRVDEVEXT pDevExt, PSUPDRVLDRIMAGE pImage)
+{
+ RT_NOREF(pDevExt, pImage);
+ AssertFailed();
+}
+
+#ifdef SUPDRV_WITH_MSR_PROBER
+
+int VBOXCALL supdrvOSMsrProberRead(uint32_t uMsr, RTCPUID idCpu, uint64_t *puValue)
+{
+/** @todo cmi_hdl_rdmsr can safely do this. there is also the on_trap() fun
+ * for catching traps that could possibly be used directly. */
+ NOREF(uMsr); NOREF(idCpu); NOREF(puValue);
+ return VERR_NOT_SUPPORTED;
+}
+
+
+int VBOXCALL supdrvOSMsrProberWrite(uint32_t uMsr, RTCPUID idCpu, uint64_t uValue)
+{
+/** @todo cmi_hdl_wrmsr can safely do this. */
+ NOREF(uMsr); NOREF(idCpu); NOREF(uValue);
+ return VERR_NOT_SUPPORTED;
+}
+
+
+int VBOXCALL supdrvOSMsrProberModify(RTCPUID idCpu, PSUPMSRPROBER pReq)
+{
+ NOREF(idCpu); NOREF(pReq);
+ return VERR_NOT_SUPPORTED;
+}
+
+#endif /* SUPDRV_WITH_MSR_PROBER */
+
+
+SUPR0DECL(int) SUPR0HCPhysToVirt(RTHCPHYS HCPhys, void **ppv)
+{
+ AssertReturn(!(HCPhys & PAGE_OFFSET_MASK), VERR_INVALID_POINTER);
+ AssertReturn(HCPhys != NIL_RTHCPHYS, VERR_INVALID_POINTER);
+ HCPhys >>= PAGE_SHIFT;
+ AssertReturn(HCPhys <= physmax, VERR_INVALID_POINTER);
+ *ppv = hat_kpm_pfn2va(HCPhys);
+ return VINF_SUCCESS;
+}
+
+
+RTDECL(int) SUPR0PrintfV(const char *pszFormat, va_list va)
+{
+ /* cmn_err() acquires adaptive mutexes. Not preemption safe, see @bugref{6657}. */
+ if (RTThreadPreemptIsEnabled(NIL_RTTHREAD))
+ {
+ char szMsg[512];
+ RTStrPrintfV(szMsg, sizeof(szMsg) - 1, pszFormat, va);
+ szMsg[sizeof(szMsg) - 1] = '\0';
+
+ cmn_err(CE_CONT, "%s", szMsg);
+ }
+ return 0;
+}
+
+
+SUPR0DECL(uint32_t) SUPR0GetKernelFeatures(void)
+{
+ return 0;
+}
+
+
+SUPR0DECL(bool) SUPR0FpuBegin(bool fCtxHook)
+{
+ RT_NOREF(fCtxHook);
+ return false;
+}
+
+
+SUPR0DECL(void) SUPR0FpuEnd(bool fCtxHook)
+{
+ RT_NOREF(fCtxHook);
+}
+
diff --git a/src/VBox/HostDrivers/Support/solaris/SUPLib-solaris.cpp b/src/VBox/HostDrivers/Support/solaris/SUPLib-solaris.cpp
new file mode 100644
index 00000000..8e12b929
--- /dev/null
+++ b/src/VBox/HostDrivers/Support/solaris/SUPLib-solaris.cpp
@@ -0,0 +1,245 @@
+/* $Id: SUPLib-solaris.cpp $ */
+/** @file
+ * VirtualBox Support Library - Solaris specific parts.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#define LOG_GROUP LOG_GROUP_SUP
+#ifdef IN_SUP_HARDENED_R3
+# undef DEBUG /* Warning: disables RT_STRICT */
+# ifndef LOG_DISABLED
+# define LOG_DISABLED
+# endif
+# define RTLOG_REL_DISABLED
+# include <iprt/log.h>
+#endif
+
+#include <VBox/types.h>
+#include <VBox/sup.h>
+#include <VBox/param.h>
+#include <VBox/err.h>
+#include <VBox/log.h>
+#include <iprt/path.h>
+#include <iprt/assert.h>
+#include <iprt/mem.h>
+#include <iprt/err.h>
+#include <iprt/string.h>
+#include "../SUPLibInternal.h"
+#include "../SUPDrvIOC.h"
+
+#include <sys/fcntl.h>
+#include <sys/ioctl.h>
+#include <sys/zone.h>
+
+#include <fcntl.h>
+#include <errno.h>
+#include <unistd.h>
+#include <sys/mman.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <zone.h>
+
+
+/*********************************************************************************************************************************
+* Defined Constants And Macros *
+*********************************************************************************************************************************/
+/** Solaris device link - system. */
+#define DEVICE_NAME_SYS "/devices/pseudo/vboxdrv@0:vboxdrv"
+/** Solaris device link - user. */
+#define DEVICE_NAME_USR "/devices/pseudo/vboxdrv@0:vboxdrvu"
+/** Solaris device link - system (non-global zone). */
+#define DEVICE_NAME_SYS_ZONE "/dev/vboxdrv"
+/** Solaris device link - user (non-global zone). */
+#define DEVICE_NAME_USR_ZONE "/dev/vboxdrvu"
+
+
+
+DECLHIDDEN(int) suplibOsInit(PSUPLIBDATA pThis, bool fPreInited, uint32_t fFlags, SUPINITOP *penmWhat, PRTERRINFO pErrInfo)
+{
+ /*
+ * Nothing to do if pre-inited.
+ */
+ if (fPreInited)
+ return VINF_SUCCESS;
+
+ /*
+ * Open dummy files to preallocate file descriptors, see @bugref{4650}.
+ */
+ for (int i = 0; i < SUPLIB_FLT_DUMMYFILES; i++)
+ {
+ pThis->ahDummy[i] = -1;
+ int hDummy = open("/dev/null", O_RDWR, 0);
+ if (hDummy >= 0)
+ {
+ if (fcntl(hDummy, F_SETFD, FD_CLOEXEC) == 0)
+ pThis->ahDummy[i] = hDummy;
+ else
+ {
+ close(hDummy);
+ LogRel(("Failed to set close on exec [%d] /dev/null! errno=%d\n", i, errno));
+ }
+ }
+ else
+ LogRel(("Failed to open[%d] /dev/null! errno=%d\n", i, errno));
+ }
+
+ /*
+ * Try to open the device.
+ */
+ const char *pszDeviceNm;
+ if (getzoneid() == GLOBAL_ZONEID)
+ pszDeviceNm = fFlags & SUPR3INIT_F_UNRESTRICTED ? DEVICE_NAME_SYS : DEVICE_NAME_USR;
+ else
+ pszDeviceNm = fFlags & SUPR3INIT_F_UNRESTRICTED ? DEVICE_NAME_SYS_ZONE : DEVICE_NAME_USR_ZONE;
+ int hDevice = open(pszDeviceNm, O_RDWR, 0);
+ if (hDevice < 0)
+ {
+ int rc;
+ switch (errno)
+ {
+ case ENODEV: rc = VERR_VM_DRIVER_LOAD_ERROR; break;
+ case EPERM:
+ case EACCES: rc = VERR_VM_DRIVER_NOT_ACCESSIBLE; break;
+ case ENOENT: rc = VERR_VM_DRIVER_NOT_INSTALLED; break;
+ default: rc = VERR_VM_DRIVER_OPEN_ERROR; break;
+ }
+ LogRel(("Failed to open \"%s\", errno=%d, rc=%Rrc\n", pszDeviceNm, errno, rc));
+ return rc;
+ }
+
+ /*
+ * Mark the file handle close on exec.
+ */
+ if (fcntl(hDevice, F_SETFD, FD_CLOEXEC) != 0)
+ {
+#ifdef IN_SUP_HARDENED_R3
+ int rc = VERR_INTERNAL_ERROR;
+#else
+ int err = errno;
+ int rc = RTErrConvertFromErrno(err);
+ LogRel(("suplibOSInit: setting FD_CLOEXEC failed, errno=%d (%Rrc)\n", err, rc));
+#endif
+ close(hDevice);
+ return rc;
+ }
+
+ pThis->hDevice = hDevice;
+ pThis->fUnrestricted = RT_BOOL(fFlags & SUPR3INIT_F_UNRESTRICTED);
+ return VINF_SUCCESS;
+}
+
+
+DECLHIDDEN(int) suplibOsTerm(PSUPLIBDATA pThis)
+{
+ /*
+ * Close the dummy files first.
+ */
+ for (int i = 0; i < SUPLIB_FLT_DUMMYFILES; i++)
+ {
+ if (pThis->ahDummy[i] != -1)
+ {
+ close(pThis->ahDummy[i]);
+ pThis->ahDummy[i] = -1;
+ }
+ }
+
+ /*
+ * Check if we're initialized
+ */
+ if (pThis->hDevice != (intptr_t)NIL_RTFILE)
+ {
+ if (close(pThis->hDevice))
+ AssertFailed();
+ pThis->hDevice = (intptr_t)NIL_RTFILE;
+ }
+
+ return VINF_SUCCESS;
+}
+
+
+#ifndef IN_SUP_HARDENED_R3
+
+DECLHIDDEN(int) suplibOsInstall(void)
+{
+ return VERR_NOT_IMPLEMENTED;
+}
+
+DECLHIDDEN(int) suplibOsUninstall(void)
+{
+ return VERR_NOT_IMPLEMENTED;
+}
+
+
+DECLHIDDEN(int) suplibOsIOCtl(PSUPLIBDATA pThis, uintptr_t uFunction, void *pvReq, size_t cbReq)
+{
+ if (RT_LIKELY(ioctl(pThis->hDevice, uFunction, pvReq) >= 0))
+ return VINF_SUCCESS;
+ return RTErrConvertFromErrno(errno);
+}
+
+
+DECLHIDDEN(int) suplibOsIOCtlFast(PSUPLIBDATA pThis, uintptr_t uFunction, uintptr_t idCpu)
+{
+ int rc = ioctl(pThis->hDevice, uFunction, idCpu);
+ if (rc == -1)
+ rc = errno;
+ return rc;
+}
+
+
+DECLHIDDEN(int) suplibOsPageAlloc(PSUPLIBDATA pThis, size_t cPages, uint32_t fFlags, void **ppvPages)
+{
+ RT_NOREF(pThis, fFlags);
+ *ppvPages = mmap(NULL, cPages * PAGE_SIZE, PROT_EXEC | PROT_READ | PROT_WRITE,
+ MAP_PRIVATE | MAP_ANON, -1, 0);
+ if (*ppvPages != (void *)-1)
+ return VINF_SUCCESS;
+ if (errno == EAGAIN)
+ return VERR_NO_MEMORY;
+ return RTErrConvertFromErrno(errno);
+}
+
+
+DECLHIDDEN(int) suplibOsPageFree(PSUPLIBDATA pThis, void *pvPages, size_t cPages)
+{
+ NOREF(pThis);
+ munmap(pvPages, cPages * PAGE_SIZE);
+ return VINF_SUCCESS;
+}
+
+#endif /* !IN_SUP_HARDENED_R3 */
+
diff --git a/src/VBox/HostDrivers/Support/solaris/SUPR0IdcClient-solaris.c b/src/VBox/HostDrivers/Support/solaris/SUPR0IdcClient-solaris.c
new file mode 100644
index 00000000..ba0b5e3e
--- /dev/null
+++ b/src/VBox/HostDrivers/Support/solaris/SUPR0IdcClient-solaris.c
@@ -0,0 +1,66 @@
+/* $Id: SUPR0IdcClient-solaris.c $ */
+/** @file
+ * VirtualBox Support Driver - IDC Client Lib, Solaris Specific Code.
+ */
+
+/*
+ * Copyright (C) 2008-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include "../SUPR0IdcClientInternal.h"
+#include <iprt/errcore.h>
+
+
+int VBOXCALL supR0IdcNativeOpen(PSUPDRVIDCHANDLE pHandle, PSUPDRVIDCREQCONNECT pReq)
+{
+ return supR0IdcNativeCall(pHandle, SUPDRV_IDC_REQ_CONNECT, &pReq->Hdr);
+}
+
+
+int VBOXCALL supR0IdcNativeClose(PSUPDRVIDCHANDLE pHandle, PSUPDRVIDCREQHDR pReq)
+{
+ return supR0IdcNativeCall(pHandle, SUPDRV_IDC_REQ_DISCONNECT, pReq);
+}
+
+
+int VBOXCALL supR0IdcNativeCall(PSUPDRVIDCHANDLE pHandle, uint32_t iReq, PSUPDRVIDCREQHDR pReq)
+{
+ int rc = SUPDrvSolarisIDC(iReq, pReq);
+ if (RT_SUCCESS(rc))
+ rc = pReq->rc;
+
+ NOREF(pHandle);
+ return rc;
+}
+
diff --git a/src/VBox/HostDrivers/Support/solaris/load.sh b/src/VBox/HostDrivers/Support/solaris/load.sh
new file mode 100755
index 00000000..b2f86bab
--- /dev/null
+++ b/src/VBox/HostDrivers/Support/solaris/load.sh
@@ -0,0 +1,128 @@
+#!/bin/bash
+# $Id: load.sh $
+## @file
+# For development.
+#
+
+#
+# Copyright (C) 2006-2023 Oracle and/or its affiliates.
+#
+# This file is part of VirtualBox base platform packages, as
+# available from https://www.virtualbox.org.
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation, in version 3 of the
+# License.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, see <https://www.gnu.org/licenses>.
+#
+# The contents of this file may alternatively be used under the terms
+# of the Common Development and Distribution License Version 1.0
+# (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+# in the VirtualBox distribution, in which case the provisions of the
+# CDDL are applicable instead of those of the GPL.
+#
+# You may elect to license modified versions of this file under the
+# terms and conditions of either the GPL or the CDDL or both.
+#
+# SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+#
+
+DRVNAME="vboxdrv"
+DRIVERS_USING_IT="vboxusb vboxusbmon vboxnet vboxflt vboxbow"
+
+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
+if [ ! -f "$DRVFILE.conf" ]; then
+ echo "load.sh: Cannot find $DRVFILE.conf or it's not a file..."
+ exit 1;
+fi
+
+SUDO=sudo
+hash "${SUDO}" 2> /dev/null || SUDO=pfexec
+#set -x
+
+# Disable the zone access service.
+servicefound=`svcs -H "virtualbox/zoneaccess" 2>/dev/null | grep '^online'`
+if test ! -z "$servicefound"; then
+ $SUDO svcadm disable svc:/application/virtualbox/zoneaccess:default
+fi
+
+# 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
+
+#
+# Reconfigure the driver so it get a major number.
+#
+# Note! We have to copy the driver and config files to somewhere the kernel can
+# find them. It is searched for as drv/${DRVNAME}.conf in
+# kobj_module_path, which is usually:
+# /platform/i86pc/kernel /kernel /usr/kernel
+# To try prevent bad drivers from being loaded on the next boot, we remove
+# always the files.
+#
+MY_RC=1
+set -e
+$SUDO rm -f \
+ "/platform/i86pc/kernel/drv/${DRVNAME}.conf" \
+ "/platform/i86pc/kernel/drv/${DRVNAME}" \
+ "/platform/i86pc/kernel/drv/amd64/${DRVNAME}"
+sync
+$SUDO cp "${DRVFILE}" /platform/i86pc/kernel/drv/amd64/
+$SUDO cp "${DRVFILE}.conf" /platform/i86pc/kernel/drv/
+set +e
+
+$SUDO rem_drv $DRVNAME
+if $SUDO add_drv -v $DRVNAME; then
+ sync
+ if $SUDO modload "/platform/i86pc/kernel/drv/amd64/${DRVNAME}"; then
+ echo "load.sh: successfully loaded the driver"
+ modinfo | grep -w "$DRVNAME"
+ MY_RC=0
+ if test ! -h "/dev/vboxdrv"; then
+ $SUDO ln -sf "/devices/pseudo/vboxdrv@0:vboxdrv" /dev/vboxdrv
+ $SUDO chmod 0666 /dev/vboxdrv
+ fi
+ else
+ dmesg | tail
+ echo "load.sh: modload failed"
+ fi
+else
+ dmesg | tail
+ echo "load.sh: add_drv failed."
+fi
+
+$SUDO rm -f \
+ "/platform/i86pc/kernel/drv/${DRVNAME}.conf" \
+ "/platform/i86pc/kernel/drv/${DRVNAME}" \
+ "/platform/i86pc/kernel/drv/amd64/${DRVNAME}"
+sync
+
+exit $MY_RC;
+
diff --git a/src/VBox/HostDrivers/Support/solaris/vboxdrv.conf b/src/VBox/HostDrivers/Support/solaris/vboxdrv.conf
new file mode 100644
index 00000000..fcc02230
--- /dev/null
+++ b/src/VBox/HostDrivers/Support/solaris/vboxdrv.conf
@@ -0,0 +1,43 @@
+# $Id: vboxdrv.conf $
+## @file
+# VirtualBox Solaris Host Driver Configuration.
+#
+
+#
+# Copyright (C) 2007-2023 Oracle and/or its affiliates.
+#
+# This file is part of VirtualBox base platform packages, as
+# available from https://www.virtualbox.org.
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation, in version 3 of the
+# License.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, see <https://www.gnu.org/licenses>.
+#
+# The contents of this file may alternatively be used under the terms
+# of the Common Development and Distribution License Version 1.0
+# (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+# in the VirtualBox distribution, in which case the provisions of the
+# CDDL are applicable instead of those of the GPL.
+#
+# You may elect to license modified versions of this file under the
+# terms and conditions of either the GPL or the CDDL or both.
+#
+# SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+#
+
+# This needs to go into /platform/i86pc/kernel/drv,
+# while the 64-bit driver object goes into the amd64
+# subdirectory (32-bit drivers goes into the same
+# directory). After copying execute add_drv vboxdrv.
+#
+name="vboxdrv" parent="pseudo" instance=0;
+
diff --git a/src/VBox/HostDrivers/Support/testcase/Makefile.kmk b/src/VBox/HostDrivers/Support/testcase/Makefile.kmk
new file mode 100644
index 00000000..039557ac
--- /dev/null
+++ b/src/VBox/HostDrivers/Support/testcase/Makefile.kmk
@@ -0,0 +1,158 @@
+# $Id: Makefile.kmk $
+## @file
+# Sub-Makefile for the SUPLib testcases.
+#
+
+#
+# Copyright (C) 2006-2023 Oracle and/or its affiliates.
+#
+# This file is part of VirtualBox base platform packages, as
+# available from https://www.virtualbox.org.
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation, in version 3 of the
+# License.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, see <https://www.gnu.org/licenses>.
+#
+# The contents of this file may alternatively be used under the terms
+# of the Common Development and Distribution License Version 1.0
+# (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+# in the VirtualBox distribution, in which case the provisions of the
+# CDDL are applicable instead of those of the GPL.
+#
+# You may elect to license modified versions of this file under the
+# terms and conditions of either the GPL or the CDDL or both.
+#
+# SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+#
+
+SUB_DEPTH = ../../../../..
+include $(KBUILD_PATH)/subheader.kmk
+
+# We need the VMM/Config.kmk one for the VMM_COMMON_DEFS variable.
+ifndef VBOX_VMM_CONFIG_KMK_INCLUDED
+ include $(PATH_ROOT)/src/VBox/VMM/Config.kmk
+endif
+
+PROGRAMS += \
+ SUPInstall \
+ SUPUninstall \
+ SUPLoggerCtl
+ifdef VBOX_WITH_TESTCASES
+ if defined(VBOX_WITH_HARDENING)
+ PROGRAMS += \
+ tstSupVerify
+ endif
+ if !defined(VBOX_WITH_HARDENING) || "$(KBUILD_TARGET)" != "win"
+ PROGRAMS += \
+ tstPage \
+ tstContiguous \
+ tstInit \
+ tstInt \
+ tstLow \
+ tstPin \
+ tstGetPagingMode \
+ tstSupLoadModule \
+ tstSupSem \
+ tstSupSem-Zombie \
+ tstSupTscDelta
+ endif
+ PROGRAMS.win += \
+ tstNtQueryStuff
+endif # VBOX_WITH_TESTCASES
+
+SUPInstall_TEMPLATE = VBoxR3Exe
+SUPInstall_SOURCES = SUPInstall.cpp
+SUPInstall_LIBS = $(LIB_RUNTIME)
+
+SUPUninstall_TEMPLATE = VBoxR3Exe
+SUPUninstall_SOURCES = SUPUninstall.cpp
+SUPUninstall_LIBS = $(LIB_RUNTIME)
+
+SUPLoggerCtl_TEMPLATE = VBoxR3Exe
+SUPLoggerCtl_SOURCES = SUPLoggerCtl.cpp
+SUPLoggerCtl_LIBS = $(LIB_RUNTIME)
+
+tstInt_TEMPLATE = VBoxR3Exe
+tstInt_DEFS = $(VMM_COMMON_DEFS)
+tstInt_SOURCES = tstInt.cpp
+tstInt_LIBS = $(LIB_RUNTIME)
+
+tstContiguous_TEMPLATE = VBoxR3TstExe
+tstContiguous_SOURCES = tstContiguous.cpp
+
+tstInit_TEMPLATE = VBoxR3TstExe
+tstInit_SOURCES = tstInit.cpp
+
+tstLow_TEMPLATE = VBoxR3TstExe
+tstLow_SOURCES = tstLow.cpp
+
+tstNtQueryStuff_TEMPLATE = VBoxR3TstExe
+tstNtQueryStuff_SDKS = VBoxNtDll
+tstNtQueryStuff_SOURCES = tstNtQueryStuff.cpp
+
+tstPin_TEMPLATE = VBoxR3TstExe
+tstPin_SOURCES = tstPin.cpp
+
+tstPage_TEMPLATE = VBoxR3TstExe
+tstPage_SOURCES = tstPage.cpp
+
+#
+# tstGIP-2
+#
+ifdef VBOX_WITH_TESTCASES
+ if defined(VBOX_WITH_HARDENING) && "$(KBUILD_TARGET)" == "win"
+ PROGRAMS += tstGIP-2Hardened
+ DLLS += tstGIP-2
+ else
+ PROGRAMS += tstGIP-2
+ endif
+endif
+
+tstGIP-2Hardened_TEMPLATE = VBoxR3HardenedTstExe
+ifdef VBOX_WITH_AUTOMATIC_DEFS_QUOTING
+ tstGIP-2Hardened_DEFS = PROGRAM_NAME_STR="tstGIP-2"
+else
+ tstGIP-2Hardened_DEFS = PROGRAM_NAME_STR=\"tstGIP-2\"
+endif
+tstGIP-2Hardened_SOURCES = ../SUPR3HardenedMainTemplateTestcase.cpp
+tstGIP-2Hardened_NAME = tstGIP-2
+
+if defined(VBOX_WITH_HARDENING) && "$(KBUILD_TARGET)" == "win"
+ tstGIP-2_TEMPLATE := VBoxR3HardenedTstDll
+else
+ tstGIP-2_TEMPLATE := VBoxR3TstExe
+endif
+tstGIP-2_SOURCES = tstGIP-2.cpp
+
+tstGetPagingMode_TEMPLATE = VBoxR3TstExe
+tstGetPagingMode_SOURCES = tstGetPagingMode.cpp
+
+tstSupLoadModule_TEMPLATE = VBoxR3TstExe
+tstSupLoadModule_SOURCES = tstSupLoadModule.cpp
+
+tstSupSem_TEMPLATE = VBoxR3TstExe
+tstSupSem_SOURCES = tstSupSem.cpp
+
+tstSupSem-Zombie_TEMPLATE = VBoxR3TstExe
+tstSupSem-Zombie_SOURCES = tstSupSem-Zombie.cpp
+
+tstSupTscDelta_TEMPLATE = VBoxR3TstExe
+tstSupTscDelta_SOURCES = tstSupTscDelta.cpp
+
+# For testing supR3HardenedVerifyFile on windows.
+tstSupVerify_TEMPLATE = VBoxR3TstExe
+tstSupVerify_SOURCES = tstSupVerify.cpp
+
+
+
+include $(FILE_KBUILD_SUB_FOOTER)
+
diff --git a/src/VBox/HostDrivers/Support/testcase/SUPInstall.cpp b/src/VBox/HostDrivers/Support/testcase/SUPInstall.cpp
new file mode 100644
index 00000000..246ff7a1
--- /dev/null
+++ b/src/VBox/HostDrivers/Support/testcase/SUPInstall.cpp
@@ -0,0 +1,69 @@
+/* $Id: SUPInstall.cpp $ */
+/** @file
+ * SUPInstall - Driver Install
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include <VBox/sup.h>
+#include <VBox/err.h>
+#include <iprt/initterm.h>
+#include <iprt/message.h>
+
+
+int main(int argc, char **argv)
+{
+ RTR3InitExeNoArguments(0);
+ if (argc != 1)
+ return RTMsgErrorExit(RTEXITCODE_SYNTAX, "This utility takes no arguments");
+ NOREF(argv);
+
+ int rc = SUPR3Install();
+ if (RT_SUCCESS(rc))
+ {
+ if (rc == VINF_SUCCESS)
+ RTMsgInfo("Installed successfully!");
+ else if (rc == VINF_ALREADY_INITIALIZED)
+ RTMsgInfo("Already loaded.");
+ else if (rc == VWRN_ALREADY_EXISTS)
+ RTMsgInfo("Service already existed; started successfully.");
+ else
+ RTMsgInfo("Unexpected status: %Rrc", rc);
+ return RTEXITCODE_SUCCESS;
+ }
+ return RTMsgErrorExit(RTEXITCODE_FAILURE, "installation failed. rc=%Rrc", rc);
+}
+
diff --git a/src/VBox/HostDrivers/Support/testcase/SUPLoggerCtl.cpp b/src/VBox/HostDrivers/Support/testcase/SUPLoggerCtl.cpp
new file mode 100644
index 00000000..61a6e718
--- /dev/null
+++ b/src/VBox/HostDrivers/Support/testcase/SUPLoggerCtl.cpp
@@ -0,0 +1,196 @@
+/* $Id: SUPLoggerCtl.cpp $ */
+/** @file
+ * SUPLoggerCtl - Support Driver Logger Control.
+ */
+
+/*
+ * Copyright (C) 2009-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include <VBox/sup.h>
+#include <iprt/buildconfig.h>
+#include <iprt/initterm.h>
+#include <iprt/getopt.h>
+#include <iprt/stream.h>
+#include <iprt/string.h>
+#include <iprt/ctype.h>
+#include <iprt/errcore.h>
+
+
+/**
+ * Prints the usage.
+ * @returns 1.
+ */
+static int usage(void)
+{
+ RTPrintf("usage: SUPLoggerCtl [-f|--flags <flags-settings>] \\\n"
+ " [-g|--groups <groups-settings>] \\\n"
+ " [-d|--dest <destination-specifiers>] \\\n"
+ " [-l|--which <release|debug>] \\\n"
+ " [-o|--what <set|create|destroy>]\n"
+ " or: SUPLoggerCtl <-h|--help>\n"
+ "\n"
+ );
+ return 1;
+}
+
+
+int main(int argc, char **argv)
+{
+ RTR3InitExe(argc, &argv, RTR3INIT_FLAGS_TRY_SUPLIB);
+
+ /*
+ * Options are mandatory.
+ */
+ if (argc <= 1)
+ return usage();
+
+ /*
+ * Parse the options.
+ */
+ static const RTGETOPTDEF s_aOptions[] =
+ {
+ { "--flags", 'f', RTGETOPT_REQ_STRING },
+ { "--groups", 'g', RTGETOPT_REQ_STRING },
+ { "--dest", 'd', RTGETOPT_REQ_STRING },
+ { "--what", 'o', RTGETOPT_REQ_STRING },
+ { "--which", 'l', RTGETOPT_REQ_STRING },
+ };
+
+ const char *pszFlags = "";
+ const char *pszGroups = "";
+ const char *pszDest = "";
+ SUPLOGGER enmWhich = SUPLOGGER_DEBUG;
+ enum
+ {
+ kSupLoggerCtl_Set, kSupLoggerCtl_Create, kSupLoggerCtl_Destroy
+ } enmWhat = kSupLoggerCtl_Set;
+
+ int ch;
+ RTGETOPTUNION Val;
+ RTGETOPTSTATE GetState;
+ RTGetOptInit(&GetState, argc, argv, s_aOptions, RT_ELEMENTS(s_aOptions), 1, 0);
+ while ((ch = RTGetOpt(&GetState, &Val)))
+ {
+ switch (ch)
+ {
+ case 'f':
+ pszFlags = Val.psz;
+ break;
+
+ case 'g':
+ pszGroups = Val.psz;
+ break;
+
+ case 'd':
+ pszDest = Val.psz;
+ break;
+
+ case 'o':
+ if (!strcmp(Val.psz, "set"))
+ enmWhat = kSupLoggerCtl_Set;
+ else if (!strcmp(Val.psz, "create"))
+ enmWhat = kSupLoggerCtl_Create;
+ else if (!strcmp(Val.psz, "destroy"))
+ enmWhat = kSupLoggerCtl_Destroy;
+ else
+ {
+ RTStrmPrintf(g_pStdErr, "SUPLoggerCtl: error: Unknown operation '%s'.\n", Val.psz);
+ return 1;
+ }
+ break;
+
+ case 'l':
+ if (!strcmp(Val.psz, "debug"))
+ enmWhich = SUPLOGGER_DEBUG;
+ else if (!strcmp(Val.psz, "release"))
+ enmWhich = SUPLOGGER_RELEASE;
+ else
+ {
+ RTStrmPrintf(g_pStdErr, "SUPLoggerCtl: error: Unknown logger '%s'.\n", Val.psz);
+ return 1;
+ }
+ break;
+
+ case 'h':
+ return usage();
+
+ case 'V':
+ RTPrintf("%sr%s\n", RTBldCfgVersion(), RTBldCfgRevisionStr());
+ return 0;
+
+ case VINF_GETOPT_NOT_OPTION:
+ RTStrmPrintf(g_pStdErr, "SUPLoggerCtl: error: Unexpected argument '%s'.\n", Val.psz);
+ return 1;
+
+ default:
+ return RTGetOptPrintError(ch, &Val);
+ }
+ }
+
+ /*
+ * Make sure the support library is initialized.
+ */
+ int rc = SUPR3Init(NULL /*ppSession*/);
+ if (RT_SUCCESS(rc))
+ {
+ /*
+ * Do the requested job.
+ */
+ switch (enmWhat)
+ {
+ case kSupLoggerCtl_Set:
+ rc = SUPR3LoggerSettings(enmWhich, pszFlags, pszGroups, pszDest);
+ break;
+ case kSupLoggerCtl_Create:
+ rc = SUPR3LoggerCreate(enmWhich, pszFlags, pszGroups, pszDest);
+ break;
+ case kSupLoggerCtl_Destroy:
+ rc = SUPR3LoggerDestroy(enmWhich);
+ break;
+ default:
+ rc = VERR_INTERNAL_ERROR;
+ break;
+ }
+ if (RT_SUCCESS(rc))
+ RTPrintf("SUPLoggerCtl: Success\n");
+ else
+ RTStrmPrintf(g_pStdErr, "SUPLoggerCtl: error: rc=%Rrc\n", rc);
+ }
+ else
+ RTStrmPrintf(g_pStdErr, "SUPR3Init: error: rc=%Rrc\n", rc);
+
+ return RT_SUCCESS(rc) ? 0 : 1;
+}
+
diff --git a/src/VBox/HostDrivers/Support/testcase/SUPUninstall.cpp b/src/VBox/HostDrivers/Support/testcase/SUPUninstall.cpp
new file mode 100644
index 00000000..64b47795
--- /dev/null
+++ b/src/VBox/HostDrivers/Support/testcase/SUPUninstall.cpp
@@ -0,0 +1,63 @@
+/* $Id: SUPUninstall.cpp $ */
+/** @file
+ * SUPUninstall - Driver Uninstall.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include <VBox/sup.h>
+#include <iprt/errcore.h>
+#include <iprt/initterm.h>
+#include <iprt/stream.h>
+#include <iprt/message.h>
+
+
+int main(int argc, char **argv)
+{
+ RTR3InitExeNoArguments(0);
+ if (argc != 1)
+ return RTMsgErrorExit(RTEXITCODE_SYNTAX, "This utility takes no arguments\n");
+ NOREF(argv);
+
+ int rc = SUPR3Uninstall();
+ if (RT_SUCCESS(rc))
+ {
+ RTMsgInfo("uninstalled successfully");
+ return RTEXITCODE_SUCCESS;
+ }
+ return RTMsgErrorExit(RTEXITCODE_FAILURE, "uninstallation failed. rc=%Rrc", rc);
+}
+
diff --git a/src/VBox/HostDrivers/Support/testcase/tstContiguous.cpp b/src/VBox/HostDrivers/Support/testcase/tstContiguous.cpp
new file mode 100644
index 00000000..6f1e8290
--- /dev/null
+++ b/src/VBox/HostDrivers/Support/testcase/tstContiguous.cpp
@@ -0,0 +1,118 @@
+/* $Id: tstContiguous.cpp $ */
+/** @file
+ * SUP Testcase - Contiguous Memory Interface (ring-3).
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include <VBox/sup.h>
+#include <VBox/param.h>
+#include <iprt/initterm.h>
+#include <iprt/stream.h>
+#include <stdlib.h>
+#include <string.h>
+
+
+int main(int argc, char **argv)
+{
+ int rc;
+ int rcRet = 0;
+
+ RTR3InitExe(argc, &argv, 0);
+ rc = SUPR3Init(NULL);
+ RTPrintf("tstContiguous: SUPR3Init -> rc=%Rrc\n", rc);
+ rcRet += rc != 0;
+ if (!rc)
+ {
+ /*
+ * Allocate a bit of contiguous memory.
+ */
+ RTHCPHYS HCPhys;
+ void *pv = SUPR3ContAlloc(8, NULL, &HCPhys);
+ rcRet += pv == NULL || HCPhys == 0;
+ if (pv && HCPhys)
+ {
+ memset(pv, 0xff, PAGE_SIZE * 8);
+ pv = SUPR3ContAlloc(5, NULL, &HCPhys);
+ rcRet += pv == NULL || HCPhys == 0;
+ if (pv && HCPhys)
+ {
+ memset(pv, 0x7f, PAGE_SIZE * 5);
+ rc = SUPR3ContFree(pv, 5);
+ rcRet += rc != 0;
+ if (rc)
+ RTPrintf("tstContiguous: SUPR3ContFree failed! rc=%Rrc\n", rc);
+
+ void *apv[128];
+ for (unsigned i = 0; i < RT_ELEMENTS(apv); i++)
+ {
+ apv[i] = SUPR3ContAlloc(1 + (i % 11), NULL, &HCPhys);
+ if (!apv[i])
+ {
+ RTPrintf("tstContiguous: i=%d: failed to allocate %d pages", i, 1 + (i % 11));
+#if defined(RT_ARCH_X86) && defined(RT_OS_LINUX)
+ /* With 32-bit address spaces it's sometimes difficult
+ * to find bigger chunks of contiguous memory */
+ if (i % 11 > 7)
+ RTPrintf(" => ignoring (32-bit host)");
+ else
+#endif
+ rcRet++;
+ RTPrintf("\n");
+ }
+ }
+ for (unsigned i = 0; i < RT_ELEMENTS(apv); i++)
+ if (apv[i])
+ {
+ rc = SUPR3ContFree(apv[i], 1 + (i % 11));
+ rcRet += rc != 0;
+ if (rc)
+ RTPrintf("tstContiguous: i=%d SUPR3ContFree failed! rc=%Rrc\n", i, rc);
+ }
+ }
+ else
+ RTPrintf("tstContiguous: SUPR3ContAlloc (2nd) failed!\n");
+ }
+ else
+ RTPrintf("tstContiguous: SUPR3ContAlloc failed!\n");
+
+ rc = SUPR3Term(false /*fForced*/);
+ RTPrintf("tstContiguous: SUPR3Term -> rc=%Rrc\n", rc);
+ rcRet += rc != 0;
+ }
+
+ return rcRet ? 1 : 0;
+}
diff --git a/src/VBox/HostDrivers/Support/testcase/tstGIP-2.cpp b/src/VBox/HostDrivers/Support/testcase/tstGIP-2.cpp
new file mode 100644
index 00000000..a183ca27
--- /dev/null
+++ b/src/VBox/HostDrivers/Support/testcase/tstGIP-2.cpp
@@ -0,0 +1,356 @@
+/* $Id: tstGIP-2.cpp $ */
+/** @file
+ * SUP Testcase - Global Info Page interface (ring 3).
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include <VBox/sup.h>
+#include <iprt/errcore.h>
+#include <VBox/param.h>
+#include <iprt/asm.h>
+#include <iprt/assert.h>
+#include <iprt/alloc.h>
+#include <iprt/thread.h>
+#include <iprt/stream.h>
+#include <iprt/string.h>
+#include <iprt/initterm.h>
+#include <iprt/getopt.h>
+#include <iprt/x86.h>
+
+
+/**
+ * Entry point.
+ */
+extern "C" DECLEXPORT(int) TrustedMain(int argc, char **argv)
+{
+ RTR3InitExe(argc, &argv, 0);
+
+ /*
+ * Parse args
+ */
+ static const RTGETOPTDEF g_aOptions[] =
+ {
+ { "--iterations", 'i', RTGETOPT_REQ_INT32 },
+ { "--hex", 'h', RTGETOPT_REQ_NOTHING },
+ { "--decimal", 'd', RTGETOPT_REQ_NOTHING },
+ { "--spin", 's', RTGETOPT_REQ_NOTHING },
+ { "--reference", 'r', RTGETOPT_REQ_UINT64 }, /* reference value of CpuHz, display the
+ * CpuHz deviation in a separate column. */
+ { "--notestmode", 't', RTGETOPT_REQ_NOTHING } /* don't run GIP in test-mode (atm, test-mode
+ * implies updating GIP CpuHz even when invariant) */
+ };
+
+ bool fHex = true;
+ bool fSpin = false;
+ bool fCompat = true;
+ bool fTestMode = true;
+ int ch;
+ uint32_t cIterations = 40;
+ uint64_t uCpuHzRef = UINT64_MAX;
+ RTGETOPTUNION ValueUnion;
+ RTGETOPTSTATE GetState;
+ RTGetOptInit(&GetState, argc, argv, g_aOptions, RT_ELEMENTS(g_aOptions), 1, RTGETOPTINIT_FLAGS_NO_STD_OPTS);
+ while ((ch = RTGetOpt(&GetState, &ValueUnion)))
+ {
+ switch (ch)
+ {
+ case 'i':
+ cIterations = ValueUnion.u32;
+ break;
+
+ case 'd':
+ fHex = false;
+ break;
+
+ case 'h':
+ fHex = true;
+ break;
+
+ case 's':
+ fSpin = true;
+ break;
+
+ case 'r':
+ uCpuHzRef = ValueUnion.u64;
+ break;
+
+ case 't':
+ fTestMode = false;
+ break;
+
+ default:
+ return RTGetOptPrintError(ch, &ValueUnion);
+ }
+ }
+
+ /*
+ * Init
+ */
+ PSUPDRVSESSION pSession = NIL_RTR0PTR;
+ int rc = SUPR3Init(&pSession);
+ if (RT_SUCCESS(rc))
+ {
+ if (g_pSUPGlobalInfoPage)
+ {
+ uint64_t uCpuHzOverallDeviation = 0;
+ uint32_t cCpuHzNotCompat = 0;
+ int64_t iCpuHzMaxDeviation = 0;
+ int32_t cCpuHzOverallDevCnt = 0;
+ uint32_t cCpuHzChecked = 0;
+
+ /* Pick current CpuHz as the reference if none was specified. */
+ if (uCpuHzRef == UINT64_MAX)
+ uCpuHzRef = SUPGetCpuHzFromGip(g_pSUPGlobalInfoPage);
+
+ if ( fTestMode
+ && g_pSUPGlobalInfoPage->u32Mode == SUPGIPMODE_INVARIANT_TSC)
+ SUPR3GipSetFlags(SUPGIP_FLAGS_TESTING_ENABLE, UINT32_MAX);
+
+ RTPrintf("tstGIP-2: u32Mode=%d (%s) fTestMode=%RTbool u32Version=%#x fGetGipCpu=%#RX32 cPages=%#RX32\n",
+ g_pSUPGlobalInfoPage->u32Mode,
+ SUPGetGIPModeName(g_pSUPGlobalInfoPage),
+ fTestMode,
+ g_pSUPGlobalInfoPage->u32Version,
+ g_pSUPGlobalInfoPage->fGetGipCpu,
+ g_pSUPGlobalInfoPage->cPages);
+ RTPrintf("tstGIP-2: cCpus=%d cPossibleCpus=%d cPossibleCpuGroups=%d cPresentCpus=%d cOnlineCpus=%d idCpuMax=%#x\n",
+ g_pSUPGlobalInfoPage->cCpus,
+ g_pSUPGlobalInfoPage->cPossibleCpus,
+ g_pSUPGlobalInfoPage->cPossibleCpuGroups,
+ g_pSUPGlobalInfoPage->cPresentCpus,
+ g_pSUPGlobalInfoPage->cOnlineCpus,
+ g_pSUPGlobalInfoPage->idCpuMax);
+ RTPrintf("tstGIP-2: u32UpdateHz=%RU32 u32UpdateIntervalNS=%RU32 u64NanoTSLastUpdateHz=%RX64 u64CpuHz=%RU64 uCpuHzRef=%RU64\n",
+ g_pSUPGlobalInfoPage->u32UpdateHz,
+ g_pSUPGlobalInfoPage->u32UpdateIntervalNS,
+ g_pSUPGlobalInfoPage->u64NanoTSLastUpdateHz,
+ g_pSUPGlobalInfoPage->u64CpuHz,
+ uCpuHzRef);
+ for (uint32_t iCpu = 0; iCpu < g_pSUPGlobalInfoPage->cCpus; iCpu++)
+ if (g_pSUPGlobalInfoPage->aCPUs[iCpu].enmState != SUPGIPCPUSTATE_INVALID)
+ {
+ SUPGIPCPU const *pGipCpu = &g_pSUPGlobalInfoPage->aCPUs[iCpu];
+ RTPrintf("tstGIP-2: aCPU[%3u]: enmState=%d iCpuSet=%-3u idCpu=%#010x iCpuGroup=%-2u iCpuGroupMember=%-3u idApic=%#06x\n",
+ iCpu, pGipCpu->enmState, pGipCpu->iCpuSet, pGipCpu->idCpu, pGipCpu->iCpuGroup,
+ pGipCpu->iCpuGroupMember, pGipCpu->idApic);
+ }
+
+ RTPrintf(fHex
+ ? "tstGIP-2: it: u64NanoTS delta u64TSC UpIntTSC H TransId CpuHz %sTSC Interval History...\n"
+ : "tstGIP-2: it: u64NanoTS delta u64TSC UpIntTSC H TransId CpuHz %sTSC Interval History...\n",
+ uCpuHzRef ? " CpuHz deviation Compat " : "");
+ static SUPGIPCPU s_aaCPUs[2][RTCPUSET_MAX_CPUS];
+ for (uint32_t i = 0; i < cIterations; i++)
+ {
+ /* Copy the data. */
+ memcpy(&s_aaCPUs[i & 1][0], &g_pSUPGlobalInfoPage->aCPUs[0], g_pSUPGlobalInfoPage->cCpus * sizeof(g_pSUPGlobalInfoPage->aCPUs[0]));
+
+ /* Display it & find something to spin on. */
+ uint32_t u32TransactionId = 0;
+ uint32_t volatile *pu32TransactionId = NULL;
+ for (unsigned iCpu = 0; iCpu < g_pSUPGlobalInfoPage->cCpus; iCpu++)
+ if (g_pSUPGlobalInfoPage->aCPUs[iCpu].enmState == SUPGIPCPUSTATE_ONLINE)
+ {
+ char szCpuHzDeviation[32];
+ PSUPGIPCPU pPrevCpu = &s_aaCPUs[!(i & 1)][iCpu];
+ PSUPGIPCPU pCpu = &s_aaCPUs[i & 1][iCpu];
+ if (uCpuHzRef)
+ {
+ /* Only CPU 0 is updated for invariant & sync modes, see supdrvGipUpdate(). */
+ if ( iCpu == 0
+ || g_pSUPGlobalInfoPage->u32Mode == SUPGIPMODE_ASYNC_TSC)
+ {
+ /* Wait until the history validation code takes effect. */
+ if (pCpu->u32TransactionId > 23 + (8 * 2) + 1)
+ {
+ int64_t iCpuHzDeviation = pCpu->u64CpuHz - uCpuHzRef;
+ uint64_t uCpuHzDeviation = RT_ABS(iCpuHzDeviation);
+ bool fCurHzCompat = SUPIsTscFreqCompatibleEx(uCpuHzRef, pCpu->u64CpuHz, false /*fRelax*/);
+ if (uCpuHzDeviation <= 999999999)
+ {
+ if (RT_ABS(iCpuHzDeviation) > RT_ABS(iCpuHzMaxDeviation))
+ iCpuHzMaxDeviation = iCpuHzDeviation;
+ uCpuHzOverallDeviation += uCpuHzDeviation;
+ cCpuHzOverallDevCnt++;
+ uint32_t uPct = (uint32_t)(uCpuHzDeviation * 100000 / uCpuHzRef + 5);
+ RTStrPrintf(szCpuHzDeviation, sizeof(szCpuHzDeviation), "%10RI64%3d.%02d%% %RTbool ",
+ iCpuHzDeviation, uPct / 1000, (uPct % 1000) / 10, fCurHzCompat);
+ }
+ else
+ {
+ RTStrPrintf(szCpuHzDeviation, sizeof(szCpuHzDeviation), "%17s %RTbool ", "?",
+ fCurHzCompat);
+ }
+
+ if (!fCurHzCompat)
+ ++cCpuHzNotCompat;
+ fCompat &= fCurHzCompat;
+ ++cCpuHzChecked;
+ }
+ else
+ RTStrPrintf(szCpuHzDeviation, sizeof(szCpuHzDeviation), "%25s ", "priming");
+ }
+ else
+ RTStrPrintf(szCpuHzDeviation, sizeof(szCpuHzDeviation), "%25s ", "");
+ }
+ else
+ szCpuHzDeviation[0] = '\0';
+ RTPrintf(fHex
+ ? "tstGIP-2: %4d/%d: %016llx %09llx %016llx %08x %d %08x %15llu %s%08x %08x %08x %08x %08x %08x %08x %08x (%d)\n"
+ : "tstGIP-2: %4d/%d: %016llu %09llu %016llu %010u %d %010u %15llu %s%08x %08x %08x %08x %08x %08x %08x %08x (%d)\n",
+ i, iCpu,
+ pCpu->u64NanoTS,
+ i ? pCpu->u64NanoTS - pPrevCpu->u64NanoTS : 0,
+ pCpu->u64TSC,
+ pCpu->u32UpdateIntervalTSC,
+ pCpu->iTSCHistoryHead,
+ pCpu->u32TransactionId,
+ pCpu->u64CpuHz,
+ szCpuHzDeviation,
+ pCpu->au32TSCHistory[0],
+ pCpu->au32TSCHistory[1],
+ pCpu->au32TSCHistory[2],
+ pCpu->au32TSCHistory[3],
+ pCpu->au32TSCHistory[4],
+ pCpu->au32TSCHistory[5],
+ pCpu->au32TSCHistory[6],
+ pCpu->au32TSCHistory[7],
+ pCpu->cErrors);
+ if (!pu32TransactionId)
+ {
+ pu32TransactionId = &g_pSUPGlobalInfoPage->aCPUs[iCpu].u32TransactionId;
+ u32TransactionId = pCpu->u32TransactionId;
+ }
+ }
+
+ /* Wait a bit / spin. */
+ if (!fSpin)
+ RTThreadSleep(9);
+ else
+ {
+ if (pu32TransactionId)
+ {
+ uint32_t uTmp;
+ while ( u32TransactionId == (uTmp = *pu32TransactionId)
+ || (uTmp & 1))
+ ASMNopPause();
+ }
+ else
+ RTThreadSleep(1);
+ }
+ }
+
+ /*
+ * Display TSC deltas.
+ *
+ * First iterative over the APIC ID array to get mostly consistent CPUID to APIC ID mapping.
+ * Then iterate over the offline CPUs. It is possible that there's a race between the online/offline
+ * states between the two iterations, but that cannot be helped from ring-3 anyway and not a biggie.
+ */
+ RTPrintf("tstGIP-2: TSC deltas:\n");
+ RTPrintf("tstGIP-2: idApic: i64TSCDelta\n");
+ for (uint32_t i = 0; i < RT_ELEMENTS(g_pSUPGlobalInfoPage->aiCpuFromApicId); i++)
+ {
+ uint16_t iCpu = g_pSUPGlobalInfoPage->aiCpuFromApicId[i];
+ if (iCpu != UINT16_MAX)
+ RTPrintf("tstGIP-2: %#7x: %6lld (grp=%#04x mbr=%#05x set=%d cpu=%#05x)\n",
+ g_pSUPGlobalInfoPage->aCPUs[iCpu].idApic, g_pSUPGlobalInfoPage->aCPUs[iCpu].i64TSCDelta,
+ g_pSUPGlobalInfoPage->aCPUs[iCpu].iCpuGroup, g_pSUPGlobalInfoPage->aCPUs[iCpu].iCpuGroupMember,
+ g_pSUPGlobalInfoPage->aCPUs[iCpu].iCpuSet, iCpu);
+ }
+
+ for (uint32_t iCpu = 0; iCpu < g_pSUPGlobalInfoPage->cCpus; iCpu++)
+ if (g_pSUPGlobalInfoPage->aCPUs[iCpu].idApic == UINT16_MAX)
+ RTPrintf("tstGIP-2: offline: %6lld (grp=%#04x mbr=%#05x set=%d cpu=%#05x)\n",
+ g_pSUPGlobalInfoPage->aCPUs[iCpu].i64TSCDelta, g_pSUPGlobalInfoPage->aCPUs[iCpu].iCpuGroup,
+ g_pSUPGlobalInfoPage->aCPUs[iCpu].iCpuGroupMember, g_pSUPGlobalInfoPage->aCPUs[iCpu].iCpuSet, iCpu);
+
+ RTPrintf("tstGIP-2: enmUseTscDelta=%d fGetGipCpu=%#x\n",
+ g_pSUPGlobalInfoPage->enmUseTscDelta, g_pSUPGlobalInfoPage->fGetGipCpu);
+ if (uCpuHzRef)
+ {
+ if (cCpuHzOverallDevCnt)
+ {
+ uint32_t uPct = (uint32_t)(uCpuHzOverallDeviation * 100000 / cCpuHzOverallDevCnt / uCpuHzRef + 5);
+ RTPrintf("tstGIP-2: Average CpuHz deviation: %d.%02d%%\n",
+ uPct / 1000, (uPct % 1000) / 10);
+
+ uint32_t uMaxPct = (uint32_t)(RT_ABS(iCpuHzMaxDeviation) * 100000 / uCpuHzRef + 5);
+ RTPrintf("tstGIP-2: Maximum CpuHz deviation: %d.%02d%% (%RI64 ticks)\n",
+ uMaxPct / 1000, (uMaxPct % 1000) / 10, iCpuHzMaxDeviation);
+ }
+ else
+ {
+ RTPrintf("tstGIP-2: Average CpuHz deviation: ??.??\n");
+ RTPrintf("tstGIP-2: Average CpuHz deviation: ??.??\n");
+ }
+
+ RTPrintf("tstGIP-2: CpuHz compatibility: %RTbool (incompatible %u of %u times w/ %RU64 Hz - %s GIP)\n", fCompat,
+ cCpuHzNotCompat, cCpuHzChecked, uCpuHzRef, SUPGetGIPModeName(g_pSUPGlobalInfoPage));
+
+ if ( !fCompat
+ && g_pSUPGlobalInfoPage->u32Mode == SUPGIPMODE_INVARIANT_TSC)
+ rc = -1;
+ }
+
+ /* Disable GIP test mode. */
+ if (fTestMode)
+ SUPR3GipSetFlags(0, ~SUPGIP_FLAGS_TESTING_ENABLE);
+ }
+ else
+ {
+ RTPrintf("tstGIP-2: g_pSUPGlobalInfoPage is NULL\n");
+ rc = -1;
+ }
+
+ SUPR3Term(false /*fForced*/);
+ }
+ else
+ RTPrintf("tstGIP-2: SUPR3Init failed: %Rrc\n", rc);
+ return !!rc;
+}
+
+#if !defined(VBOX_WITH_HARDENING) || !defined(RT_OS_WINDOWS)
+/**
+ * Main entry point.
+ */
+int main(int argc, char **argv)
+{
+ return TrustedMain(argc, argv);
+}
+#endif
+
diff --git a/src/VBox/HostDrivers/Support/testcase/tstGetPagingMode.cpp b/src/VBox/HostDrivers/Support/testcase/tstGetPagingMode.cpp
new file mode 100644
index 00000000..c3d0a5ef
--- /dev/null
+++ b/src/VBox/HostDrivers/Support/testcase/tstGetPagingMode.cpp
@@ -0,0 +1,104 @@
+/* $Id: tstGetPagingMode.cpp $ */
+/** @file
+ * SUP Testcase - Host paging mode interface (ring 3).
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include <VBox/sup.h>
+#include <iprt/errcore.h>
+#include <iprt/initterm.h>
+#include <iprt/stream.h>
+
+
+int main(int argc, char **argv)
+{
+ int rc;
+ RTR3InitExe(argc, &argv, 0);
+ rc = SUPR3Init(NULL);
+ if (RT_SUCCESS(rc))
+ {
+ SUPPAGINGMODE enmMode = SUPR3GetPagingMode();
+ switch (enmMode)
+ {
+ case SUPPAGINGMODE_INVALID:
+ RTPrintf("SUPPAGINGMODE_INVALID\n");
+ break;
+ case SUPPAGINGMODE_32_BIT:
+ RTPrintf("SUPPAGINGMODE_32_BIT\n");
+ break;
+ case SUPPAGINGMODE_32_BIT_GLOBAL:
+ RTPrintf("SUPPAGINGMODE_32_BIT_GLOBAL\n");
+ break;
+ case SUPPAGINGMODE_PAE:
+ RTPrintf("SUPPAGINGMODE_PAE\n");
+ break;
+ case SUPPAGINGMODE_PAE_GLOBAL:
+ RTPrintf("SUPPAGINGMODE_PAE_GLOBAL\n");
+ break;
+ case SUPPAGINGMODE_PAE_NX:
+ RTPrintf("SUPPAGINGMODE_PAE_NX\n");
+ break;
+ case SUPPAGINGMODE_PAE_GLOBAL_NX:
+ RTPrintf("SUPPAGINGMODE_PAE_GLOBAL_NX\n");
+ break;
+ case SUPPAGINGMODE_AMD64:
+ RTPrintf("SUPPAGINGMODE_AMD64\n");
+ break;
+ case SUPPAGINGMODE_AMD64_GLOBAL:
+ RTPrintf("SUPPAGINGMODE_AMD64_GLOBAL\n");
+ break;
+ case SUPPAGINGMODE_AMD64_NX:
+ RTPrintf("SUPPAGINGMODE_AMD64_NX\n");
+ break;
+ case SUPPAGINGMODE_AMD64_GLOBAL_NX:
+ RTPrintf("SUPPAGINGMODE_AMD64_GLOBAL_NX\n");
+ break;
+ default:
+ RTPrintf("Unknown mode %d\n", enmMode);
+ rc = VERR_INTERNAL_ERROR;
+ break;
+ }
+
+ int rc2 = SUPR3Term(false /*fForced*/);
+ RTPrintf("SUPR3Term -> rc=%Rrc\n", rc2);
+ }
+ else
+ RTPrintf("SUPR3Init -> rc=%Rrc\n", rc);
+
+ return !RT_SUCCESS(rc);
+}
+
diff --git a/src/VBox/HostDrivers/Support/testcase/tstInit.cpp b/src/VBox/HostDrivers/Support/testcase/tstInit.cpp
new file mode 100644
index 00000000..1a2a78f5
--- /dev/null
+++ b/src/VBox/HostDrivers/Support/testcase/tstInit.cpp
@@ -0,0 +1,61 @@
+/* $Id: tstInit.cpp $ */
+/** @file
+ * SUP Testcase - Support Library initialization and termination.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include <VBox/sup.h>
+#include <iprt/errcore.h>
+#include <iprt/initterm.h>
+#include <iprt/stream.h>
+
+
+int main(int argc, char **argv)
+{
+ int rc;
+ RTR3InitExe(argc, &argv, 0);
+ rc = SUPR3Init(NULL);
+ RTPrintf("tstInit: SUPR3Init -> rc=%Rrc\n", rc);
+ if (!rc)
+ {
+ rc = SUPR3Term(false /*fForced*/);
+ RTPrintf("tstInit: SUPR3Term -> rc=%Rrc\n", rc);
+ }
+
+ return rc;
+}
+
diff --git a/src/VBox/HostDrivers/Support/testcase/tstInt.cpp b/src/VBox/HostDrivers/Support/testcase/tstInt.cpp
new file mode 100644
index 00000000..094d5a3d
--- /dev/null
+++ b/src/VBox/HostDrivers/Support/testcase/tstInt.cpp
@@ -0,0 +1,240 @@
+/* $Id: tstInt.cpp $ */
+/** @file
+ * SUP Testcase - Test the interrupt gate feature of the support library.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include <VBox/sup.h>
+#include <VBox/vmm/vmm.h>
+#include <VBox/vmm/gvmm.h>
+#include <VBox/vmm/vm.h>
+#include <iprt/errcore.h>
+#include <VBox/param.h>
+#if defined(RT_ARCH_AMD64) || defined(RT_ARCH_X86)
+# include <iprt/asm-amd64-x86.h>
+#else
+# define ASMReadTSC RTTimeSystemNanoTS
+#endif
+#include <iprt/initterm.h>
+#include <iprt/stream.h>
+#include <iprt/string.h>
+#include <iprt/alloc.h>
+#include <iprt/time.h>
+#include <iprt/path.h>
+
+
+int main(int argc, char **argv)
+{
+ int rcRet = 0;
+ int i;
+ int rc;
+ int cIterations = argc > 1 ? RTStrToUInt32(argv[1]) : 32;
+ if (cIterations == 0)
+ cIterations = 64;
+
+ /*
+ * Init.
+ */
+ RTR3InitExe(argc, &argv, 0);
+ PSUPDRVSESSION pSession;
+ rc = SUPR3Init(&pSession);
+ rcRet += rc != 0;
+ RTPrintf("tstInt: SUPR3Init -> rc=%Rrc\n", rc);
+ char szFile[RTPATH_MAX];
+ if (!rc)
+ {
+ rc = RTPathExecDir(szFile, sizeof(szFile) - sizeof("/VMMR0.r0"));
+ }
+ char szAbsFile[RTPATH_MAX];
+ if (RT_SUCCESS(rc))
+ {
+ strcat(szFile, "/VMMR0.r0");
+ rc = RTPathAbs(szFile, szAbsFile, sizeof(szAbsFile));
+ }
+ if (RT_SUCCESS(rc))
+ {
+ /*
+ * Load VMM code.
+ */
+ RTERRINFOSTATIC ErrInfo;
+ rc = SUPR3LoadVMM(szAbsFile, RTErrInfoInitStatic(&ErrInfo));
+ if (RT_SUCCESS(rc))
+ {
+ /*
+ * Create a tiny dummy VM so we can do NOP calls into it using the fast I/O control path.
+ */
+ GVMMCREATEVMREQ CreateVMReq;
+ CreateVMReq.Hdr.u32Magic = SUPVMMR0REQHDR_MAGIC;
+ CreateVMReq.Hdr.cbReq = sizeof(CreateVMReq);
+ CreateVMReq.pSession = pSession;
+ CreateVMReq.pVMR0 = NIL_RTR0PTR;
+ CreateVMReq.pVMR3 = NULL;
+ CreateVMReq.cCpus = 1;
+ rc = SUPR3CallVMMR0Ex(NIL_RTR0PTR, NIL_VMCPUID, VMMR0_DO_GVMM_CREATE_VM, 0, &CreateVMReq.Hdr);
+ if (RT_SUCCESS(rc))
+ {
+ PVM pVM = CreateVMReq.pVMR3;
+ AssertRelease(RT_VALID_PTR(pVM));
+ AssertRelease(pVM->pVMR0ForCall == CreateVMReq.pVMR0);
+ AssertRelease(pVM->pSession == pSession);
+ AssertRelease(pVM->cCpus == 1);
+ pVM->enmVMState = VMSTATE_CREATED;
+ PVMR0 const pVMR0 = CreateVMReq.pVMR0;
+
+ rc = SUPR3SetVMForFastIOCtl(pVMR0);
+ if (!rc)
+ {
+ /*
+ * Call VMM code with invalid function.
+ */
+ for (i = cIterations; i > 0; i--)
+ {
+ rc = SUPR3CallVMMR0(pVMR0, NIL_VMCPUID, VMMR0_DO_SLOW_NOP, NULL);
+ if (rc != VINF_SUCCESS)
+ {
+ RTPrintf("tstInt: SUPR3CallVMMR0 -> rc=%Rrc i=%d Expected VINF_SUCCESS!\n", rc, i);
+ rcRet++;
+ break;
+ }
+ }
+ RTPrintf("tstInt: Performed SUPR3CallVMMR0 %d times (rc=%Rrc)\n", cIterations, rc);
+
+ /*
+ * The fast path.
+ */
+ if (rc == VINF_SUCCESS)
+ {
+ RTTimeNanoTS();
+ uint64_t StartTS = RTTimeNanoTS();
+ uint64_t StartTick = ASMReadTSC();
+ uint64_t MinTicks = UINT64_MAX;
+ for (i = 0; i < 1000000; i++)
+ {
+ uint64_t OneStartTick = ASMReadTSC();
+ rc = SUPR3CallVMMR0Fast(pVMR0, VMMR0_DO_NOP, 0);
+ uint64_t Ticks = ASMReadTSC() - OneStartTick;
+ if (Ticks < MinTicks)
+ MinTicks = Ticks;
+
+ if (RT_UNLIKELY(rc != VINF_SUCCESS))
+ {
+ RTPrintf("tstInt: SUPR3CallVMMR0Fast -> rc=%Rrc i=%d Expected VINF_SUCCESS!\n", rc, i);
+ rcRet++;
+ break;
+ }
+ }
+ uint64_t Ticks = ASMReadTSC() - StartTick;
+ uint64_t NanoSecs = RTTimeNanoTS() - StartTS;
+
+ RTPrintf("tstInt: SUPR3CallVMMR0Fast - %d iterations in %llu ns / %llu ticks. %llu ns / %#llu ticks per iteration. Min %llu ticks.\n",
+ i, NanoSecs, Ticks, NanoSecs / i, Ticks / i, MinTicks);
+
+ /*
+ * The ordinary path.
+ */
+ RTTimeNanoTS();
+ StartTS = RTTimeNanoTS();
+ StartTick = ASMReadTSC();
+ MinTicks = UINT64_MAX;
+ for (i = 0; i < 1000000; i++)
+ {
+ uint64_t OneStartTick = ASMReadTSC();
+ rc = SUPR3CallVMMR0Ex(pVMR0, NIL_VMCPUID, VMMR0_DO_SLOW_NOP, 0, NULL);
+ uint64_t OneTicks = ASMReadTSC() - OneStartTick;
+ if (OneTicks < MinTicks)
+ MinTicks = OneTicks;
+
+ if (RT_UNLIKELY(rc != VINF_SUCCESS))
+ {
+ RTPrintf("tstInt: SUPR3CallVMMR0Ex -> rc=%Rrc i=%d Expected VINF_SUCCESS!\n", rc, i);
+ rcRet++;
+ break;
+ }
+ }
+ Ticks = ASMReadTSC() - StartTick;
+ NanoSecs = RTTimeNanoTS() - StartTS;
+
+ RTPrintf("tstInt: SUPR3CallVMMR0Ex - %d iterations in %llu ns / %llu ticks. %llu ns / %#llu ticks per iteration. Min %llu ticks.\n",
+ i, NanoSecs, Ticks, NanoSecs / i, Ticks / i, MinTicks);
+ }
+ }
+ else
+ {
+ RTPrintf("tstInt: SUPR3SetVMForFastIOCtl failed: %Rrc\n", rc);
+ rcRet++;
+ }
+
+ rc = SUPR3CallVMMR0Ex(pVMR0, 0 /*idCpu*/, VMMR0_DO_GVMM_DESTROY_VM, 0, NULL);
+ if (RT_FAILURE(rc))
+ {
+ RTPrintf("tstInt: VMMR0_DO_GVMM_DESTROY_VM failed: %Rrc\n", rc);
+ rcRet++;
+ }
+ }
+ else
+ {
+ RTPrintf("tstInt: VMMR0_DO_GVMM_CREATE_VM failed\n");
+ rcRet++;
+ }
+
+ /*
+ * Unload VMM.
+ */
+ rc = SUPR3UnloadVMM();
+ if (rc)
+ {
+ RTPrintf("tstInt: SUPR3UnloadVMM failed with rc=%Rrc\n", rc);
+ rcRet++;
+ }
+ }
+ else
+ {
+ RTPrintf("tstInt: SUPR3LoadVMM failed with rc=%Rrc%#RTeim\n", rc, &ErrInfo.Core);
+ rcRet++;
+ }
+
+ /*
+ * Terminate.
+ */
+ rc = SUPR3Term(false /*fForced*/);
+ rcRet += rc != 0;
+ RTPrintf("tstInt: SUPR3Term -> rc=%Rrc\n", rc);
+ }
+
+ return !!rc;
+}
+
diff --git a/src/VBox/HostDrivers/Support/testcase/tstLow.cpp b/src/VBox/HostDrivers/Support/testcase/tstLow.cpp
new file mode 100644
index 00000000..032accdb
--- /dev/null
+++ b/src/VBox/HostDrivers/Support/testcase/tstLow.cpp
@@ -0,0 +1,164 @@
+/* $Id: tstLow.cpp $ */
+/** @file
+ * SUP Testcase - Low (<4GB) Memory Allocate interface (ring 3).
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include <VBox/sup.h>
+#include <VBox/param.h>
+#include <iprt/errcore.h>
+#include <iprt/initterm.h>
+#include <iprt/stream.h>
+#include <iprt/string.h>
+
+
+int main(int argc, char **argv)
+{
+ int rc;
+ int rcRet = 0;
+
+ RTR3InitExe(argc, &argv, 0);
+ RTPrintf("tstLow: TESTING...\n");
+
+ rc = SUPR3Init(NULL);
+ if (RT_SUCCESS(rc))
+ {
+ /*
+ * Allocate a bit of contiguous memory.
+ */
+ SUPPAGE aPages0[128];
+ void *pvPages0 = (void *)0x77777777;
+ memset(&aPages0[0], 0x8f, sizeof(aPages0));
+ rc = SUPR3LowAlloc(RT_ELEMENTS(aPages0), &pvPages0, NULL, aPages0);
+ if (RT_SUCCESS(rc))
+ {
+ /* check that the pages are below 4GB and valid. */
+ for (unsigned iPage = 0; iPage < RT_ELEMENTS(aPages0); iPage++)
+ {
+ RTPrintf("%-4d: Phys=%RHp Reserved=%p\n", iPage, aPages0[iPage].Phys, aPages0[iPage].uReserved);
+ if (aPages0[iPage].uReserved != 0)
+ {
+ rcRet++;
+ RTPrintf("tstLow: error: aPages0[%d].uReserved=%#x expected 0!\n", iPage, aPages0[iPage].uReserved);
+ }
+ if ( aPages0[iPage].Phys >= _4G
+ || (aPages0[iPage].Phys & PAGE_OFFSET_MASK))
+ {
+ rcRet++;
+ RTPrintf("tstLow: error: aPages0[%d].Phys=%RHp!\n", iPage, aPages0[iPage].Phys);
+ }
+ }
+ if (!rcRet)
+ {
+ for (unsigned iPage = 0; iPage < RT_ELEMENTS(aPages0); iPage++)
+ memset((char *)pvPages0 + iPage * PAGE_SIZE, iPage, PAGE_SIZE);
+ for (unsigned iPage = 0; iPage < RT_ELEMENTS(aPages0); iPage++)
+ for (uint8_t *pu8 = (uint8_t *)pvPages0 + iPage * PAGE_SIZE, *pu8End = pu8 + PAGE_SIZE; pu8 < pu8End; pu8++)
+ if (*pu8 != (uint8_t)iPage)
+ {
+ RTPrintf("tstLow: error: invalid page content %02x != %02x. iPage=%u off=%#x\n",
+ *pu8, (uint8_t)iPage, iPage, (uintptr_t)pu8 & PAGE_OFFSET_MASK);
+ rcRet++;
+ }
+ }
+ SUPR3LowFree(pvPages0, RT_ELEMENTS(aPages0));
+ }
+ else
+ {
+ RTPrintf("SUPR3LowAlloc(%d,,) failed -> rc=%Rrc\n", RT_ELEMENTS(aPages0), rc);
+ rcRet++;
+ }
+
+ /*
+ * Allocate odd amounts in from 1 to 127.
+ */
+ for (unsigned cPages = 1; cPages <= 127; cPages++)
+ {
+ SUPPAGE aPages1[128];
+ void *pvPages1 = (void *)0x77777777;
+ memset(&aPages1[0], 0x8f, sizeof(aPages1));
+ rc = SUPR3LowAlloc(cPages, &pvPages1, NULL, aPages1);
+ if (RT_SUCCESS(rc))
+ {
+ /* check that the pages are below 4GB and valid. */
+ for (unsigned iPage = 0; iPage < cPages; iPage++)
+ {
+ RTPrintf("%-4d::%-4d: Phys=%RHp Reserved=%p\n", cPages, iPage, aPages1[iPage].Phys, aPages1[iPage].uReserved);
+ if (aPages1[iPage].uReserved != 0)
+ {
+ rcRet++;
+ RTPrintf("tstLow: error: aPages1[%d].uReserved=%#x expected 0!\n", iPage, aPages1[iPage].uReserved);
+ }
+ if ( aPages1[iPage].Phys >= _4G
+ || (aPages1[iPage].Phys & PAGE_OFFSET_MASK))
+ {
+ rcRet++;
+ RTPrintf("tstLow: error: aPages1[%d].Phys=%RHp!\n", iPage, aPages1[iPage].Phys);
+ }
+ }
+ if (!rcRet)
+ {
+ for (unsigned iPage = 0; iPage < cPages; iPage++)
+ memset((char *)pvPages1 + iPage * PAGE_SIZE, iPage, PAGE_SIZE);
+ for (unsigned iPage = 0; iPage < cPages; iPage++)
+ for (uint8_t *pu8 = (uint8_t *)pvPages1 + iPage * PAGE_SIZE, *pu8End = pu8 + PAGE_SIZE; pu8 < pu8End; pu8++)
+ if (*pu8 != (uint8_t)iPage)
+ {
+ RTPrintf("tstLow: error: invalid page content %02x != %02x. iPage=%p off=%#x\n",
+ *pu8, (uint8_t)iPage, iPage, (uintptr_t)pu8 & PAGE_OFFSET_MASK);
+ rcRet++;
+ }
+ }
+ SUPR3LowFree(pvPages1, cPages);
+ }
+ else
+ {
+ RTPrintf("SUPR3LowAlloc(%d,,) failed -> rc=%Rrc\n", cPages, rc);
+ rcRet++;
+ }
+ }
+
+ }
+ else
+ {
+ RTPrintf("SUPR3Init -> rc=%Rrc\n", rc);
+ rcRet++;
+ }
+
+
+ return rcRet;
+}
diff --git a/src/VBox/HostDrivers/Support/testcase/tstNtQueryStuff.cpp b/src/VBox/HostDrivers/Support/testcase/tstNtQueryStuff.cpp
new file mode 100644
index 00000000..9f181afd
--- /dev/null
+++ b/src/VBox/HostDrivers/Support/testcase/tstNtQueryStuff.cpp
@@ -0,0 +1,440 @@
+/* $Id: tstNtQueryStuff.cpp $ */
+/** @file
+ * SUP Testcase - Exploring some NT Query APIs.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include <iprt/nt/nt-and-windows.h>
+#include <iprt/test.h>
+#include <iprt/string.h>
+
+
+/*********************************************************************************************************************************
+* Structures and Typedefs *
+*********************************************************************************************************************************/
+typedef struct FLAGDESC
+{
+ ULONG f;
+ const char *psz;
+} FLAGDESC;
+typedef const FLAGDESC *PCFLAGDESC;
+
+
+
+/*********************************************************************************************************************************
+* Global Variables *
+*********************************************************************************************************************************/
+static RTTEST g_hTest = NIL_RTTEST;
+static HANDLE g_hProcess = NULL;
+
+
+static char *stringifyAppend(char *pszBuf, size_t *pcbBuf, const char *pszAppend, bool fWithSpace)
+{
+ size_t cchAppend = strlen(pszAppend);
+ if (cchAppend + 1 + fWithSpace <= *pcbBuf)
+ {
+ if (fWithSpace)
+ {
+ *pszBuf++ = ' ';
+ *pcbBuf += 1;
+ }
+ memcpy(pszBuf, pszAppend, cchAppend + 1);
+ *pcbBuf -= cchAppend;
+ pszBuf += cchAppend;
+ }
+
+ return pszBuf;
+}
+
+
+static char *stringifyAppendUnknownFlags(uint32_t fFlags, char *pszBuf, size_t *pcbBuf, bool fWithSpace)
+{
+ for (unsigned iBit = 0; iBit < 32; iBit++)
+ if (fFlags & RT_BIT_32(iBit))
+ {
+ char szTmp[32]; /* lazy bird */
+ RTStrPrintf(szTmp, sizeof(szTmp), "BIT(%d)", iBit);
+ pszBuf = stringifyAppend(pszBuf, pcbBuf, szTmp, fWithSpace);
+ fWithSpace = true;
+ }
+
+ return pszBuf;
+}
+
+
+static char *stringifyFlags(uint32_t fFlags, char *pszBuf, size_t cbBuf, PCFLAGDESC paFlagDesc, size_t cFlagDesc)
+{
+ char *pszBufStart = pszBuf;
+ if (fFlags)
+ {
+ for (size_t i = 0; i < cFlagDesc; i++)
+ {
+ if (fFlags & paFlagDesc[i].f)
+ {
+ fFlags &= ~paFlagDesc[i].f;
+ pszBuf = stringifyAppend(pszBuf, &cbBuf, paFlagDesc[i].psz, pszBuf != pszBufStart);
+ }
+ }
+
+ if (fFlags)
+ stringifyAppendUnknownFlags(fFlags, pszBuf, &cbBuf, pszBuf != pszBufStart);
+ }
+ else
+ {
+ pszBuf[0] = '0';
+ pszBuf[1] = '\0';
+ }
+ return pszBufStart;
+}
+
+
+static char *stringifyMemType(uint32_t fType, char *pszBuf, size_t cbBuf)
+{
+ static const FLAGDESC s_aMemTypes[] =
+ {
+ { MEM_PRIVATE, "PRIVATE" },
+ { MEM_MAPPED, "MAPPED" },
+ { MEM_IMAGE, "IMAGE" },
+ };
+ return stringifyFlags(fType, pszBuf, cbBuf, s_aMemTypes, RT_ELEMENTS(s_aMemTypes));
+}
+
+
+static char *stringifyMemState(uint32_t fState, char *pszBuf, size_t cbBuf)
+{
+ static const FLAGDESC s_aMemStates[] =
+ {
+ { MEM_FREE, "FREE" },
+ { MEM_COMMIT, "COMMIT" },
+ { MEM_RESERVE, "RESERVE" },
+ { MEM_DECOMMIT, "DECOMMMIT" },
+ };
+ return stringifyFlags(fState, pszBuf, cbBuf, s_aMemStates, RT_ELEMENTS(s_aMemStates));
+}
+
+
+static char *stringifyMemProt(uint32_t fProt, char *pszBuf, size_t cbBuf)
+{
+ static const FLAGDESC s_aProtections[] =
+ {
+ { PAGE_NOACCESS, "NOACCESS" },
+ { PAGE_READONLY, "READONLY" },
+ { PAGE_READWRITE, "READWRITE" },
+ { PAGE_WRITECOPY, "WRITECOPY" },
+ { PAGE_EXECUTE, "EXECUTE" },
+ { PAGE_EXECUTE_READ, "EXECUTE_READ" },
+ { PAGE_EXECUTE_READWRITE, "EXECUTE_READWRITE" },
+ { PAGE_EXECUTE_WRITECOPY, "EXECUTE_WRITECOPY" },
+ { PAGE_GUARD, "GUARD" },
+ { PAGE_NOCACHE, "NOCACHE" },
+ { PAGE_WRITECOMBINE, "WRITECOMBINE" },
+
+ };
+ return stringifyFlags(fProt, pszBuf, cbBuf, s_aProtections, RT_ELEMENTS(s_aProtections));
+}
+
+
+
+static void tstQueryVirtualMemory(void)
+{
+ RTTestISub("NtQueryVirtualMemory");
+
+ uintptr_t cbAdvance = 0;
+ uintptr_t uPtrWhere = 0;
+ for (;;)
+ {
+ SIZE_T cbActual = 0;
+ MEMORY_BASIC_INFORMATION MemInfo = { 0, 0, 0, 0, 0, 0, 0 };
+ NTSTATUS rcNt = NtQueryVirtualMemory(g_hProcess,
+ (void const *)uPtrWhere,
+ MemoryBasicInformation,
+ &MemInfo,
+ sizeof(MemInfo),
+ &cbActual);
+ if (!NT_SUCCESS(rcNt))
+ {
+ RTTestIPrintf(RTTESTLVL_ALWAYS, "%p: rcNt=%#x\n", uPtrWhere, rcNt);
+ break;
+ }
+
+ /* stringify the memory state. */
+ char szMemType[1024];
+ char szMemState[1024];
+ char szMemProt[1024];
+ char szAllocProt[1024];
+
+ if ( MemInfo.AllocationBase != NULL
+ && MemInfo.AllocationBase == MemInfo.BaseAddress
+ && MemInfo.Protect != MemInfo.AllocationProtect)
+ RTTestIPrintf(RTTESTLVL_ALWAYS, "\n");
+
+ RTTestIPrintf(RTTESTLVL_ALWAYS, "%p-%p %-8s %-8s %-12s",
+ MemInfo.BaseAddress, (uintptr_t)MemInfo.BaseAddress + MemInfo.RegionSize - 1,
+ stringifyMemType(MemInfo.Type, szMemType, sizeof(szMemType)),
+ stringifyMemState(MemInfo.State, szMemState, sizeof(szMemState)),
+ stringifyMemProt(MemInfo.Protect, szMemProt, sizeof(szMemProt))
+ );
+ if ((uintptr_t)MemInfo.AllocationBase != 0)
+ {
+ if (MemInfo.AllocationBase != MemInfo.BaseAddress)
+ RTTestIPrintf(RTTESTLVL_ALWAYS, " %p", MemInfo.AllocationBase);
+ else
+ RTTestIPrintf(RTTESTLVL_ALWAYS, " %s", stringifyMemProt(MemInfo.AllocationProtect, szAllocProt, sizeof(szAllocProt)));
+ }
+ RTTestIPrintf(RTTESTLVL_ALWAYS, "\n");
+
+ if ((uintptr_t)MemInfo.BaseAddress != uPtrWhere)
+ RTTestIPrintf(RTTESTLVL_ALWAYS, " !Warning! Queried %p got BaseAddress=%p!\n",
+ uPtrWhere, MemInfo.BaseAddress);
+
+ /* Image or mapped, then try get a file name. */
+ if (MemInfo.Type == MEM_IMAGE || MemInfo.Type == MEM_MAPPED)
+ {
+ union
+ {
+ MEMORY_SECTION_NAME Core;
+ WCHAR awcPadding[UNICODE_STRING_MAX_CHARS + (sizeof(UNICODE_STRING_MAX_CHARS) + 1) / sizeof(WCHAR)];
+ } uBuf;
+ RT_ZERO(uBuf);
+ uBuf.Core.SectionFileName.Length = UNICODE_STRING_MAX_CHARS * 2;
+ uBuf.Core.SectionFileName.MaximumLength = UNICODE_STRING_MAX_CHARS * 2;
+ uBuf.Core.SectionFileName.Buffer = &uBuf.Core.NameBuffer[0];
+
+ cbActual = 0;
+ rcNt = NtQueryVirtualMemory(g_hProcess,
+ (void const *)uPtrWhere,
+ MemorySectionName,
+ &uBuf,
+ sizeof(uBuf),
+ &cbActual);
+ if (NT_SUCCESS(rcNt))
+ RTTestIPrintf(RTTESTLVL_ALWAYS, " %.*ls\n",
+ uBuf.Core.SectionFileName.Length / 2, uBuf.Core.SectionFileName.Buffer);
+ else
+ {
+ RTTestIPrintf(RTTESTLVL_ALWAYS, "%p: MemorySectionName - rcNt=%#x\n", uPtrWhere, rcNt);
+ RTTESTI_CHECK(rcNt == STATUS_FILE_INVALID && MemInfo.Type == MEM_MAPPED);
+ }
+ }
+
+ /* Advance. */
+ cbAdvance = MemInfo.RegionSize;
+ //cbAdvance = 0;
+ if (uPtrWhere + cbAdvance <= uPtrWhere)
+ break;
+ uPtrWhere += MemInfo.RegionSize;
+ }
+}
+
+
+static void tstQueryInformationProcess(void)
+{
+ RTTestISub("NtQueryInformationProcess");
+
+ NTSTATUS rcNt;
+
+ /* Basic info */
+ PROCESS_BASIC_INFORMATION BasicInfo;
+ RT_ZERO(BasicInfo);
+ DWORD cbActual = 0;
+ rcNt = NtQueryInformationProcess(g_hProcess,
+ ProcessBasicInformation,
+ &BasicInfo, sizeof(BasicInfo), &cbActual);
+ RTTESTI_CHECK_MSG(NT_SUCCESS(rcNt), ("rcNt=%#x\n", rcNt));
+ if (NT_SUCCESS(rcNt))
+ RTTestIPrintf(RTTESTLVL_ALWAYS, "BasicInfo:\n"
+ " UniqueProcessId = %#x (%6d)\n"
+ " InheritedFromUniqueProcessId = %#x (%6d)\n"
+ " ExitStatus = %#x\n"
+ " PebBaseAddress = %p\n"
+ " AffinityMask = %#zx\n"
+ " BasePriority = %#zx\n"
+ ,
+ BasicInfo.UniqueProcessId, BasicInfo.UniqueProcessId,
+ BasicInfo.InheritedFromUniqueProcessId, BasicInfo.InheritedFromUniqueProcessId,
+ BasicInfo.ExitStatus,
+ BasicInfo.PebBaseAddress,
+ BasicInfo.AffinityMask,
+ BasicInfo.BasePriority
+ );
+
+ /* Debugger present? */
+ DWORD_PTR uPtr = ~(DWORD_PTR)0;
+ cbActual = 0;
+ rcNt = NtQueryInformationProcess(g_hProcess,
+ ProcessDebugPort,
+ &uPtr, sizeof(uPtr), &cbActual);
+ RTTESTI_CHECK_MSG(NT_SUCCESS(rcNt), ("rcNt=%#x\n", rcNt));
+ if (NT_SUCCESS(rcNt))
+ RTTestIPrintf(RTTESTLVL_ALWAYS, "ProcessDebugPort: %p\n", uPtr);
+
+ /* Debug object handle, whatever that is... */
+ uPtr = ~(DWORD_PTR)0;
+ cbActual = 0;
+ rcNt = NtQueryInformationProcess(g_hProcess,
+ ProcessDebugObjectHandle,
+ &uPtr, sizeof(uPtr), &cbActual);
+ if (NT_SUCCESS(rcNt))
+ RTTestIPrintf(RTTESTLVL_ALWAYS, "ProcessDebugObjectHandle: %p\n", uPtr);
+ else if (rcNt == STATUS_PORT_NOT_SET)
+ RTTestIPrintf(RTTESTLVL_ALWAYS, "ProcessDebugObjectHandle: rcNt=%#x (STATUS_PORT_NOT_SET)\n", uPtr);
+ else
+ RTTESTI_CHECK_MSG(NT_SUCCESS(rcNt), ("rcNt=%#x\n", rcNt));
+
+ /* 32-bit app on 64-bit host? */
+ uPtr = ~(DWORD_PTR)0;
+ cbActual = 0;
+ rcNt = NtQueryInformationProcess(g_hProcess,
+ ProcessWow64Information,
+ &uPtr, sizeof(uPtr), &cbActual);
+ RTTESTI_CHECK_MSG(NT_SUCCESS(rcNt), ("rcNt=%#x\n", rcNt));
+ if (NT_SUCCESS(rcNt))
+ RTTestIPrintf(RTTESTLVL_ALWAYS, "ProcessWow64Information: %p\n", uPtr);
+
+ /* Process image name (NT). */
+ struct
+ {
+ UNICODE_STRING UniStr;
+ WCHAR awBuffer[UNICODE_STRING_MAX_CHARS];
+ } StrBuf;
+ RT_ZERO(StrBuf);
+ StrBuf.UniStr.Length = UNICODE_STRING_MAX_CHARS * 2;
+ StrBuf.UniStr.MaximumLength = UNICODE_STRING_MAX_CHARS * 2;
+ StrBuf.UniStr.Buffer = &StrBuf.awBuffer[0];
+ cbActual = 0;
+ rcNt = NtQueryInformationProcess(g_hProcess,
+ ProcessImageFileName,
+ &StrBuf, sizeof(StrBuf), &cbActual);
+ RTTESTI_CHECK_MSG(NT_SUCCESS(rcNt), ("rcNt=%#x\n", rcNt));
+ if (NT_SUCCESS(rcNt))
+ RTTestIPrintf(RTTESTLVL_ALWAYS, "ProcessImageFileName: len=%u\n %.*ls\n",
+ StrBuf.UniStr.Length, StrBuf.UniStr.Length, StrBuf.UniStr.Buffer);
+
+ /* Process image name (Win32) - Not available on Windows 2003. */
+ RT_ZERO(StrBuf);
+ StrBuf.UniStr.Length = UNICODE_STRING_MAX_CHARS * 2;
+ StrBuf.UniStr.MaximumLength = UNICODE_STRING_MAX_CHARS * 2;
+ StrBuf.UniStr.Buffer = &StrBuf.awBuffer[0];
+ cbActual = 0;
+ rcNt = NtQueryInformationProcess(g_hProcess,
+ ProcessImageFileNameWin32,
+ &StrBuf, sizeof(StrBuf), &cbActual);
+ if (rcNt != STATUS_INVALID_INFO_CLASS)
+ {
+ RTTESTI_CHECK_MSG(NT_SUCCESS(rcNt), ("rcNt=%#x\n", rcNt));
+ if (NT_SUCCESS(rcNt))
+ RTTestIPrintf(RTTESTLVL_ALWAYS, "ProcessImageFileNameWin32: len=%u\n %.*ls\n",
+ StrBuf.UniStr.Length, StrBuf.UniStr.Length, StrBuf.UniStr.Buffer);
+ }
+ else
+ RTTestIPrintf(RTTESTLVL_ALWAYS, "ProcessImageFileNameWin32: Not supported (STATUS_INVALID_INFO_CLASS).\n");
+
+ /* Process image mapping - Not available on Windows 2003. */
+ uPtr = ~(DWORD_PTR)0;
+ cbActual = 0;
+ rcNt = NtQueryInformationProcess(g_hProcess,
+ ProcessImageFileMapping,
+ &uPtr, sizeof(uPtr), &cbActual);
+ if (NT_SUCCESS(rcNt))
+ RTTestIPrintf(RTTESTLVL_ALWAYS, "ProcessImageFileMapping: %p\n", uPtr);
+ else if (rcNt == STATUS_OBJECT_TYPE_MISMATCH)
+ RTTestIPrintf(RTTESTLVL_ALWAYS, "ProcessImageFileMapping: rcNt=%#x (STATUS_OBJECT_TYPE_MISMATCH)\n", rcNt);
+ else if (rcNt == STATUS_INVALID_INFO_CLASS)
+ RTTestIPrintf(RTTESTLVL_ALWAYS, "ProcessImageFileMapping: Not supported (STATUS_INVALID_INFO_CLASS).\n");
+ else
+ RTTestIFailed("ProcessImageFileMapping: rcNt=%#x\n", rcNt);
+
+
+ /* Handles. Broken for 64-bit input. */
+ uint32_t u32 = UINT32_MAX;
+ cbActual = 0;
+ rcNt = NtQueryInformationProcess(g_hProcess,
+ ProcessHandleCount,
+ &u32, sizeof(u32), &cbActual);
+ if (NT_SUCCESS(rcNt))
+ RTTestIPrintf(RTTESTLVL_ALWAYS, "ProcessHandleCount: %#x (%d)\n", u32, u32);
+ else
+ RTTestIFailed("ProcessHandleCount: rcNt=%#x\n", rcNt);
+
+ /* Execute flags. */
+#if 0 /* fails... wrong process handle? */
+ u32 = ~(DWORD_PTR)0;
+ cbActual = 0;
+ rcNt = NtQueryInformationProcess(g_hProcess,
+ ProcessExecuteFlags,
+ &u32, sizeof(u32), &cbActual);
+ if (NT_SUCCESS(rcNt))
+ RTTestIPrintf(RTTESTLVL_ALWAYS, "ProcessExecuteFlags: %#p\n", u32);
+ else
+ RTTestIFailed("ProcessExecuteFlags: rcNt=%#x\n", rcNt);
+#endif
+
+ /** @todo ProcessImageInformation */
+}
+
+
+int main(int argc, char **argv)
+{
+ RTEXITCODE rcExit = RTTestInitAndCreate("tstNtQueryStuff", &g_hTest);
+ if (rcExit != RTEXITCODE_SUCCESS)
+ return rcExit;
+ RTTestBanner(g_hTest);
+
+ g_hProcess = GetCurrentProcess();
+ if (argc >= 2 && argv[1][0] != '-')
+ {
+ const char *pszPid = argv[1];
+ uint32_t idPid = RTStrToInt32(pszPid);
+
+ uint32_t fAccess = PROCESS_QUERY_INFORMATION;
+ if (argc >= 3)
+ fAccess = RTStrToInt32(argv[2]);
+
+ g_hProcess = OpenProcess(fAccess, FALSE, idPid);
+ if (g_hProcess == NULL)
+ {
+ RTTestIFailed("Error %u opening process %u (%s)\n", GetLastError(), idPid, pszPid);
+ return RTTestSummaryAndDestroy(g_hTest);
+ }
+ }
+
+ tstQueryVirtualMemory();
+ tstQueryInformationProcess();
+
+ return RTTestSummaryAndDestroy(g_hTest);
+}
+
diff --git a/src/VBox/HostDrivers/Support/testcase/tstPage.cpp b/src/VBox/HostDrivers/Support/testcase/tstPage.cpp
new file mode 100644
index 00000000..2104d56f
--- /dev/null
+++ b/src/VBox/HostDrivers/Support/testcase/tstPage.cpp
@@ -0,0 +1,101 @@
+/* $Id: tstPage.cpp $ */
+/** @file
+ * SUP Testcase - Page allocation interface (ring 3).
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include <VBox/sup.h>
+#include <VBox/param.h>
+#include <iprt/initterm.h>
+#include <iprt/stream.h>
+#include <string.h>
+
+
+int main(int argc, char **argv)
+{
+ int cErrors = 0;
+
+ RTR3InitExe(argc, &argv, RTR3INIT_FLAGS_TRY_SUPLIB);
+ int rc = SUPR3Init(NULL);
+ cErrors += rc != 0;
+ if (!rc)
+ {
+ void *pv;
+ rc = SUPR3PageAlloc(1, 0, &pv);
+ cErrors += rc != 0;
+ if (!rc)
+ {
+ memset(pv, 0xff, PAGE_SIZE);
+ rc = SUPR3PageFree(pv, 1);
+ cErrors += rc != 0;
+ if (rc)
+ RTPrintf("tstPage: SUPR3PageFree() failed rc=%Rrc\n", rc);
+ }
+ else
+ RTPrintf("tstPage: SUPR3PageAlloc(1,) failed rc=%Rrc\n", rc);
+
+ /*
+ * Big chunk.
+ */
+ rc = SUPR3PageAlloc(1023, 0, &pv);
+ cErrors += rc != 0;
+ if (!rc)
+ {
+ memset(pv, 0xfe, 1023 << PAGE_SHIFT);
+ rc = SUPR3PageFree(pv, 1023);
+ cErrors += rc != 0;
+ if (rc)
+ RTPrintf("tstPage: SUPR3PageFree() failed rc=%Rrc\n", rc);
+ }
+ else
+ RTPrintf("tstPage: SUPR3PageAlloc(1,) failed rc=%Rrc\n", rc);
+
+
+ //rc = SUPR3Term();
+ cErrors += rc != 0;
+ if (rc)
+ RTPrintf("tstPage: SUPR3Term failed rc=%Rrc\n", rc);
+ }
+ else
+ RTPrintf("tstPage: SUPR3Init failed rc=%Rrc\n", rc);
+
+ if (!cErrors)
+ RTPrintf("tstPage: SUCCESS\n");
+ else
+ RTPrintf("tstPage: FAILURE - %d errors\n", cErrors);
+ return !!cErrors;
+}
diff --git a/src/VBox/HostDrivers/Support/testcase/tstPin.cpp b/src/VBox/HostDrivers/Support/testcase/tstPin.cpp
new file mode 100644
index 00000000..33d7c131
--- /dev/null
+++ b/src/VBox/HostDrivers/Support/testcase/tstPin.cpp
@@ -0,0 +1,223 @@
+/* $Id: tstPin.cpp $ */
+/** @file
+ * SUP Testcase - Memory locking interface (ring 3).
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include <VBox/sup.h>
+#include <VBox/param.h>
+#include <iprt/errcore.h>
+#include <iprt/initterm.h>
+#include <iprt/stream.h>
+#include <iprt/thread.h>
+#include <iprt/string.h>
+
+#include "../SUPLibInternal.h"
+
+
+int main(int argc, char **argv)
+{
+ int rc;
+ int rcRet = 0;
+ RTHCPHYS HCPhys;
+
+ RTR3InitExe(argc, &argv, RTR3INIT_FLAGS_TRY_SUPLIB);
+ rc = SUPR3Init(NULL);
+ RTPrintf("SUPR3Init -> rc=%d\n", rc);
+ rcRet += rc != 0;
+ if (!rc)
+ {
+ /*
+ * Simple test.
+ */
+ void *pv;
+ rc = SUPR3PageAlloc(1, 0, &pv);
+ AssertRC(rc);
+ RTPrintf("pv=%p\n", pv);
+ SUPPAGE aPages[1];
+ rc = supR3PageLock(pv, 1, &aPages[0]);
+ RTPrintf("rc=%d pv=%p aPages[0]=%RHp\n", rc, pv, aPages[0]);
+ RTThreadSleep(1500);
+#if 0
+ RTPrintf("Unlocking...\n");
+ RTThreadSleep(250);
+ rc = SUPPageUnlock(pv);
+ RTPrintf("rc=%d\n", rc);
+ RTThreadSleep(1500);
+#endif
+
+ /*
+ * More extensive.
+ */
+ static struct
+ {
+ void *pv;
+ void *pvAligned;
+ SUPPAGE aPages[16];
+ } aPinnings[500];
+ for (unsigned i = 0; i < sizeof(aPinnings) / sizeof(aPinnings[0]); i++)
+ {
+ aPinnings[i].pv = NULL;
+ SUPR3PageAlloc(0x10000 >> PAGE_SHIFT, 0, &aPinnings[i].pv);
+ aPinnings[i].pvAligned = RT_ALIGN_P(aPinnings[i].pv, PAGE_SIZE);
+ rc = supR3PageLock(aPinnings[i].pvAligned, 0xf000 >> PAGE_SHIFT, &aPinnings[i].aPages[0]);
+ if (!rc)
+ {
+ RTPrintf("i=%d: pvAligned=%p pv=%p:\n", i, aPinnings[i].pvAligned, aPinnings[i].pv);
+ memset(aPinnings[i].pv, 0xfa, 0x10000);
+ unsigned c4GPluss = 0;
+ for (unsigned j = 0; j < (0xf000 >> PAGE_SHIFT); j++)
+ if (aPinnings[i].aPages[j].Phys >= _4G)
+ {
+ RTPrintf("%2d: vrt=%p phys=%RHp\n", j, (char *)aPinnings[i].pvAligned + (j << PAGE_SHIFT), aPinnings[i].aPages[j].Phys);
+ c4GPluss++;
+ }
+ RTPrintf("i=%d: c4GPluss=%d\n", i, c4GPluss);
+ }
+ else
+ {
+ RTPrintf("SUPPageLock -> rc=%d\n", rc);
+ rcRet++;
+ SUPR3PageFree(aPinnings[i].pv, 0x10000 >> PAGE_SHIFT);
+ aPinnings[i].pv = aPinnings[i].pvAligned = NULL;
+ break;
+ }
+ }
+
+ for (unsigned i = 0; i < sizeof(aPinnings) / sizeof(aPinnings[0]); i += 2)
+ {
+ if (aPinnings[i].pvAligned)
+ {
+ rc = supR3PageUnlock(aPinnings[i].pvAligned);
+ if (rc)
+ {
+ RTPrintf("SUPPageUnlock(%p) -> rc=%d\n", aPinnings[i].pvAligned, rc);
+ rcRet++;
+ }
+ memset(aPinnings[i].pv, 0xaf, 0x10000);
+ }
+ }
+
+ for (unsigned i = 0; i < sizeof(aPinnings) / sizeof(aPinnings[0]); i += 2)
+ {
+ if (aPinnings[i].pv)
+ {
+ memset(aPinnings[i].pv, 0xcc, 0x10000);
+ SUPR3PageFree(aPinnings[i].pv, 0x10000 >> PAGE_SHIFT);
+ aPinnings[i].pv = NULL;
+ }
+ }
+
+
+/* Support for allocating Ring-0 executable memory with contiguous physical backing isn't implemented on Solaris. */
+#if !defined(RT_OS_SOLARIS)
+ /*
+ * Allocate a bit of contiguous memory.
+ */
+ pv = SUPR3ContAlloc(RT_ALIGN_Z(15003, PAGE_SIZE) >> PAGE_SHIFT, NULL, &HCPhys);
+ rcRet += pv == NULL || HCPhys == 0;
+ if (pv && HCPhys)
+ {
+ RTPrintf("SUPR3ContAlloc(15003) -> HCPhys=%llx pv=%p\n", HCPhys, pv);
+ void *pv0 = pv;
+ memset(pv0, 0xaf, 15003);
+ pv = SUPR3ContAlloc(RT_ALIGN_Z(12999, PAGE_SIZE) >> PAGE_SHIFT, NULL, &HCPhys);
+ rcRet += pv == NULL || HCPhys == 0;
+ if (pv && HCPhys)
+ {
+ RTPrintf("SUPR3ContAlloc(12999) -> HCPhys=%llx pv=%p\n", HCPhys, pv);
+ memset(pv, 0xbf, 12999);
+ rc = SUPR3ContFree(pv, RT_ALIGN_Z(12999, PAGE_SIZE) >> PAGE_SHIFT);
+ rcRet += rc != 0;
+ if (rc)
+ RTPrintf("SUPR3ContFree failed! rc=%d\n", rc);
+ }
+ else
+ RTPrintf("SUPR3ContAlloc (2nd) failed!\n");
+ memset(pv0, 0xaf, 15003);
+ /* pv0 is intentionally not freed! */
+ }
+ else
+ RTPrintf("SUPR3ContAlloc failed!\n");
+#endif
+
+ /*
+ * Allocate a big chunk of virtual memory and then lock it.
+ */
+ #define BIG_SIZE 72*1024*1024
+ #define BIG_SIZEPP (BIG_SIZE + PAGE_SIZE)
+ pv = NULL;
+ SUPR3PageAlloc(BIG_SIZEPP >> PAGE_SHIFT, 0, &pv);
+ if (pv)
+ {
+ static SUPPAGE s_aPages[BIG_SIZE >> PAGE_SHIFT];
+ void *pvAligned = RT_ALIGN_P(pv, PAGE_SIZE);
+ rc = supR3PageLock(pvAligned, BIG_SIZE >> PAGE_SHIFT, &s_aPages[0]);
+ if (!rc)
+ {
+ /* dump */
+ RTPrintf("SUPPageLock(%p,%d,) succeeded!\n", pvAligned, BIG_SIZE);
+ memset(pv, 0x42, BIG_SIZEPP);
+ #if 0
+ for (unsigned j = 0; j < (BIG_SIZE >> PAGE_SHIFT); j++)
+ RTPrintf("%2d: vrt=%p phys=%08x\n", j, (char *)pvAligned + (j << PAGE_SHIFT), (uintptr_t)s_aPages[j].pvPhys);
+ #endif
+
+ /* unlock */
+ rc = supR3PageUnlock(pvAligned);
+ if (rc)
+ {
+ RTPrintf("SUPPageUnlock(%p) -> rc=%d\n", pvAligned, rc);
+ rcRet++;
+ }
+ memset(pv, 0xcc, BIG_SIZEPP);
+ }
+ else
+ {
+ RTPrintf("SUPPageLock(%p) -> rc=%d\n", pvAligned, rc);
+ rcRet++;
+ }
+ SUPR3PageFree(pv, BIG_SIZEPP >> PAGE_SHIFT);
+ }
+
+ rc = SUPR3Term(false /*fForced*/);
+ RTPrintf("SUPR3Term -> rc=%d\n", rc);
+ rcRet += rc != 0;
+ }
+
+ return rcRet;
+}
diff --git a/src/VBox/HostDrivers/Support/testcase/tstSupLoadModule.cpp b/src/VBox/HostDrivers/Support/testcase/tstSupLoadModule.cpp
new file mode 100644
index 00000000..844cc552
--- /dev/null
+++ b/src/VBox/HostDrivers/Support/testcase/tstSupLoadModule.cpp
@@ -0,0 +1,128 @@
+/* $Id: tstSupLoadModule.cpp $ */
+/** @file
+ * SUP Testcase - Test SUPR3LoadModule.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include <VBox/sup.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/stream.h>
+
+
+int main(int argc, char **argv)
+{
+ /*
+ * Init.
+ */
+ int rc = RTR3InitExe(argc, &argv, RTR3INIT_FLAGS_SUPLIB);
+ if (RT_FAILURE(rc))
+ return RTMsgInitFailure(rc);
+
+ /*
+ * Process arguments.
+ */
+ static const RTGETOPTDEF s_aOptions[] =
+ {
+ { "--keep", 'k', RTGETOPT_REQ_NOTHING },
+ { "--no-keep", 'n', RTGETOPT_REQ_NOTHING },
+ };
+
+ bool fKeepLoaded = false;
+
+ int ch;
+ RTGETOPTUNION ValueUnion;
+ RTGETOPTSTATE GetState;
+ RTGetOptInit(&GetState, argc, argv, s_aOptions, RT_ELEMENTS(s_aOptions), 1, 0);
+ while ((ch = RTGetOpt(&GetState, &ValueUnion)))
+ {
+ switch (ch)
+ {
+ case VINF_GETOPT_NOT_OPTION:
+ {
+ void *pvImageBase;
+ RTERRINFOSTATIC ErrInfo;
+ RTErrInfoInitStatic(&ErrInfo);
+ rc = SUPR3LoadModule(ValueUnion.psz, RTPathFilename(ValueUnion.psz), &pvImageBase, &ErrInfo.Core);
+ if (RT_FAILURE(rc))
+ {
+ RTMsgError("%Rrc when attempting to load '%s': %s\n", rc, ValueUnion.psz, ErrInfo.Core.pszMsg);
+ return 1;
+ }
+ RTPrintf("Loaded '%s' at %p\n", ValueUnion.psz, pvImageBase);
+
+ if (!fKeepLoaded)
+ {
+ rc = SUPR3FreeModule(pvImageBase);
+ if (RT_FAILURE(rc))
+ {
+ RTMsgError("%Rrc when attempting to load '%s'\n", rc, ValueUnion.psz);
+ return 1;
+ }
+ }
+ break;
+ }
+
+ case 'k':
+ fKeepLoaded = true;
+ break;
+
+ case 'n':
+ fKeepLoaded = false;
+ break;
+
+ case 'h':
+ RTPrintf("%s [mod1 [mod2...]]\n", argv[0]);
+ return 1;
+
+ case 'V':
+ RTPrintf("$Revision: 155244 $\n");
+ return 0;
+
+ default:
+ return RTGetOptPrintError(ch, &ValueUnion);
+ }
+ }
+
+ return 0;
+}
+
diff --git a/src/VBox/HostDrivers/Support/testcase/tstSupSem-Zombie.cpp b/src/VBox/HostDrivers/Support/testcase/tstSupSem-Zombie.cpp
new file mode 100644
index 00000000..fff1795c
--- /dev/null
+++ b/src/VBox/HostDrivers/Support/testcase/tstSupSem-Zombie.cpp
@@ -0,0 +1,232 @@
+/* $Id: tstSupSem-Zombie.cpp $ */
+/** @file
+ * Support Library Testcase - Ring-3 Semaphore interface - Zombie bugs.
+ */
+
+/*
+ * Copyright (C) 2009-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include <VBox/sup.h>
+
+#include <VBox/param.h>
+#include <iprt/env.h>
+#include <iprt/errcore.h>
+#include <iprt/initterm.h>
+#include <iprt/process.h>
+#include <iprt/stream.h>
+#include <iprt/string.h>
+#include <iprt/test.h>
+#include <iprt/time.h>
+#include <iprt/thread.h>
+
+
+/*********************************************************************************************************************************
+* Structures and Typedefs *
+*********************************************************************************************************************************/
+static PSUPDRVSESSION g_pSession;
+static RTTEST g_hTest;
+
+
+
+static DECLCALLBACK(int) tstSupSemSRETimed(RTTHREAD hSelf, void *pvUser)
+{
+ SUPSEMEVENT hEvent = (SUPSEMEVENT)pvUser;
+ RTThreadUserSignal(hSelf);
+ int rc = SUPSemEventWaitNoResume(g_pSession, hEvent, 120*1000);
+ AssertReleaseMsg(rc == VERR_INTERRUPTED, ("%Rrc\n", rc));
+ return rc;
+}
+
+
+static DECLCALLBACK(int) tstSupSemMRETimed(RTTHREAD hSelf, void *pvUser)
+{
+ SUPSEMEVENTMULTI hEventMulti = (SUPSEMEVENTMULTI)pvUser;
+ RTThreadUserSignal(hSelf);
+ int rc = SUPSemEventMultiWaitNoResume(g_pSession, hEventMulti, 120*1000);
+ AssertReleaseMsg(rc == VERR_INTERRUPTED, ("%Rrc\n", rc));
+ return rc;
+}
+
+
+static DECLCALLBACK(int) tstSupSemSREInf(RTTHREAD hSelf, void *pvUser)
+{
+ SUPSEMEVENT hEvent = (SUPSEMEVENT)pvUser;
+ RTThreadUserSignal(hSelf);
+ int rc = SUPSemEventWaitNoResume(g_pSession, hEvent, RT_INDEFINITE_WAIT);
+ AssertReleaseMsg(rc == VERR_INTERRUPTED, ("%Rrc\n", rc));
+ return rc;
+}
+
+
+static DECLCALLBACK(int) tstSupSemMREInf(RTTHREAD hSelf, void *pvUser)
+{
+ SUPSEMEVENTMULTI hEventMulti = (SUPSEMEVENTMULTI)pvUser;
+ RTThreadUserSignal(hSelf);
+ int rc = SUPSemEventMultiWaitNoResume(g_pSession, hEventMulti, RT_INDEFINITE_WAIT);
+ AssertReleaseMsg(rc == VERR_INTERRUPTED, ("%Rrc\n", rc));
+ return rc;
+}
+
+static int mainChild(void)
+{
+ /*
+ * Init.
+ */
+ int rc = RTR3InitExeNoArguments(RTR3INIT_FLAGS_TRY_SUPLIB);
+ if (RT_FAILURE(rc))
+ {
+ RTPrintf("tstSupSem-Zombie-Child: fatal error: RTR3InitExeNoArguments failed with rc=%Rrc\n", rc);
+ return 1;
+ }
+
+ RTTEST hTest;
+ rc = RTTestCreate("tstSupSem-Zombie-Child", &hTest);
+ if (RT_FAILURE(rc))
+ {
+ RTPrintf("tstSupSem-Zombie-Child: fatal error: RTTestCreate failed with rc=%Rrc\n", rc);
+ return 1;
+ }
+ g_hTest = hTest;
+
+ PSUPDRVSESSION pSession;
+ rc = SUPR3Init(&pSession);
+ if (RT_FAILURE(rc))
+ {
+ RTTestFailed(hTest, "SUPR3Init failed with rc=%Rrc\n", rc);
+ return RTTestSummaryAndDestroy(hTest);
+ }
+ g_pSession = pSession;
+
+ /*
+ * A semaphore of each kind and throw a bunch of threads on them.
+ */
+ SUPSEMEVENT hEvent = NIL_SUPSEMEVENT;
+ RTTESTI_CHECK_RC(rc = SUPSemEventCreate(pSession, &hEvent), VINF_SUCCESS);
+ if (RT_SUCCESS(rc))
+ {
+ SUPSEMEVENTMULTI hEventMulti = NIL_SUPSEMEVENT;
+ RTTESTI_CHECK_RC(SUPSemEventMultiCreate(pSession, &hEventMulti), VINF_SUCCESS);
+ if (RT_SUCCESS(rc))
+ {
+ for (uint32_t cThreads = 0; cThreads < 5; cThreads++)
+ {
+ RTTHREAD hThread;
+ RTTESTI_CHECK_RC(RTThreadCreate(&hThread, tstSupSemSRETimed, (void *)hEvent, 0, RTTHREADTYPE_TIMER, 0 /*fFlags*/, "IntSRE"), VINF_SUCCESS);
+ RTTESTI_CHECK_RC(RTThreadCreate(&hThread, tstSupSemMRETimed, (void *)hEventMulti, 0, RTTHREADTYPE_TIMER, 0 /*fFlags*/, "IntMRE"), VINF_SUCCESS);
+ RTTESTI_CHECK_RC(RTThreadCreate(&hThread, tstSupSemSREInf, (void *)hEvent, 0, RTTHREADTYPE_TIMER, 0 /*fFlags*/, "IntSRE"), VINF_SUCCESS);
+ RTTESTI_CHECK_RC(RTThreadCreate(&hThread, tstSupSemMREInf, (void *)hEventMulti, 0, RTTHREADTYPE_TIMER, 0 /*fFlags*/, "IntMRE"), VINF_SUCCESS);
+ RTThreadSleep(2);
+ }
+ RTThreadSleep(50);
+
+ /*
+ * This is where the test really starts...
+ */
+ return 0;
+ }
+ }
+
+ return RTTestSummaryAndDestroy(hTest);
+}
+
+
+/**
+ * The parent main routine.
+ * @param argv0 The executable name (or whatever).
+ */
+static int mainParent(const char *argv0)
+{
+ /*
+ * Init.
+ */
+ RTTEST hTest;
+ int rc = RTTestInitAndCreate("tstSupSem-Zombie", &hTest);
+ if (rc)
+ return rc;
+ RTTestBanner(hTest);
+
+ /*
+ * Spin of the child process which may or may not turn into a zombie
+ */
+ for (uint32_t iPass = 0; iPass < 32; iPass++)
+ {
+ RTTestSubF(hTest, "Pass %u", iPass);
+
+ RTPROCESS hProcess;
+ const char *apszArgs[3] = { argv0, "--child", NULL };
+ RTTESTI_CHECK_RC_OK(rc = RTProcCreate(argv0, apszArgs, RTENV_DEFAULT, 0 /*fFlags*/, &hProcess));
+ if (RT_SUCCESS(rc))
+ {
+ /*
+ * Wait for 60 seconds then give up.
+ */
+ RTPROCSTATUS Status;
+ uint64_t StartTS = RTTimeMilliTS();
+ for (;;)
+ {
+ rc = RTProcWait(hProcess, RTPROCWAIT_FLAGS_NOBLOCK, &Status);
+ if (RT_SUCCESS(rc))
+ break;
+ uint64_t cElapsed = RTTimeMilliTS() - StartTS;
+ if (cElapsed > 60*1000)
+ break;
+ RTThreadSleep(cElapsed < 60 ? 30 : cElapsed < 200 ? 10 : 100);
+ }
+ RTTESTI_CHECK_RC_OK(rc);
+ if ( RT_SUCCESS(rc)
+ && ( Status.enmReason != RTPROCEXITREASON_NORMAL
+ || Status.iStatus != 0))
+ {
+ RTTestIFailed("child %d (%#x) reason %d\n", Status.iStatus, Status.iStatus, Status.enmReason);
+ rc = VERR_PERMISSION_DENIED;
+ }
+ }
+ /* one zombie process is enough. */
+ if (RT_FAILURE(rc))
+ break;
+ }
+
+ return RTTestSummaryAndDestroy(hTest);
+}
+
+
+int main(int argc, char **argv)
+{
+ if ( argc == 2
+ && !strcmp(argv[1], "--child"))
+ return mainChild();
+ return mainParent(argv[0]);
+}
+
diff --git a/src/VBox/HostDrivers/Support/testcase/tstSupSem.cpp b/src/VBox/HostDrivers/Support/testcase/tstSupSem.cpp
new file mode 100644
index 00000000..3091d67a
--- /dev/null
+++ b/src/VBox/HostDrivers/Support/testcase/tstSupSem.cpp
@@ -0,0 +1,649 @@
+/* $Id: tstSupSem.cpp $ */
+/** @file
+ * Support Library Testcase - Ring-3 Semaphore interface.
+ */
+
+/*
+ * Copyright (C) 2009-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include <VBox/sup.h>
+
+#include <VBox/param.h>
+#include <iprt/err.h>
+#include <iprt/initterm.h>
+#include <iprt/message.h>
+#include <iprt/stream.h>
+#include <iprt/test.h>
+#include <iprt/thread.h>
+#include <iprt/process.h>
+#include <iprt/env.h>
+#include <iprt/string.h>
+#include <iprt/time.h>
+
+
+/*********************************************************************************************************************************
+* Structures and Typedefs *
+*********************************************************************************************************************************/
+static PSUPDRVSESSION g_pSession;
+static RTTEST g_hTest;
+static uint32_t g_cMillies; /* Used by the interruptible tests. */
+
+
+
+static DECLCALLBACK(int) tstSupSemInterruptibleSRE(RTTHREAD hSelf, void *pvUser)
+{
+ SUPSEMEVENT hEvent = (SUPSEMEVENT)pvUser;
+ RTThreadUserSignal(hSelf);
+ return SUPSemEventWaitNoResume(g_pSession, hEvent, g_cMillies);
+}
+
+
+static DECLCALLBACK(int) tstSupSemInterruptibleMRE(RTTHREAD hSelf, void *pvUser)
+{
+ SUPSEMEVENTMULTI hEventMulti = (SUPSEMEVENTMULTI)pvUser;
+ RTThreadUserSignal(hSelf);
+ return SUPSemEventMultiWaitNoResume(g_pSession, hEventMulti, g_cMillies);
+}
+
+
+int main(int argc, char **argv)
+{
+ bool fSys = true;
+ bool fGip = false;
+#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
+ fGip = true;
+#endif
+
+ /*
+ * Init.
+ */
+ int rc = RTR3InitExe(argc, &argv, RTR3INIT_FLAGS_TRY_SUPLIB);
+ if (RT_FAILURE(rc))
+ return RTMsgInitFailure(rc);
+
+ if (argc == 2 && !strcmp(argv[1], "child"))
+ {
+ RTThreadSleep(300);
+ return 0;
+ }
+
+ RTTEST hTest;
+ rc = RTTestCreate("tstSupSem", &hTest);
+ if (RT_FAILURE(rc))
+ {
+ RTPrintf("tstSupSem: fatal error: RTTestCreate failed with rc=%Rrc\n", rc);
+ return 1;
+ }
+ g_hTest = hTest;
+
+ PSUPDRVSESSION pSession;
+ rc = SUPR3Init(&pSession);
+ if (RT_FAILURE(rc))
+ {
+ RTTestFailed(hTest, "SUPR3Init failed with rc=%Rrc\n", rc);
+ return RTTestSummaryAndDestroy(hTest);
+ }
+ g_pSession = pSession;
+ RTTestBanner(hTest);
+
+ /*
+ * Basic API checks.
+ */
+ RTTestSub(hTest, "Single Release Event (SRE) API");
+ SUPSEMEVENT hEvent = NIL_SUPSEMEVENT;
+ RTTESTI_CHECK_RC(SUPSemEventCreate(pSession, &hEvent), VINF_SUCCESS);
+ RTTESTI_CHECK_RC(SUPSemEventWaitNoResume(pSession, hEvent, 0), VERR_TIMEOUT);
+ RTTESTI_CHECK_RC(SUPSemEventWaitNoResume(pSession, hEvent, 1), VERR_TIMEOUT);
+ RTTESTI_CHECK_RC(SUPSemEventWaitNoResume(pSession, hEvent, 2), VERR_TIMEOUT);
+ RTTESTI_CHECK_RC(SUPSemEventWaitNoResume(pSession, hEvent, 8), VERR_TIMEOUT);
+ RTTESTI_CHECK_RC(SUPSemEventWaitNoResume(pSession, hEvent,20), VERR_TIMEOUT);
+ RTTESTI_CHECK_RC(SUPSemEventSignal(pSession, hEvent), VINF_SUCCESS);
+ RTTESTI_CHECK_RC(SUPSemEventWaitNoResume(pSession, hEvent, 0), VINF_SUCCESS);
+ RTTESTI_CHECK_RC(SUPSemEventSignal(pSession, hEvent), VINF_SUCCESS);
+ RTTESTI_CHECK_RC(SUPSemEventWaitNoResume(pSession, hEvent, 1), VINF_SUCCESS);
+ RTTESTI_CHECK_RC(SUPSemEventSignal(pSession, hEvent), VINF_SUCCESS);
+ RTTESTI_CHECK_RC(SUPSemEventWaitNoResume(pSession, hEvent, 2), VINF_SUCCESS);
+ RTTESTI_CHECK_RC(SUPSemEventSignal(pSession, hEvent), VINF_SUCCESS);
+ RTTESTI_CHECK_RC(SUPSemEventWaitNoResume(pSession, hEvent, 8), VINF_SUCCESS);
+ RTTESTI_CHECK_RC(SUPSemEventSignal(pSession, hEvent), VINF_SUCCESS);
+ RTTESTI_CHECK_RC(SUPSemEventWaitNoResume(pSession, hEvent, 20), VINF_SUCCESS);
+ RTTESTI_CHECK_RC(SUPSemEventSignal(pSession, hEvent), VINF_SUCCESS);
+ RTTESTI_CHECK_RC(SUPSemEventWaitNoResume(pSession, hEvent,1000),VINF_SUCCESS);
+ RTTESTI_CHECK_RC(SUPSemEventSignal(pSession, hEvent), VINF_SUCCESS);
+ RTTESTI_CHECK_RC(SUPSemEventSignal(pSession, hEvent), VINF_SUCCESS);
+ RTTESTI_CHECK_RC(SUPSemEventWaitNoResume(pSession, hEvent, 0), VINF_SUCCESS);
+ RTTESTI_CHECK_RC(SUPSemEventWaitNoResume(pSession, hEvent, 0), VERR_TIMEOUT);
+ RTTESTI_CHECK_RC(SUPSemEventWaitNoResume(pSession, hEvent, 1), VERR_TIMEOUT);
+ RTTESTI_CHECK_RC(SUPSemEventWaitNoResume(pSession, hEvent, 2), VERR_TIMEOUT);
+ RTTESTI_CHECK_RC(SUPSemEventWaitNoResume(pSession, hEvent, 8), VERR_TIMEOUT);
+ RTTESTI_CHECK_RC(SUPSemEventWaitNoResume(pSession, hEvent,20), VERR_TIMEOUT);
+ RTTESTI_CHECK_RC(SUPSemEventClose(pSession, hEvent), VINF_OBJECT_DESTROYED);
+ RTTESTI_CHECK_RC(SUPSemEventClose(pSession, hEvent), VERR_INVALID_HANDLE);
+ RTTESTI_CHECK_RC(SUPSemEventClose(pSession, NIL_SUPSEMEVENT), VINF_SUCCESS);
+
+ RTTestSub(hTest, "Multiple Release Event (MRE) API");
+ SUPSEMEVENTMULTI hEventMulti = NIL_SUPSEMEVENT;
+ RTTESTI_CHECK_RC(SUPSemEventMultiCreate(pSession, &hEventMulti), VINF_SUCCESS);
+ RTTESTI_CHECK_RC(SUPSemEventMultiWaitNoResume(pSession, hEventMulti, 0), VERR_TIMEOUT);
+ RTTESTI_CHECK_RC(SUPSemEventMultiWaitNoResume(pSession, hEventMulti, 1), VERR_TIMEOUT);
+ RTTESTI_CHECK_RC(SUPSemEventMultiWaitNoResume(pSession, hEventMulti, 2), VERR_TIMEOUT);
+ RTTESTI_CHECK_RC(SUPSemEventMultiWaitNoResume(pSession, hEventMulti, 8), VERR_TIMEOUT);
+ RTTESTI_CHECK_RC(SUPSemEventMultiWaitNoResume(pSession, hEventMulti,20), VERR_TIMEOUT);
+ RTTESTI_CHECK_RC(SUPSemEventMultiSignal(pSession, hEventMulti), VINF_SUCCESS);
+ RTTESTI_CHECK_RC(SUPSemEventMultiWaitNoResume(pSession, hEventMulti, 0), VINF_SUCCESS);
+ RTTESTI_CHECK_RC(SUPSemEventMultiWaitNoResume(pSession, hEventMulti, 0), VINF_SUCCESS);
+ RTTESTI_CHECK_RC(SUPSemEventMultiWaitNoResume(pSession, hEventMulti, 0), VINF_SUCCESS);
+ RTTESTI_CHECK_RC(SUPSemEventMultiWaitNoResume(pSession, hEventMulti, 1), VINF_SUCCESS);
+ RTTESTI_CHECK_RC(SUPSemEventMultiWaitNoResume(pSession, hEventMulti, 2), VINF_SUCCESS);
+ RTTESTI_CHECK_RC(SUPSemEventMultiWaitNoResume(pSession, hEventMulti, 8), VINF_SUCCESS);
+ RTTESTI_CHECK_RC(SUPSemEventMultiWaitNoResume(pSession, hEventMulti,20), VINF_SUCCESS);
+ RTTESTI_CHECK_RC(SUPSemEventMultiWaitNoResume(pSession, hEventMulti,1000), VINF_SUCCESS);
+ RTTESTI_CHECK_RC(SUPSemEventMultiSignal(pSession, hEventMulti), VINF_SUCCESS);
+ RTTESTI_CHECK_RC(SUPSemEventMultiSignal(pSession, hEventMulti), VINF_SUCCESS);
+ RTTESTI_CHECK_RC(SUPSemEventMultiWaitNoResume(pSession, hEventMulti, 0), VINF_SUCCESS);
+ RTTESTI_CHECK_RC(SUPSemEventMultiReset(pSession, hEventMulti), VINF_SUCCESS);
+ RTTESTI_CHECK_RC(SUPSemEventMultiWaitNoResume(pSession, hEventMulti, 0), VERR_TIMEOUT);
+ RTTESTI_CHECK_RC(SUPSemEventMultiWaitNoResume(pSession, hEventMulti, 1), VERR_TIMEOUT);
+ RTTESTI_CHECK_RC(SUPSemEventMultiWaitNoResume(pSession, hEventMulti, 2), VERR_TIMEOUT);
+ RTTESTI_CHECK_RC(SUPSemEventMultiWaitNoResume(pSession, hEventMulti, 8), VERR_TIMEOUT);
+ RTTESTI_CHECK_RC(SUPSemEventMultiWaitNoResume(pSession, hEventMulti,20), VERR_TIMEOUT);
+ RTTESTI_CHECK_RC(SUPSemEventMultiSignal(pSession, hEventMulti), VINF_SUCCESS);
+ RTTESTI_CHECK_RC(SUPSemEventMultiWaitNoResume(pSession, hEventMulti, 0), VINF_SUCCESS);
+ RTTESTI_CHECK_RC(SUPSemEventMultiWaitNoResume(pSession, hEventMulti, 1), VINF_SUCCESS);
+ RTTESTI_CHECK_RC(SUPSemEventMultiWaitNoResume(pSession, hEventMulti, 2), VINF_SUCCESS);
+ RTTESTI_CHECK_RC(SUPSemEventMultiWaitNoResume(pSession, hEventMulti, 8), VINF_SUCCESS);
+ RTTESTI_CHECK_RC(SUPSemEventMultiWaitNoResume(pSession, hEventMulti, 20), VINF_SUCCESS);
+ RTTESTI_CHECK_RC(SUPSemEventMultiWaitNoResume(pSession, hEventMulti,1000), VINF_SUCCESS);
+ RTTESTI_CHECK_RC(SUPSemEventMultiClose(pSession, hEventMulti), VINF_OBJECT_DESTROYED);
+ RTTESTI_CHECK_RC(SUPSemEventMultiClose(pSession, hEventMulti), VERR_INVALID_HANDLE);
+ RTTESTI_CHECK_RC(SUPSemEventMultiClose(pSession, NIL_SUPSEMEVENTMULTI), VINF_SUCCESS);
+
+#if !defined(RT_OS_OS2) && !defined(RT_OS_WINDOWS)
+ RTTestSub(hTest, "SRE Interruptibility");
+ RTTESTI_CHECK_RC(SUPSemEventCreate(pSession, &hEvent), VINF_SUCCESS);
+ g_cMillies = RT_INDEFINITE_WAIT;
+ RTTHREAD hThread = NIL_RTTHREAD;
+ RTTESTI_CHECK_RC(RTThreadCreate(&hThread, tstSupSemInterruptibleSRE, (void *)hEvent, 0, RTTHREADTYPE_TIMER, RTTHREADFLAGS_WAITABLE, "IntSRE"), VINF_SUCCESS);
+ RTTESTI_CHECK_RC(RTThreadUserWait(hThread, 60*1000), VINF_SUCCESS);
+ RTThreadSleep(120);
+ RTThreadPoke(hThread);
+ int rcThread = VINF_SUCCESS;
+ RTTESTI_CHECK_RC(RTThreadWait(hThread, 60*1000, &rcThread), VINF_SUCCESS);
+ RTTESTI_CHECK_RC(rcThread, VERR_INTERRUPTED);
+ RTTESTI_CHECK_RC(SUPSemEventClose(pSession, hEvent), VINF_OBJECT_DESTROYED);
+
+ RTTESTI_CHECK_RC(SUPSemEventCreate(pSession, &hEvent), VINF_SUCCESS);
+ g_cMillies = 120*1000;
+ hThread = NIL_RTTHREAD;
+ RTTESTI_CHECK_RC(RTThreadCreate(&hThread, tstSupSemInterruptibleSRE, (void *)hEvent, 0, RTTHREADTYPE_TIMER, RTTHREADFLAGS_WAITABLE, "IntSRE"), VINF_SUCCESS);
+ RTTESTI_CHECK_RC(RTThreadUserWait(hThread, 60*1000), VINF_SUCCESS);
+ RTThreadSleep(120);
+ RTThreadPoke(hThread);
+ rcThread = VINF_SUCCESS;
+ RTTESTI_CHECK_RC(RTThreadWait(hThread, 60*1000, &rcThread), VINF_SUCCESS);
+ RTTESTI_CHECK_RC(rcThread, VERR_INTERRUPTED);
+ RTTESTI_CHECK_RC(SUPSemEventClose(pSession, hEvent), VINF_OBJECT_DESTROYED);
+
+
+ RTTestSub(hTest, "MRE Interruptibility");
+ RTTESTI_CHECK_RC(SUPSemEventMultiCreate(pSession, &hEventMulti), VINF_SUCCESS);
+ g_cMillies = RT_INDEFINITE_WAIT;
+ hThread = NIL_RTTHREAD;
+ RTTESTI_CHECK_RC(RTThreadCreate(&hThread, tstSupSemInterruptibleMRE, (void *)hEventMulti, 0, RTTHREADTYPE_TIMER, RTTHREADFLAGS_WAITABLE, "IntMRE"), VINF_SUCCESS);
+ RTTESTI_CHECK_RC(RTThreadUserWait(hThread, 60*1000), VINF_SUCCESS);
+ RTThreadSleep(120);
+ RTThreadPoke(hThread);
+ rcThread = VINF_SUCCESS;
+ RTTESTI_CHECK_RC(RTThreadWait(hThread, 60*1000, &rcThread), VINF_SUCCESS);
+ RTTESTI_CHECK_RC(rcThread, VERR_INTERRUPTED);
+ RTTESTI_CHECK_RC(SUPSemEventMultiClose(pSession, hEventMulti), VINF_OBJECT_DESTROYED);
+
+ RTTESTI_CHECK_RC(SUPSemEventMultiCreate(pSession, &hEventMulti), VINF_SUCCESS);
+ g_cMillies = 120*1000;
+ hThread = NIL_RTTHREAD;
+ RTTESTI_CHECK_RC(RTThreadCreate(&hThread, tstSupSemInterruptibleMRE, (void *)hEventMulti, 0, RTTHREADTYPE_TIMER, RTTHREADFLAGS_WAITABLE, "IntMRE"), VINF_SUCCESS);
+ RTTESTI_CHECK_RC(RTThreadUserWait(hThread, 60*1000), VINF_SUCCESS);
+ RTThreadSleep(120);
+ RTThreadPoke(hThread);
+ rcThread = VINF_SUCCESS;
+ RTTESTI_CHECK_RC(RTThreadWait(hThread, 60*1000, &rcThread), VINF_SUCCESS);
+ RTTESTI_CHECK_RC(rcThread, VERR_INTERRUPTED);
+ RTTESTI_CHECK_RC(SUPSemEventMultiClose(pSession, hEventMulti), VINF_OBJECT_DESTROYED);
+
+ /*
+ * Fork test.
+ * Spawn a thread waiting for an event, then spawn a new child process (of
+ * ourselves) and make sure that this does not alter the intended behaviour
+ * of our event semaphore implementation (see @bugref{5090}).
+ */
+ RTTestSub(hTest, "SRE Process Spawn");
+ hThread = NIL_RTTHREAD;
+ g_cMillies = 120*1000;
+ RTTESTI_CHECK_RC(SUPSemEventCreate(pSession, &hEvent), VINF_SUCCESS);
+ RTTESTI_CHECK_RC(RTThreadCreate(&hThread, tstSupSemInterruptibleSRE, (void *)hEvent, 0, RTTHREADTYPE_TIMER, RTTHREADFLAGS_WAITABLE, "IntSRE"), VINF_SUCCESS);
+
+ const char *apszArgs[3] = { argv[0], "child", NULL };
+ RTPROCESS Process = NIL_RTPROCESS;
+ RTThreadSleep(250);
+ RTTESTI_CHECK_RC(RTProcCreate(apszArgs[0], apszArgs, RTENV_DEFAULT, 0, &Process), VINF_SUCCESS);
+
+ RTThreadSleep(250);
+ RTTESTI_CHECK_RC(SUPSemEventSignal(pSession, hEvent), VINF_SUCCESS);
+
+ rcThread = VERR_GENERAL_FAILURE;
+ RTTESTI_CHECK_RC(RTThreadWait(hThread, 120*1000, &rcThread), VINF_SUCCESS);
+ RTTESTI_CHECK_RC(rcThread, VINF_SUCCESS);
+ RTTESTI_CHECK_RC(SUPSemEventClose(pSession, hEvent), VINF_OBJECT_DESTROYED);
+
+
+ RTTestSub(hTest, "MRE Process Spawn");
+ hThread = NIL_RTTHREAD;
+ g_cMillies = 120*1000;
+ RTTESTI_CHECK_RC(SUPSemEventMultiCreate(pSession, &hEvent), VINF_SUCCESS);
+ RTTESTI_CHECK_RC(RTThreadCreate(&hThread, tstSupSemInterruptibleMRE, (void *)hEvent, 0, RTTHREADTYPE_TIMER, RTTHREADFLAGS_WAITABLE, "IntSRE"), VINF_SUCCESS);
+
+ RTTHREAD hThread2 = NIL_RTTHREAD;
+ RTTESTI_CHECK_RC(RTThreadCreate(&hThread2, tstSupSemInterruptibleMRE, (void *)hEvent, 0, RTTHREADTYPE_TIMER, RTTHREADFLAGS_WAITABLE, "IntSRE"), VINF_SUCCESS);
+
+ Process = NIL_RTPROCESS;
+ RTThreadSleep(250);
+ RTTESTI_CHECK_RC(RTProcCreate(apszArgs[0], apszArgs, RTENV_DEFAULT, 0, &Process), VINF_SUCCESS);
+
+ RTThreadSleep(250);
+ RTTESTI_CHECK_RC(SUPSemEventMultiSignal(pSession, hEvent), VINF_SUCCESS);
+
+ rcThread = VERR_GENERAL_FAILURE;
+ RTTESTI_CHECK_RC(RTThreadWait(hThread, 120*1000, &rcThread), VINF_SUCCESS);
+ RTTESTI_CHECK_RC(rcThread, VINF_SUCCESS);
+
+ int rcThread2 = VERR_GENERAL_FAILURE;
+ RTTESTI_CHECK_RC(RTThreadWait(hThread2, 120*1000, &rcThread2), VINF_SUCCESS);
+ RTTESTI_CHECK_RC(rcThread2, VINF_SUCCESS);
+
+ RTTESTI_CHECK_RC(SUPSemEventMultiClose(pSession, hEvent), VINF_OBJECT_DESTROYED);
+
+#endif /* !OS2 && !WINDOWS */
+
+ {
+
+#define LOOP_COUNT 20
+ static unsigned const s_acMsIntervals[] = { 0, 1, 2, 3, 4, 8, 10, 16, 32 };
+ if (RTTestErrorCount(hTest) == 0)
+ {
+ RTTestSub(hTest, "SRE Timeout Accuracy (ms)");
+ RTTESTI_CHECK_RC(SUPSemEventCreate(pSession, &hEvent), VINF_SUCCESS);
+
+ uint32_t cInterrupted = 0;
+ for (unsigned i = 0; i < RT_ELEMENTS(s_acMsIntervals); i++)
+ {
+ uint64_t cMs = s_acMsIntervals[i];
+ uint64_t cNsMinSys = UINT64_MAX;
+ uint64_t cNsMin = UINT64_MAX;
+ uint64_t cNsTotalSys= 0;
+ uint64_t cNsTotal = 0;
+ unsigned cLoops = 0;
+ while (cLoops < LOOP_COUNT)
+ {
+ uint64_t u64StartSys = RTTimeSystemNanoTS();
+ uint64_t u64Start = RTTimeNanoTS();
+ int rcX = SUPSemEventWaitNoResume(pSession, hEvent, cMs);
+ uint64_t cNsElapsedSys = RTTimeSystemNanoTS() - u64StartSys;
+ uint64_t cNsElapsed = RTTimeNanoTS() - u64Start;
+
+ if (rcX == VERR_INTERRUPTED)
+ {
+ cInterrupted++;
+ continue; /* retry */
+ }
+ if (rcX != VERR_TIMEOUT)
+ RTTestFailed(hTest, "%Rrc cLoops=%u cMs=%u", rcX, cLoops, cMs);
+
+ if (cNsElapsedSys < cNsMinSys)
+ cNsMinSys = cNsElapsedSys;
+ if (cNsElapsed < cNsMin)
+ cNsMin = cNsElapsed;
+ cNsTotalSys += cNsElapsedSys;
+ cNsTotal += cNsElapsed;
+ cLoops++;
+ }
+ if (fSys)
+ {
+ RTTestValueF(hTest, cNsMinSys, RTTESTUNIT_NS, "%u ms min (clock=sys)", cMs);
+ RTTestValueF(hTest, cNsTotalSys / cLoops, RTTESTUNIT_NS, "%u ms avg (clock=sys)", cMs);
+ }
+ if (fGip)
+ {
+ RTTestValueF(hTest, cNsMin, RTTESTUNIT_NS, "%u ms min (clock=gip)", cMs);
+ RTTestValueF(hTest, cNsTotal / cLoops, RTTESTUNIT_NS, "%u ms avg (clock=gip)", cMs);
+ }
+ }
+
+ RTTESTI_CHECK_RC(SUPSemEventClose(pSession, hEvent), VINF_OBJECT_DESTROYED);
+ RTTestValueF(hTest, cInterrupted, RTTESTUNIT_OCCURRENCES, "VERR_INTERRUPTED returned");
+ }
+
+ if (RTTestErrorCount(hTest) == 0)
+ {
+ RTTestSub(hTest, "MRE Timeout Accuracy (ms)");
+ RTTESTI_CHECK_RC(SUPSemEventMultiCreate(pSession, &hEvent), VINF_SUCCESS);
+
+ uint32_t cInterrupted = 0;
+ for (unsigned i = 0; i < RT_ELEMENTS(s_acMsIntervals); i++)
+ {
+ uint64_t cMs = s_acMsIntervals[i];
+ uint64_t cNsMinSys = UINT64_MAX;
+ uint64_t cNsMin = UINT64_MAX;
+ uint64_t cNsTotalSys= 0;
+ uint64_t cNsTotal = 0;
+ unsigned cLoops = 0;
+ while (cLoops < LOOP_COUNT)
+ {
+ uint64_t u64StartSys = RTTimeSystemNanoTS();
+ uint64_t u64Start = RTTimeNanoTS();
+ int rcX = SUPSemEventMultiWaitNoResume(pSession, hEvent, cMs);
+ uint64_t cNsElapsedSys = RTTimeSystemNanoTS() - u64StartSys;
+ uint64_t cNsElapsed = RTTimeNanoTS() - u64Start;
+
+ if (rcX == VERR_INTERRUPTED)
+ {
+ cInterrupted++;
+ continue; /* retry */
+ }
+ if (rcX != VERR_TIMEOUT)
+ RTTestFailed(hTest, "%Rrc cLoops=%u cMs=%u", rcX, cLoops, cMs);
+
+ if (cNsElapsedSys < cNsMinSys)
+ cNsMinSys = cNsElapsedSys;
+ if (cNsElapsed < cNsMin)
+ cNsMin = cNsElapsed;
+ cNsTotalSys += cNsElapsedSys;
+ cNsTotal += cNsElapsed;
+ cLoops++;
+ }
+ if (fSys)
+ {
+ RTTestValueF(hTest, cNsMinSys, RTTESTUNIT_NS, "%u ms min (clock=sys)", cMs);
+ RTTestValueF(hTest, cNsTotalSys / cLoops, RTTESTUNIT_NS, "%u ms avg (clock=sys)", cMs);
+ }
+ if (fGip)
+ {
+ RTTestValueF(hTest, cNsMin, RTTESTUNIT_NS, "%u ms min (clock=gip)", cMs);
+ RTTestValueF(hTest, cNsTotal / cLoops, RTTESTUNIT_NS, "%u ms avg (clock=gip)", cMs);
+ }
+ }
+
+ RTTESTI_CHECK_RC(SUPSemEventMultiClose(pSession, hEvent), VINF_OBJECT_DESTROYED);
+ RTTestValueF(hTest, cInterrupted, RTTESTUNIT_OCCURRENCES, "VERR_INTERRUPTED returned");
+ }
+ }
+
+ {
+ static uint32_t const s_acNsIntervals[] =
+ {
+ 0, 1000, 5000, 15000, 30000, 50000, 100000, 250000, 500000, 750000, 900000, 1500000, 2200000
+ };
+
+ if (RTTestErrorCount(hTest) == 0)
+ {
+ RTTestSub(hTest, "SUPSemEventWaitNsRelIntr Accuracy");
+ RTTestValueF(hTest, SUPSemEventGetResolution(pSession), RTTESTUNIT_NS, "SRE resolution");
+ RTTESTI_CHECK_RC(SUPSemEventCreate(pSession, &hEvent), VINF_SUCCESS);
+
+ uint32_t cInterrupted = 0;
+ for (unsigned i = 0; i < RT_ELEMENTS(s_acNsIntervals); i++)
+ {
+ uint64_t cNs = s_acNsIntervals[i];
+ uint64_t cNsMinSys = UINT64_MAX;
+ uint64_t cNsMin = UINT64_MAX;
+ uint64_t cNsTotalSys= 0;
+ uint64_t cNsTotal = 0;
+ unsigned cLoops = 0;
+ while (cLoops < LOOP_COUNT)
+ {
+ uint64_t u64StartSys = RTTimeSystemNanoTS();
+ uint64_t u64Start = RTTimeNanoTS();
+ int rcX = SUPSemEventWaitNsRelIntr(pSession, hEvent, cNs);
+ uint64_t cNsElapsedSys = RTTimeSystemNanoTS() - u64StartSys;
+ uint64_t cNsElapsed = RTTimeNanoTS() - u64Start;
+
+ if (rcX == VERR_INTERRUPTED)
+ {
+ cInterrupted++;
+ continue; /* retry */
+ }
+ if (rcX != VERR_TIMEOUT)
+ RTTestFailed(hTest, "%Rrc cLoops=%u cNs=%u", rcX, cLoops, cNs);
+
+ if (cNsElapsedSys < cNsMinSys)
+ cNsMinSys = cNsElapsedSys;
+ if (cNsElapsed < cNsMin)
+ cNsMin = cNsElapsed;
+ cNsTotalSys += cNsElapsedSys;
+ cNsTotal += cNsElapsed;
+ cLoops++;
+ }
+ if (fSys)
+ {
+ RTTestValueF(hTest, cNsMinSys, RTTESTUNIT_NS, "%'u ns min (clock=sys)", cNs);
+ RTTestValueF(hTest, cNsTotalSys / cLoops, RTTESTUNIT_NS, "%'u ns avg (clock=sys)", cNs);
+ }
+ if (fGip)
+ {
+ RTTestValueF(hTest, cNsMin, RTTESTUNIT_NS, "%'u ns min (clock=gip)", cNs);
+ RTTestValueF(hTest, cNsTotal / cLoops, RTTESTUNIT_NS, "%'u ns avg (clock=gip)", cNs);
+ }
+ }
+
+ RTTESTI_CHECK_RC(SUPSemEventClose(pSession, hEvent), VINF_OBJECT_DESTROYED);
+ RTTestValueF(hTest, cInterrupted, RTTESTUNIT_OCCURRENCES, "VERR_INTERRUPTED returned");
+ }
+
+ if (RTTestErrorCount(hTest) == 0)
+ {
+ RTTestSub(hTest, "SUPSemEventMultiWaitNsRelIntr Accuracy");
+ RTTestValueF(hTest, SUPSemEventMultiGetResolution(pSession), RTTESTUNIT_NS, "MRE resolution");
+ RTTESTI_CHECK_RC(SUPSemEventMultiCreate(pSession, &hEvent), VINF_SUCCESS);
+
+ uint32_t cInterrupted = 0;
+ for (unsigned i = 0; i < RT_ELEMENTS(s_acNsIntervals); i++)
+ {
+ uint64_t cNs = s_acNsIntervals[i];
+ uint64_t cNsMinSys = UINT64_MAX;
+ uint64_t cNsMin = UINT64_MAX;
+ uint64_t cNsTotalSys= 0;
+ uint64_t cNsTotal = 0;
+ unsigned cLoops = 0;
+ while (cLoops < LOOP_COUNT)
+ {
+ uint64_t u64StartSys = RTTimeSystemNanoTS();
+ uint64_t u64Start = RTTimeNanoTS();
+ int rcX = SUPSemEventMultiWaitNsRelIntr(pSession, hEvent, cNs);
+ uint64_t cNsElapsedSys = RTTimeSystemNanoTS() - u64StartSys;
+ uint64_t cNsElapsed = RTTimeNanoTS() - u64Start;
+
+ if (rcX == VERR_INTERRUPTED)
+ {
+ cInterrupted++;
+ continue; /* retry */
+ }
+ if (rcX != VERR_TIMEOUT)
+ RTTestFailed(hTest, "%Rrc cLoops=%u cNs=%u", rcX, cLoops, cNs);
+
+ if (cNsElapsedSys < cNsMinSys)
+ cNsMinSys = cNsElapsedSys;
+ if (cNsElapsed < cNsMin)
+ cNsMin = cNsElapsed;
+ cNsTotalSys += cNsElapsedSys;
+ cNsTotal += cNsElapsed;
+ cLoops++;
+ }
+ if (fSys)
+ {
+ RTTestValueF(hTest, cNsMinSys, RTTESTUNIT_NS, "%'u ns min (clock=sys)", cNs);
+ RTTestValueF(hTest, cNsTotalSys / cLoops, RTTESTUNIT_NS, "%'u ns avg (clock=sys)", cNs);
+ }
+ if (fGip)
+ {
+ RTTestValueF(hTest, cNsMin, RTTESTUNIT_NS, "%'u ns min (clock=gip)", cNs);
+ RTTestValueF(hTest, cNsTotal / cLoops, RTTESTUNIT_NS, "%'u ns avg (clock=gip)", cNs);
+ }
+ }
+
+ RTTESTI_CHECK_RC(SUPSemEventMultiClose(pSession, hEvent), VINF_OBJECT_DESTROYED);
+ RTTestValueF(hTest, cInterrupted, RTTESTUNIT_OCCURRENCES, "VERR_INTERRUPTED returned");
+ }
+
+ if (RTTestErrorCount(hTest) == 0)
+ {
+ RTTestSub(hTest, "SUPSemEventWaitNsAbsIntr Accuracy");
+ RTTestValueF(hTest, SUPSemEventGetResolution(pSession), RTTESTUNIT_NS, "MRE resolution");
+ RTTESTI_CHECK_RC(SUPSemEventCreate(pSession, &hEvent), VINF_SUCCESS);
+
+ uint32_t cInterrupted = 0;
+ for (unsigned i = 0; i < RT_ELEMENTS(s_acNsIntervals); i++)
+ {
+ uint64_t cNs = s_acNsIntervals[i];
+ uint64_t cNsMinSys = UINT64_MAX;
+ uint64_t cNsMin = UINT64_MAX;
+ uint64_t cNsTotalSys= 0;
+ uint64_t cNsTotal = 0;
+ unsigned cLoops = 0;
+ while (cLoops < LOOP_COUNT)
+ {
+ uint64_t u64StartSys = RTTimeSystemNanoTS();
+ uint64_t u64Start = RTTimeNanoTS();
+ uint64_t uAbsDeadline = (fGip ? u64Start : u64StartSys) + cNs;
+ int rcX = SUPSemEventWaitNsAbsIntr(pSession, hEvent, uAbsDeadline);
+ uint64_t cNsElapsedSys = RTTimeSystemNanoTS() - u64StartSys;
+ uint64_t cNsElapsed = RTTimeNanoTS() - u64Start;
+
+ if (rcX == VERR_INTERRUPTED)
+ {
+ cInterrupted++;
+ continue; /* retry */
+ }
+ if (rcX != VERR_TIMEOUT)
+ RTTestFailed(hTest, "%Rrc cLoops=%u cNs=%u", rcX, cLoops, cNs);
+
+ if (cNsElapsedSys < cNsMinSys)
+ cNsMinSys = cNsElapsedSys;
+ if (cNsElapsed < cNsMin)
+ cNsMin = cNsElapsed;
+ cNsTotalSys += cNsElapsedSys;
+ cNsTotal += cNsElapsed;
+ cLoops++;
+ }
+ if (fSys)
+ {
+ RTTestValueF(hTest, cNsMinSys, RTTESTUNIT_NS, "%'u ns min (clock=sys)", cNs);
+ RTTestValueF(hTest, cNsTotalSys / cLoops, RTTESTUNIT_NS, "%'u ns avg (clock=sys)", cNs);
+ }
+ if (fGip)
+ {
+ RTTestValueF(hTest, cNsMin, RTTESTUNIT_NS, "%'u ns min (clock=gip)", cNs);
+ RTTestValueF(hTest, cNsTotal / cLoops, RTTESTUNIT_NS, "%'u ns avg (clock=gip)", cNs);
+ }
+ }
+
+ RTTESTI_CHECK_RC(SUPSemEventClose(pSession, hEvent), VINF_OBJECT_DESTROYED);
+ RTTestValueF(hTest, cInterrupted, RTTESTUNIT_OCCURRENCES, "VERR_INTERRUPTED returned");
+ }
+
+
+ if (RTTestErrorCount(hTest) == 0)
+ {
+ RTTestSub(hTest, "SUPSemEventMultiWaitNsAbsIntr Accuracy");
+ RTTestValueF(hTest, SUPSemEventMultiGetResolution(pSession), RTTESTUNIT_NS, "MRE resolution");
+ RTTESTI_CHECK_RC(SUPSemEventMultiCreate(pSession, &hEvent), VINF_SUCCESS);
+
+ uint32_t cInterrupted = 0;
+ for (unsigned i = 0; i < RT_ELEMENTS(s_acNsIntervals); i++)
+ {
+ uint64_t cNs = s_acNsIntervals[i];
+ uint64_t cNsMinSys = UINT64_MAX;
+ uint64_t cNsMin = UINT64_MAX;
+ uint64_t cNsTotalSys= 0;
+ uint64_t cNsTotal = 0;
+ unsigned cLoops = 0;
+ while (cLoops < LOOP_COUNT)
+ {
+ uint64_t u64StartSys = RTTimeSystemNanoTS();
+ uint64_t u64Start = RTTimeNanoTS();
+ uint64_t uAbsDeadline = (fGip ? u64Start : u64StartSys) + cNs;
+ int rcX = SUPSemEventMultiWaitNsAbsIntr(pSession, hEvent, uAbsDeadline);
+ uint64_t cNsElapsedSys = RTTimeSystemNanoTS() - u64StartSys;
+ uint64_t cNsElapsed = RTTimeNanoTS() - u64Start;
+
+ if (rcX == VERR_INTERRUPTED)
+ {
+ cInterrupted++;
+ continue; /* retry */
+ }
+ if (rcX != VERR_TIMEOUT)
+ RTTestFailed(hTest, "%Rrc cLoops=%u cNs=%u", rcX, cLoops, cNs);
+
+ if (cNsElapsedSys < cNsMinSys)
+ cNsMinSys = cNsElapsedSys;
+ if (cNsElapsed < cNsMin)
+ cNsMin = cNsElapsed;
+ cNsTotalSys += cNsElapsedSys;
+ cNsTotal += cNsElapsed;
+ cLoops++;
+ }
+ if (fSys)
+ {
+ RTTestValueF(hTest, cNsMinSys, RTTESTUNIT_NS, "%'u ns min (clock=sys)", cNs);
+ RTTestValueF(hTest, cNsTotalSys / cLoops, RTTESTUNIT_NS, "%'u ns avg (clock=sys)", cNs);
+ }
+ if (fGip)
+ {
+ RTTestValueF(hTest, cNsMin, RTTESTUNIT_NS, "%'u ns min (clock=gip)", cNs);
+ RTTestValueF(hTest, cNsTotal / cLoops, RTTESTUNIT_NS, "%'u ns avg (clock=gip)", cNs);
+ }
+ }
+
+ RTTESTI_CHECK_RC(SUPSemEventMultiClose(pSession, hEvent), VINF_OBJECT_DESTROYED);
+ RTTestValueF(hTest, cInterrupted, RTTESTUNIT_OCCURRENCES, "VERR_INTERRUPTED returned");
+ }
+
+ }
+
+
+ /*
+ * Done.
+ */
+ return RTTestSummaryAndDestroy(hTest);
+}
+
diff --git a/src/VBox/HostDrivers/Support/testcase/tstSupTscDelta.cpp b/src/VBox/HostDrivers/Support/testcase/tstSupTscDelta.cpp
new file mode 100644
index 00000000..4fe0d8d4
--- /dev/null
+++ b/src/VBox/HostDrivers/Support/testcase/tstSupTscDelta.cpp
@@ -0,0 +1,235 @@
+/* $Id: tstSupTscDelta.cpp $ */
+/** @file
+ * SUP Testcase - Global Info Page TSC Delta Measurement Utility.
+ */
+
+/*
+ * Copyright (C) 2015-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include <VBox/sup.h>
+#include <iprt/errcore.h>
+#include <iprt/assert.h>
+#include <iprt/stream.h>
+#include <iprt/string.h>
+#include <iprt/getopt.h>
+#include <iprt/test.h>
+#include <iprt/thread.h>
+
+
+
+int main(int argc, char **argv)
+{
+ RTTEST hTest;
+ RTEXITCODE rcExit = RTTestInitExAndCreate(argc, &argv, 0 /*fRtInit*/, "tstSupTscDelta", &hTest);
+ if (rcExit != RTEXITCODE_SUCCESS)
+ return rcExit;
+
+ /*
+ * Parse args
+ */
+ static const RTGETOPTDEF g_aOptions[] =
+ {
+ { "--iterations", 'i', RTGETOPT_REQ_INT32 },
+ { "--delay", 'd', RTGETOPT_REQ_INT32 },
+ };
+
+ uint32_t cIterations = 0; /* Currently 0 so that it doesn't upset testing. */
+ uint32_t cMsSleepBetweenIterations = 10;
+
+ int ch;
+ RTGETOPTUNION ValueUnion;
+ RTGETOPTSTATE GetState;
+ RTGetOptInit(&GetState, argc, argv, g_aOptions, RT_ELEMENTS(g_aOptions), 1, RTGETOPTINIT_FLAGS_NO_STD_OPTS);
+ while ((ch = RTGetOpt(&GetState, &ValueUnion)))
+ {
+ switch (ch)
+ {
+ case 'd':
+ cMsSleepBetweenIterations = ValueUnion.u32;
+ break;
+ case 'i':
+ cIterations = ValueUnion.u32;
+ break;
+
+ default:
+ return RTGetOptPrintError(ch, &ValueUnion);
+ }
+ }
+ if (!cIterations)
+ return RTTestSkipAndDestroy(hTest, "Nothing to do. The --iterations argument is 0 or not given.");
+
+ /*
+ * Init
+ */
+ PSUPDRVSESSION pSession = NIL_RTR0PTR;
+ int rc = SUPR3Init(&pSession);
+ if (RT_SUCCESS(rc))
+ {
+ PSUPGLOBALINFOPAGE pGip = g_pSUPGlobalInfoPage;
+ if (pGip)
+ {
+ if (pGip->enmUseTscDelta < SUPGIPUSETSCDELTA_PRACTICALLY_ZERO)
+ return RTTestSkipAndDestroy(hTest, "No deltas to play with: enmUseTscDelta=%d\n", pGip->enmUseTscDelta);
+
+ /*
+ * Init stats.
+ */
+ struct
+ {
+ int64_t iLowest;
+ int64_t iHighest;
+ int64_t iTotal;
+ uint64_t uAbsMin;
+ uint64_t uAbsMax;
+ uint64_t uAbsTotal;
+ } aCpuStats[RTCPUSET_MAX_CPUS];
+ RT_ZERO(aCpuStats);
+ for (uint32_t i = 0; i < pGip->cCpus; i++)
+ {
+ aCpuStats[i].iLowest = INT64_MAX;
+ aCpuStats[i].iHighest = INT64_MIN;
+ aCpuStats[i].uAbsMin = UINT64_MAX;
+ }
+
+ /*
+ * Do the work.
+ */
+ for (uint32_t iIteration = 0; ; iIteration++)
+ {
+ /*
+ * Display the current deltas and gather statistics.
+ */
+ RTPrintf("tstSupTscDelta: Iteration #%u results:", iIteration);
+ for (uint32_t iCpu = 0; iCpu < pGip->cCpus; iCpu++)
+ {
+ int64_t iTscDelta = pGip->aCPUs[iCpu].i64TSCDelta;
+
+ /* print */
+ if ((iCpu % 4) == 0)
+ RTPrintf("\ntstSupTscDelta:");
+ if (pGip->aCPUs[iCpu].enmState != SUPGIPCPUSTATE_ONLINE)
+ RTPrintf(" %02x: offline ", iCpu);
+ else if (iTscDelta != INT64_MAX)
+ RTPrintf(" %02x: %-12lld", iCpu, iTscDelta);
+ else
+ RTPrintf(" %02x: INT64_MAX ", iCpu);
+
+ /* stats */
+ if ( iTscDelta != INT64_MAX
+ && pGip->aCPUs[iCpu].enmState == SUPGIPCPUSTATE_ONLINE)
+ {
+ if (aCpuStats[iCpu].iLowest > iTscDelta)
+ aCpuStats[iCpu].iLowest = iTscDelta;
+ if (aCpuStats[iCpu].iHighest < iTscDelta)
+ aCpuStats[iCpu].iHighest = iTscDelta;
+ aCpuStats[iCpu].iTotal += iTscDelta;
+
+ uint64_t uAbsTscDelta = iTscDelta >= 0 ? (uint64_t)iTscDelta : (uint64_t)-iTscDelta;
+ if (aCpuStats[iCpu].uAbsMin > uAbsTscDelta)
+ aCpuStats[iCpu].uAbsMin = uAbsTscDelta;
+ if (aCpuStats[iCpu].uAbsMax < uAbsTscDelta)
+ aCpuStats[iCpu].uAbsMax = uAbsTscDelta;
+ aCpuStats[iCpu].uAbsTotal += uAbsTscDelta;
+ }
+ }
+ if (((pGip->cCpus - 1) % 4) != 0)
+ RTPrintf("\n");
+
+ /*
+ * Done?
+ */
+ if (iIteration + 1 >= cIterations)
+ break;
+
+ /*
+ * Force a new measurement.
+ */
+ RTThreadSleep(cMsSleepBetweenIterations);
+ for (uint32_t iCpu = 0; iCpu < pGip->cCpus; iCpu++)
+ if (pGip->aCPUs[iCpu].enmState == SUPGIPCPUSTATE_ONLINE)
+ {
+ rc = SUPR3TscDeltaMeasure(pGip->aCPUs[iCpu].idCpu, false /*fAsync*/, true /*fForce*/, 64, 16 /*ms*/);
+ if (RT_FAILURE(rc))
+ RTTestFailed(hTest, "SUPR3TscDeltaMeasure failed on %#x: %Rrc", pGip->aCPUs[iCpu].idCpu, rc);
+ }
+ }
+
+ /*
+ * Display statistics that we've gathered.
+ */
+ RTPrintf("tstSupTscDelta: Results:\n");
+ int64_t iLowest = INT64_MAX;
+ int64_t iHighest = INT64_MIN;
+ int64_t iTotal = 0;
+ uint32_t cTotal = 0;
+ for (uint32_t iCpu = 0; iCpu < pGip->cCpus; iCpu++)
+ {
+ if (pGip->aCPUs[iCpu].enmState != SUPGIPCPUSTATE_ONLINE)
+ RTPrintf("tstSupTscDelta: %02x: offline\n", iCpu);
+ else
+ {
+ RTPrintf("tstSupTscDelta: %02x: lowest=%-12lld highest=%-12lld average=%-12lld spread=%-12lld\n",
+ iCpu,
+ aCpuStats[iCpu].iLowest,
+ aCpuStats[iCpu].iHighest,
+ aCpuStats[iCpu].iTotal / cIterations,
+ aCpuStats[iCpu].iHighest - aCpuStats[iCpu].iLowest);
+ RTPrintf( "tstSupTscDelta: absmin=%-12llu absmax=%-12llu absavg=%-12llu idCpu=%#4x idApic=%#4x\n",
+ aCpuStats[iCpu].uAbsMin,
+ aCpuStats[iCpu].uAbsMax,
+ aCpuStats[iCpu].uAbsTotal / cIterations,
+ pGip->aCPUs[iCpu].idCpu,
+ pGip->aCPUs[iCpu].idApic);
+ if (iLowest > aCpuStats[iCpu].iLowest)
+ iLowest = aCpuStats[iCpu].iLowest;
+ if (iHighest < aCpuStats[iCpu].iHighest)
+ iHighest = aCpuStats[iCpu].iHighest;
+ iTotal += aCpuStats[iCpu].iHighest;
+ cTotal += cIterations;
+ }
+ }
+ RTPrintf("tstSupTscDelta: all: lowest=%-12lld highest=%-12lld average=%-12lld spread=%-12lld\n",
+ iLowest, iHighest, iTotal / cTotal, iHighest - iLowest);
+ }
+ else
+ RTTestFailed(hTest, "g_pSUPGlobalInfoPage is NULL");
+
+ SUPR3Term(false /*fForced*/);
+ }
+ else
+ RTTestFailed(hTest, "SUPR3Init failed: %Rrc", rc);
+ return RTTestSummaryAndDestroy(hTest);
+}
+
diff --git a/src/VBox/HostDrivers/Support/testcase/tstSupVerify.cpp b/src/VBox/HostDrivers/Support/testcase/tstSupVerify.cpp
new file mode 100644
index 00000000..b8f157d8
--- /dev/null
+++ b/src/VBox/HostDrivers/Support/testcase/tstSupVerify.cpp
@@ -0,0 +1,162 @@
+/* $Id: tstSupVerify.cpp $ */
+/** @file
+ * SUP Testcase - Test SUPR3HardenedVerifyPlugIn.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include <VBox/sup.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/stream.h>
+
+
+//#define DYNAMIC
+#ifdef DYNAMIC
+# include <iprt/win/windows.h>
+
+# define DYNAMIC_IMPORTS() \
+ ONE_IMPORT(RTR3InitExe); \
+ ONE_IMPORT(RTMsgInitFailure); \
+ ONE_IMPORT(RTGetOpt); \
+ ONE_IMPORT(RTGetOptInit); \
+ ONE_IMPORT(RTGetOptPrintError); \
+ ONE_IMPORT(RTMsgError); \
+ ONE_IMPORT(RTMsgErrorExit); \
+ ONE_IMPORT(RTMsgInfo); \
+ ONE_IMPORT(RTPrintf); \
+ ONE_IMPORT(SUPR3HardenedVerifyInit); \
+ ONE_IMPORT(SUPR3HardenedVerifyPlugIn)
+
+# define ONE_IMPORT(a_fnName) static decltype(a_fnName) *g_pfn##a_fnName
+DYNAMIC_IMPORTS();
+# undef ONE_IMPORT
+
+static void resolve(void)
+{
+ HMODULE hmod = LoadLibrary("VBoxRT.dll");
+ DWORD cbWritten = 0;
+
+# define ONE_IMPORT(a_fnName) do { \
+ g_pfn##a_fnName = (decltype(a_fnName) *)GetProcAddress(hmod, #a_fnName); \
+ if (!g_pfn##a_fnName) \
+ WriteFile(GetStdHandle(STD_ERROR_HANDLE), RT_STR_TUPLE("Failed to resolve: " #a_fnName "\r\n"), &cbWritten, NULL); \
+ } while (0)
+ DYNAMIC_IMPORTS();
+# undef ONE_IMPORT
+}
+
+#define RTR3InitExe g_pfnRTR3InitExe
+#define RTMsgInitFailure g_pfnRTMsgInitFailure
+#define RTGetOpt g_pfnRTGetOpt
+#define RTGetOptInit g_pfnRTGetOptInit
+#define RTGetOptPrintError g_pfnRTGetOptPrintError
+#define RTMsgError g_pfnRTMsgError
+#define RTMsgErrorExit g_pfnRTMsgErrorExit
+#define RTMsgInfo g_pfnRTMsgInfo
+#define RTPrintf g_pfnRTPrintf
+#define SUPR3HardenedVerifyInit g_pfnSUPR3HardenedVerifyInit
+#define SUPR3HardenedVerifyPlugIn g_pfnSUPR3HardenedVerifyPlugIn
+
+#endif /* DYNAMIC */
+
+int main(int argc, char **argv)
+{
+ /*
+ * Init.
+ */
+#ifdef DYNAMIC
+ resolve();
+#endif
+ int rc = RTR3InitExe(argc, &argv, 0);
+ if (RT_FAILURE(rc))
+ return RTMsgInitFailure(rc);
+ rc = SUPR3HardenedVerifyInit();
+ if (RT_FAILURE(rc))
+ return RTMsgErrorExit(RTEXITCODE_FAILURE, "SUPR3HardenedVerifyInit failed: %Rrc", rc);
+
+ /*
+ * Process arguments.
+ */
+ static const RTGETOPTDEF s_aOptions[] =
+ {
+ { "--dummy", 'd', RTGETOPT_REQ_NOTHING },
+ };
+
+ //bool fKeepLoaded = false;
+
+ int ch;
+ RTGETOPTUNION ValueUnion;
+ RTGETOPTSTATE GetState;
+ RTGetOptInit(&GetState, argc, argv, s_aOptions, RT_ELEMENTS(s_aOptions), 1, 0);
+ while ((ch = RTGetOpt(&GetState, &ValueUnion)))
+ {
+ switch (ch)
+ {
+ case VINF_GETOPT_NOT_OPTION:
+ {
+ RTERRINFOSTATIC ErrInfo;
+ RTErrInfoInitStatic(&ErrInfo);
+ rc = SUPR3HardenedVerifyPlugIn(ValueUnion.psz, &ErrInfo.Core);
+ if (RT_SUCCESS(rc))
+ RTMsgInfo("SUPR3HardenedVerifyPlugIn: %Rrc for '%s'\n", rc, ValueUnion.psz);
+ else
+ RTMsgError("SUPR3HardenedVerifyPlugIn: %Rrc for '%s' ErrInfo: %s\n",
+ rc, ValueUnion.psz, ErrInfo.Core.pszMsg);
+ break;
+ }
+
+ case 'h':
+ RTPrintf("%s [dll1 [dll2...]]\n", argv[0]);
+ return 1;
+
+ case 'V':
+ RTPrintf("$Revision: 155244 $\n");
+ return 0;
+
+ default:
+ return RTGetOptPrintError(ch, &ValueUnion);
+ }
+ }
+
+ return 0;
+}
+
diff --git a/src/VBox/HostDrivers/Support/win/Makefile.kup b/src/VBox/HostDrivers/Support/win/Makefile.kup
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/src/VBox/HostDrivers/Support/win/Makefile.kup
diff --git a/src/VBox/HostDrivers/Support/win/SUPDrv-win.cpp b/src/VBox/HostDrivers/Support/win/SUPDrv-win.cpp
new file mode 100644
index 00000000..369e68c0
--- /dev/null
+++ b/src/VBox/HostDrivers/Support/win/SUPDrv-win.cpp
@@ -0,0 +1,5719 @@
+/* $Id: SUPDrv-win.cpp $ */
+/** @file
+ * VBoxDrv - The VirtualBox Support Driver - Windows NT specifics.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#ifndef IPRT_NT_MAP_TO_ZW
+# define IPRT_NT_MAP_TO_ZW
+#endif
+#define LOG_GROUP LOG_GROUP_SUP_DRV
+#include "../SUPDrvInternal.h"
+#include <excpt.h>
+#include <ntimage.h>
+
+#include <iprt/assert.h>
+#include <iprt/avl.h>
+#include <iprt/ctype.h>
+#include <iprt/initterm.h>
+#include <iprt/mem.h>
+#include <iprt/process.h>
+#include <iprt/power.h>
+#include <iprt/rand.h>
+#include <iprt/semaphore.h>
+#include <iprt/spinlock.h>
+#include <iprt/string.h>
+#include <iprt/utf16.h>
+#include <iprt/x86.h>
+#include <VBox/log.h>
+#include <VBox/err.h>
+
+#include <iprt/asm-amd64-x86.h>
+
+#ifdef VBOX_WITH_HARDENING
+# include "SUPHardenedVerify-win.h"
+#endif
+
+
+/*********************************************************************************************************************************
+* Defined Constants And Macros *
+*********************************************************************************************************************************/
+/** The support service name. */
+#define SERVICE_NAME "VBoxDrv"
+/** The Pool tag (VBox). */
+#define SUPDRV_NT_POOL_TAG 'xoBV'
+
+/** NT device name for user access. */
+#define DEVICE_NAME_NT_USR L"\\Device\\VBoxDrvU"
+#ifdef VBOX_WITH_HARDENING
+/** Macro for checking for deflecting calls to the stub device. */
+# define VBOXDRV_COMPLETE_IRP_AND_RETURN_IF_STUB_DEV(a_pDevObj, a_pIrp) \
+ do { if ((a_pDevObj) == g_pDevObjStub) \
+ return supdrvNtCompleteRequest(STATUS_ACCESS_DENIED, a_pIrp); \
+ } while (0)
+/** Macro for checking for deflecting calls to the stub and error info
+ * devices. */
+# define VBOXDRV_COMPLETE_IRP_AND_RETURN_IF_STUB_OR_ERROR_INFO_DEV(a_pDevObj, a_pIrp) \
+ do { if ((a_pDevObj) == g_pDevObjStub || (a_pDevObj) == g_pDevObjErrorInfo) \
+ return supdrvNtCompleteRequest(STATUS_ACCESS_DENIED, a_pIrp); \
+ } while (0)
+#else
+# define VBOXDRV_COMPLETE_IRP_AND_RETURN_IF_STUB_DEV(a_pDevObj, a_pIrp) do {} while (0)
+# define VBOXDRV_COMPLETE_IRP_AND_RETURN_IF_STUB_OR_ERROR_INFO_DEV(a_pDevObj, a_pIrp) do {} while (0)
+#endif
+
+/** Enables the fast I/O control code path. */
+#define VBOXDRV_WITH_FAST_IO
+
+/** Enables generating UID from NT SIDs so the GMM can share free memory
+ * among VMs running as the same user. */
+#define VBOXDRV_WITH_SID_TO_UID_MAPPING
+
+/* Missing if we're compiling against older WDKs. */
+#ifndef NonPagedPoolNx
+# define NonPagedPoolNx ((POOL_TYPE)512)
+#endif
+
+
+/*********************************************************************************************************************************
+* Structures and Typedefs *
+*********************************************************************************************************************************/
+#ifdef VBOXDRV_WITH_SID_TO_UID_MAPPING
+/**
+ * SID to User ID mapping.
+ *
+ * This is used to generate a RTUID value for a NT security identifier.
+ * Normally, the UID is the hash of the SID string, but due to collisions it may
+ * differ. See g_NtUserIdHashTree and g_NtUserIdUidTree.
+ */
+typedef struct SUPDRVNTUSERID
+{
+ /** Hash tree node, key: RTStrHash1 of szSid. */
+ AVLLU32NODECORE HashCore;
+ /** UID three node, key: The UID. */
+ AVLU32NODECORE UidCore;
+ /** Reference counter. */
+ uint32_t volatile cRefs;
+ /** The length of the SID string. */
+ uint16_t cchSid;
+ /** The SID string for the user. */
+ char szSid[RT_FLEXIBLE_ARRAY];
+} SUPDRVNTUSERID;
+/** Pointer to a SID to UID mapping. */
+typedef SUPDRVNTUSERID *PSUPDRVNTUSERID;
+#endif
+
+/**
+ * Device extension used by VBoxDrvU.
+ */
+typedef struct SUPDRVDEVEXTUSR
+{
+ /** Global cookie (same location as in SUPDRVDEVEXT, different value). */
+ uint32_t u32Cookie;
+ /** Pointer to the main driver extension. */
+ PSUPDRVDEVEXT pMainDrvExt;
+} SUPDRVDEVEXTUSR;
+AssertCompileMembersAtSameOffset(SUPDRVDEVEXT, u32Cookie, SUPDRVDEVEXTUSR, u32Cookie);
+/** Pointer to the VBoxDrvU device extension. */
+typedef SUPDRVDEVEXTUSR *PSUPDRVDEVEXTUSR;
+/** Value of SUPDRVDEVEXTUSR::u32Cookie. */
+#define SUPDRVDEVEXTUSR_COOKIE UINT32_C(0x12345678)
+
+/** Get the main device extension. */
+#define SUPDRVNT_GET_DEVEXT(pDevObj) \
+ ( pDevObj != g_pDevObjUsr \
+ ? (PSUPDRVDEVEXT)pDevObj->DeviceExtension \
+ : ((PSUPDRVDEVEXTUSR)pDevObj->DeviceExtension)->pMainDrvExt )
+
+#ifdef VBOX_WITH_HARDENING
+
+/**
+ * Device extension used by VBoxDrvStub.
+ */
+typedef struct SUPDRVDEVEXTSTUB
+{
+ /** Common header. */
+ SUPDRVDEVEXTUSR Common;
+} SUPDRVDEVEXTSTUB;
+/** Pointer to the VBoxDrvStub device extension. */
+typedef SUPDRVDEVEXTSTUB *PSUPDRVDEVEXTSTUB;
+/** Value of SUPDRVDEVEXTSTUB::Common.u32Cookie. */
+#define SUPDRVDEVEXTSTUB_COOKIE UINT32_C(0x90abcdef)
+
+
+/**
+ * Device extension used by VBoxDrvErrorInfo.
+ */
+typedef struct SUPDRVDEVEXTERRORINFO
+{
+ /** Common header. */
+ SUPDRVDEVEXTUSR Common;
+} SUPDRVDEVEXTERRORINFO;
+/** Pointer to the VBoxDrvErrorInfo device extension. */
+typedef SUPDRVDEVEXTERRORINFO *PSUPDRVDEVEXTERRORINFO;
+/** Value of SUPDRVDEVEXTERRORINFO::Common.u32Cookie. */
+#define SUPDRVDEVEXTERRORINFO_COOKIE UINT32_C(0xBadC0ca0)
+
+/**
+ * Error info for a failed VBoxDrv or VBoxDrvStub open attempt.
+ */
+typedef struct SUPDRVNTERRORINFO
+{
+ /** The list entry (in g_ErrorInfoHead). */
+ RTLISTNODE ListEntry;
+ /** The ID of the process this error info belongs to. */
+ HANDLE hProcessId;
+ /** The ID of the thread owning this info. */
+ HANDLE hThreadId;
+ /** Milliseconds createion timestamp (for cleaning up). */
+ uint64_t uCreatedMsTs;
+ /** Number of bytes of valid info. */
+ uint32_t cchErrorInfo;
+ /** The error info. */
+ char szErrorInfo[16384 - sizeof(RTLISTNODE) - sizeof(HANDLE)*2 - sizeof(uint64_t) - sizeof(uint32_t) - 0x20];
+} SUPDRVNTERRORINFO;
+/** Pointer to error info. */
+typedef SUPDRVNTERRORINFO *PSUPDRVNTERRORINFO;
+
+
+/**
+ * The kind of process we're protecting.
+ */
+typedef enum SUPDRVNTPROTECTKIND
+{
+ kSupDrvNtProtectKind_Invalid = 0,
+
+ /** Stub process protection while performing process verification.
+ * Next: StubSpawning (or free) */
+ kSupDrvNtProtectKind_StubUnverified,
+ /** Stub process protection before it creates the VM process.
+ * Next: StubParent, StubDead. */
+ kSupDrvNtProtectKind_StubSpawning,
+ /** Stub process protection while having a VM process as child.
+ * Next: StubDead */
+ kSupDrvNtProtectKind_StubParent,
+ /** Dead stub process. */
+ kSupDrvNtProtectKind_StubDead,
+
+ /** Potential VM process.
+ * Next: VmProcessConfirmed, VmProcessDead. */
+ kSupDrvNtProtectKind_VmProcessUnconfirmed,
+ /** Confirmed VM process.
+ * Next: VmProcessDead. */
+ kSupDrvNtProtectKind_VmProcessConfirmed,
+ /** Dead VM process. */
+ kSupDrvNtProtectKind_VmProcessDead,
+
+ /** End of valid protection kinds. */
+ kSupDrvNtProtectKind_End
+} SUPDRVNTPROTECTKIND;
+
+/**
+ * A NT process protection structure.
+ */
+typedef struct SUPDRVNTPROTECT
+{
+ /** The AVL node core structure. The process ID is the pid. */
+ AVLPVNODECORE AvlCore;
+ /** Magic value (SUPDRVNTPROTECT_MAGIC). */
+ uint32_t volatile u32Magic;
+ /** Reference counter. */
+ uint32_t volatile cRefs;
+ /** The kind of process we're protecting. */
+ SUPDRVNTPROTECTKIND volatile enmProcessKind;
+ /** Whether this structure is in the tree. */
+ bool fInTree : 1;
+ /** 7,: Hack to allow the supid themes service duplicate handle privileges to
+ * our process. */
+ bool fThemesFirstProcessCreateHandle : 1;
+ /** Vista, 7 & 8: Hack to allow more rights to the handle returned by
+ * NtCreateUserProcess. Only applicable to VmProcessUnconfirmed. */
+ bool fFirstProcessCreateHandle : 1;
+ /** Vista, 7 & 8: Hack to allow more rights to the handle returned by
+ * NtCreateUserProcess. Only applicable to VmProcessUnconfirmed. */
+ bool fFirstThreadCreateHandle : 1;
+ /** 8.1: Hack to allow more rights to the handle returned by
+ * NtCreateUserProcess. Only applicable to VmProcessUnconfirmed. */
+ bool fCsrssFirstProcessCreateHandle : 1;
+ /** Vista, 7 & 8: Hack to allow more rights to the handle duplicated by CSRSS
+ * during process creation. Only applicable to VmProcessUnconfirmed. On
+ * 32-bit systems we allow two as ZoneAlarm's system call hooks has been
+ * observed to do some seemingly unnecessary duplication work. */
+ int32_t volatile cCsrssFirstProcessDuplicateHandle;
+
+ /** The parent PID for VM processes, otherwise NULL. */
+ HANDLE hParentPid;
+ /** The TID of the thread opening VBoxDrv or VBoxDrvStub, NULL if not opened. */
+ HANDLE hOpenTid;
+ /** The PID of the CSRSS process associated with this process. */
+ HANDLE hCsrssPid;
+ /** Pointer to the CSRSS process structure (referenced). */
+ PEPROCESS pCsrssProcess;
+ /** State dependent data. */
+ union
+ {
+ /** A stub process in the StubParent state will keep a reference to a child
+ * while it's in the VmProcessUnconfirmed state so that it can be cleaned up
+ * correctly if things doesn't work out. */
+ struct SUPDRVNTPROTECT *pChild;
+ /** A process in the VmProcessUnconfirmed state will keep a weak
+ * reference to the parent's protection structure so it can clean up the pChild
+ * reference the parent has to it. */
+ struct SUPDRVNTPROTECT *pParent;
+ } u;
+} SUPDRVNTPROTECT;
+/** Pointer to a NT process protection record. */
+typedef SUPDRVNTPROTECT *PSUPDRVNTPROTECT;
+/** The SUPDRVNTPROTECT::u32Magic value (Robert A. Heinlein). */
+# define SUPDRVNTPROTECT_MAGIC UINT32_C(0x19070707)
+/** The SUPDRVNTPROTECT::u32Magic value of a dead structure. */
+# define SUPDRVNTPROTECT_MAGIC_DEAD UINT32_C(0x19880508)
+
+/** Pointer to ObGetObjectType. */
+typedef POBJECT_TYPE (NTAPI *PFNOBGETOBJECTTYPE)(PVOID);
+/** Pointer to ObRegisterCallbacks. */
+typedef NTSTATUS (NTAPI *PFNOBREGISTERCALLBACKS)(POB_CALLBACK_REGISTRATION, PVOID *);
+/** Pointer to ObUnregisterCallbacks. */
+typedef VOID (NTAPI *PFNOBUNREGISTERCALLBACKS)(PVOID);
+/** Pointer to PsSetCreateProcessNotifyRoutineEx. */
+typedef NTSTATUS (NTAPI *PFNPSSETCREATEPROCESSNOTIFYROUTINEEX)(PCREATE_PROCESS_NOTIFY_ROUTINE_EX, BOOLEAN);
+/** Pointer to PsReferenceProcessFilePointer. */
+typedef NTSTATUS (NTAPI *PFNPSREFERENCEPROCESSFILEPOINTER)(PEPROCESS, PFILE_OBJECT *);
+/** Pointer to PsIsProtectedProcessLight. */
+typedef BOOLEAN (NTAPI *PFNPSISPROTECTEDPROCESSLIGHT)(PEPROCESS);
+/** Pointer to ZwAlpcCreatePort. */
+typedef NTSTATUS (NTAPI *PFNZWALPCCREATEPORT)(PHANDLE, POBJECT_ATTRIBUTES, struct _ALPC_PORT_ATTRIBUTES *);
+
+#endif /* VBOX_WITH_HARDENINIG */
+
+
+/*********************************************************************************************************************************
+* Internal Functions *
+*********************************************************************************************************************************/
+static void _stdcall VBoxDrvNtUnload(PDRIVER_OBJECT pDrvObj);
+static NTSTATUS _stdcall VBoxDrvNtCreate(PDEVICE_OBJECT pDevObj, PIRP pIrp);
+static NTSTATUS _stdcall VBoxDrvNtCleanup(PDEVICE_OBJECT pDevObj, PIRP pIrp);
+static NTSTATUS _stdcall VBoxDrvNtClose(PDEVICE_OBJECT pDevObj, PIRP pIrp);
+#ifdef VBOXDRV_WITH_FAST_IO
+static BOOLEAN _stdcall VBoxDrvNtFastIoDeviceControl(PFILE_OBJECT pFileObj, BOOLEAN fWait, PVOID pvInput, ULONG cbInput,
+ PVOID pvOutput, ULONG cbOutput, ULONG uCmd,
+ PIO_STATUS_BLOCK pIoStatus, PDEVICE_OBJECT pDevObj);
+#endif
+static NTSTATUS _stdcall VBoxDrvNtDeviceControl(PDEVICE_OBJECT pDevObj, PIRP pIrp);
+static int VBoxDrvNtDeviceControlSlow(PSUPDRVDEVEXT pDevExt, PSUPDRVSESSION pSession, PIRP pIrp, PIO_STACK_LOCATION pStack);
+static NTSTATUS _stdcall VBoxDrvNtInternalDeviceControl(PDEVICE_OBJECT pDevObj, PIRP pIrp);
+static VOID _stdcall VBoxPowerDispatchCallback(PVOID pCallbackContext, PVOID pArgument1, PVOID pArgument2);
+static NTSTATUS _stdcall VBoxDrvNtRead(PDEVICE_OBJECT pDevObj, PIRP pIrp);
+static NTSTATUS _stdcall VBoxDrvNtNotSupportedStub(PDEVICE_OBJECT pDevObj, PIRP pIrp);
+static NTSTATUS VBoxDrvNtErr2NtStatus(int rc);
+#ifdef VBOX_WITH_HARDENING
+static NTSTATUS supdrvNtProtectInit(void);
+static void supdrvNtProtectTerm(void);
+static int supdrvNtProtectCreate(PSUPDRVNTPROTECT *ppNtProtect, HANDLE hPid,
+ SUPDRVNTPROTECTKIND enmProcessKind, bool fLink);
+static void supdrvNtProtectRelease(PSUPDRVNTPROTECT pNtProtect);
+static PSUPDRVNTPROTECT supdrvNtProtectLookup(HANDLE hPid);
+static int supdrvNtProtectFindAssociatedCsrss(PSUPDRVNTPROTECT pNtProtect);
+static int supdrvNtProtectVerifyProcess(PSUPDRVNTPROTECT pNtProtect);
+
+static bool supdrvNtIsDebuggerAttached(void);
+static void supdrvNtErrorInfoCleanupProcess(HANDLE hProcessId);
+
+#endif
+
+
+/*********************************************************************************************************************************
+* Exported Functions *
+*********************************************************************************************************************************/
+RT_C_DECLS_BEGIN
+NTSTATUS _stdcall DriverEntry(PDRIVER_OBJECT pDrvObj, PUNICODE_STRING pRegPath);
+RT_C_DECLS_END
+
+
+/*********************************************************************************************************************************
+* Global Variables *
+*********************************************************************************************************************************/
+/** The non-paged pool type to use, NonPagedPool or NonPagedPoolNx. */
+static POOL_TYPE g_enmNonPagedPoolType = NonPagedPool;
+/** Pointer to the system device instance. */
+static PDEVICE_OBJECT g_pDevObjSys = NULL;
+/** Pointer to the user device instance. */
+static PDEVICE_OBJECT g_pDevObjUsr = NULL;
+#ifdef VBOXDRV_WITH_FAST_IO
+/** Fast I/O dispatch table. */
+static FAST_IO_DISPATCH const g_VBoxDrvFastIoDispatch =
+{
+ /* .SizeOfFastIoDispatch = */ sizeof(g_VBoxDrvFastIoDispatch),
+ /* .FastIoCheckIfPossible = */ NULL,
+ /* .FastIoRead = */ NULL,
+ /* .FastIoWrite = */ NULL,
+ /* .FastIoQueryBasicInfo = */ NULL,
+ /* .FastIoQueryStandardInfo = */ NULL,
+ /* .FastIoLock = */ NULL,
+ /* .FastIoUnlockSingle = */ NULL,
+ /* .FastIoUnlockAll = */ NULL,
+ /* .FastIoUnlockAllByKey = */ NULL,
+ /* .FastIoDeviceControl = */ VBoxDrvNtFastIoDeviceControl,
+ /* .AcquireFileForNtCreateSection = */ NULL,
+ /* .ReleaseFileForNtCreateSection = */ NULL,
+ /* .FastIoDetachDevice = */ NULL,
+ /* .FastIoQueryNetworkOpenInfo = */ NULL,
+ /* .AcquireForModWrite = */ NULL,
+ /* .MdlRead = */ NULL,
+ /* .MdlReadComplete = */ NULL,
+ /* .PrepareMdlWrite = */ NULL,
+ /* .MdlWriteComplete = */ NULL,
+ /* .FastIoReadCompressed = */ NULL,
+ /* .FastIoWriteCompressed = */ NULL,
+ /* .MdlReadCompleteCompressed = */ NULL,
+ /* .MdlWriteCompleteCompressed = */ NULL,
+ /* .FastIoQueryOpen = */ NULL,
+ /* .ReleaseForModWrite = */ NULL,
+ /* .AcquireForCcFlush = */ NULL,
+ /* .ReleaseForCcFlush = */ NULL,
+};
+#endif /* VBOXDRV_WITH_FAST_IO */
+
+/** Default ZERO value. */
+static ULONG g_fOptDefaultZero = 0;
+/** Registry values.
+ * We wrap these in a struct to ensure they are followed by a little zero
+ * padding in order to limit the chance of trouble on unpatched systems. */
+struct
+{
+ /** The ForceAsync registry value. */
+ ULONG fOptForceAsyncTsc;
+ /** Padding. */
+ uint64_t au64Padding[2];
+} g_Options = { FALSE, 0, 0 };
+/** Registry query table for RtlQueryRegistryValues. */
+static RTL_QUERY_REGISTRY_TABLE g_aRegValues[] =
+{
+ {
+ /* .QueryRoutine = */ NULL,
+ /* .Flags = */ RTL_QUERY_REGISTRY_DIRECT | RTL_QUERY_REGISTRY_TYPECHECK,
+ /* .Name = */ L"ForceAsyncTsc",
+ /* .EntryContext = */ &g_Options.fOptForceAsyncTsc,
+ /* .DefaultType = */ (REG_DWORD << RTL_QUERY_REGISTRY_TYPECHECK_SHIFT) | REG_DWORD,
+ /* .DefaultData = */ &g_fOptDefaultZero,
+ /* .DefaultLength = */ sizeof(g_fOptDefaultZero),
+ },
+ { NULL, 0, NULL, NULL, 0, NULL, 0 } /* terminator entry. */
+};
+
+/** Pointer to KeQueryMaximumGroupCount. */
+static PFNKEQUERYMAXIMUMGROUPCOUNT g_pfnKeQueryMaximumGroupCount = NULL;
+/** Pointer to KeGetProcessorIndexFromNumber. */
+static PFNKEGETPROCESSORINDEXFROMNUMBER g_pfnKeGetProcessorIndexFromNumber = NULL;
+/** Pointer to KeGetProcessorNumberFromIndex. */
+static PFNKEGETPROCESSORNUMBERFROMINDEX g_pfnKeGetProcessorNumberFromIndex = NULL;
+
+#ifdef VBOXDRV_WITH_SID_TO_UID_MAPPING
+/** Spinlock protecting g_NtUserIdHashTree and g_NtUserIdUidTree. */
+static RTSPINLOCK g_hNtUserIdLock = NIL_RTSPINLOCK;
+/** AVL tree of SUPDRVNTUSERID structures by hash value. */
+static PAVLLU32NODECORE g_NtUserIdHashTree = NULL;
+/** AVL tree of SUPDRVNTUSERID structures by UID. */
+static PAVLU32NODECORE g_NtUserIdUidTree = NULL;
+#endif
+
+#ifdef VBOX_WITH_HARDENING
+/** Pointer to the stub device instance. */
+static PDEVICE_OBJECT g_pDevObjStub = NULL;
+/** Spinlock protecting g_NtProtectTree as well as the releasing of protection
+ * structures. */
+static RTSPINLOCK g_hNtProtectLock = NIL_RTSPINLOCK;
+/** AVL tree of SUPDRVNTPROTECT structures. */
+static AVLPVTREE g_NtProtectTree = NULL;
+/** Cookie returned by ObRegisterCallbacks for the callbacks. */
+static PVOID g_pvObCallbacksCookie = NULL;
+/** Combined windows NT version number. See SUP_MAKE_NT_VER_COMBINED. */
+uint32_t g_uNtVerCombined = 0;
+/** Pointer to ObGetObjectType if available.. */
+static PFNOBGETOBJECTTYPE g_pfnObGetObjectType = NULL;
+/** Pointer to ObRegisterCallbacks if available.. */
+static PFNOBREGISTERCALLBACKS g_pfnObRegisterCallbacks = NULL;
+/** Pointer to ObUnregisterCallbacks if available.. */
+static PFNOBUNREGISTERCALLBACKS g_pfnObUnRegisterCallbacks = NULL;
+/** Pointer to PsSetCreateProcessNotifyRoutineEx if available.. */
+static PFNPSSETCREATEPROCESSNOTIFYROUTINEEX g_pfnPsSetCreateProcessNotifyRoutineEx = NULL;
+/** Pointer to PsReferenceProcessFilePointer if available. */
+static PFNPSREFERENCEPROCESSFILEPOINTER g_pfnPsReferenceProcessFilePointer = NULL;
+/** Pointer to PsIsProtectedProcessLight. */
+static PFNPSISPROTECTEDPROCESSLIGHT g_pfnPsIsProtectedProcessLight = NULL;
+/** Pointer to ZwAlpcCreatePort. */
+static PFNZWALPCCREATEPORT g_pfnZwAlpcCreatePort = NULL;
+
+# ifdef RT_ARCH_AMD64
+extern "C" {
+/** Pointer to KiServiceLinkage (used to fake missing ZwQueryVirtualMemory on
+ * XP64 / W2K3-64). */
+PFNRT g_pfnKiServiceLinkage = NULL;
+/** Pointer to KiServiceInternal (used to fake missing ZwQueryVirtualMemory on
+ * XP64 / W2K3-64) */
+PFNRT g_pfnKiServiceInternal = NULL;
+}
+# endif
+/** The primary ALPC port object type. (LpcPortObjectType at init time.) */
+static POBJECT_TYPE g_pAlpcPortObjectType1 = NULL;
+/** The secondary ALPC port object type. (Sampled at runtime.) */
+static POBJECT_TYPE volatile g_pAlpcPortObjectType2 = NULL;
+
+/** Pointer to the error information device instance. */
+static PDEVICE_OBJECT g_pDevObjErrorInfo = NULL;
+/** Fast mutex semaphore protecting the error info list. */
+static RTSEMMUTEX g_hErrorInfoLock = NIL_RTSEMMUTEX;
+/** Head of the error info (SUPDRVNTERRORINFO). */
+static RTLISTANCHOR g_ErrorInfoHead;
+
+#endif
+
+
+/**
+ * Takes care of creating the devices and their symbolic links.
+ *
+ * @returns NT status code.
+ * @param pDrvObj Pointer to driver object.
+ */
+static NTSTATUS vboxdrvNtCreateDevices(PDRIVER_OBJECT pDrvObj)
+{
+ /*
+ * System device.
+ */
+ UNICODE_STRING DevName;
+ RtlInitUnicodeString(&DevName, SUPDRV_NT_DEVICE_NAME_SYS);
+ NTSTATUS rcNt = IoCreateDevice(pDrvObj, sizeof(SUPDRVDEVEXT), &DevName, FILE_DEVICE_UNKNOWN, 0, FALSE, &g_pDevObjSys);
+ if (NT_SUCCESS(rcNt))
+ {
+ /*
+ * User device.
+ */
+ RtlInitUnicodeString(&DevName, SUPDRV_NT_DEVICE_NAME_USR);
+ rcNt = IoCreateDevice(pDrvObj, sizeof(SUPDRVDEVEXTUSR), &DevName, FILE_DEVICE_UNKNOWN, 0, FALSE, &g_pDevObjUsr);
+ if (NT_SUCCESS(rcNt))
+ {
+ PSUPDRVDEVEXTUSR pDevExtUsr = (PSUPDRVDEVEXTUSR)g_pDevObjUsr->DeviceExtension;
+ pDevExtUsr->pMainDrvExt = (PSUPDRVDEVEXT)g_pDevObjSys->DeviceExtension;
+ pDevExtUsr->u32Cookie = SUPDRVDEVEXTUSR_COOKIE;
+
+#ifdef VBOX_WITH_HARDENING
+ /*
+ * Hardened stub device.
+ */
+ RtlInitUnicodeString(&DevName, SUPDRV_NT_DEVICE_NAME_STUB);
+ rcNt = IoCreateDevice(pDrvObj, sizeof(SUPDRVDEVEXTSTUB), &DevName, FILE_DEVICE_UNKNOWN, 0, FALSE, &g_pDevObjStub);
+ if (NT_SUCCESS(rcNt))
+ {
+ if (NT_SUCCESS(rcNt))
+ {
+ PSUPDRVDEVEXTSTUB pDevExtStub = (PSUPDRVDEVEXTSTUB)g_pDevObjStub->DeviceExtension;
+ pDevExtStub->Common.pMainDrvExt = (PSUPDRVDEVEXT)g_pDevObjSys->DeviceExtension;
+ pDevExtStub->Common.u32Cookie = SUPDRVDEVEXTSTUB_COOKIE;
+
+ /*
+ * Hardened error information device.
+ */
+ RtlInitUnicodeString(&DevName, SUPDRV_NT_DEVICE_NAME_ERROR_INFO);
+ rcNt = IoCreateDevice(pDrvObj, sizeof(SUPDRVDEVEXTERRORINFO), &DevName, FILE_DEVICE_UNKNOWN, 0, FALSE,
+ &g_pDevObjErrorInfo);
+ if (NT_SUCCESS(rcNt))
+ {
+ g_pDevObjErrorInfo->Flags |= DO_BUFFERED_IO;
+
+ if (NT_SUCCESS(rcNt))
+ {
+ PSUPDRVDEVEXTERRORINFO pDevExtErrInf = (PSUPDRVDEVEXTERRORINFO)g_pDevObjStub->DeviceExtension;
+ pDevExtErrInf->Common.pMainDrvExt = (PSUPDRVDEVEXT)g_pDevObjSys->DeviceExtension;
+ pDevExtErrInf->Common.u32Cookie = SUPDRVDEVEXTERRORINFO_COOKIE;
+
+#endif
+ /* Done. */
+ return rcNt;
+#ifdef VBOX_WITH_HARDENING
+ }
+
+ /* Bail out. */
+ IoDeleteDevice(g_pDevObjErrorInfo);
+ g_pDevObjErrorInfo = NULL;
+ }
+ }
+
+ /* Bail out. */
+ IoDeleteDevice(g_pDevObjStub);
+ g_pDevObjUsr = NULL;
+ }
+ IoDeleteDevice(g_pDevObjUsr);
+ g_pDevObjUsr = NULL;
+#endif
+ }
+ IoDeleteDevice(g_pDevObjSys);
+ g_pDevObjSys = NULL;
+ }
+ return rcNt;
+}
+
+/**
+ * Destroys the devices and links created by vboxdrvNtCreateDevices.
+ */
+static void vboxdrvNtDestroyDevices(void)
+{
+ if (g_pDevObjUsr)
+ {
+ PSUPDRVDEVEXTUSR pDevExtUsr = (PSUPDRVDEVEXTUSR)g_pDevObjUsr->DeviceExtension;
+ pDevExtUsr->pMainDrvExt = NULL;
+ }
+#ifdef VBOX_WITH_HARDENING
+ if (g_pDevObjStub)
+ {
+ PSUPDRVDEVEXTSTUB pDevExtStub = (PSUPDRVDEVEXTSTUB)g_pDevObjStub->DeviceExtension;
+ pDevExtStub->Common.pMainDrvExt = NULL;
+ }
+ if (g_pDevObjErrorInfo)
+ {
+ PSUPDRVDEVEXTERRORINFO pDevExtErrorInfo = (PSUPDRVDEVEXTERRORINFO)g_pDevObjStub->DeviceExtension;
+ pDevExtErrorInfo->Common.pMainDrvExt = NULL;
+ }
+#endif
+
+#ifdef VBOX_WITH_HARDENING
+ IoDeleteDevice(g_pDevObjErrorInfo);
+ g_pDevObjErrorInfo = NULL;
+ IoDeleteDevice(g_pDevObjStub);
+ g_pDevObjStub = NULL;
+#endif
+ IoDeleteDevice(g_pDevObjUsr);
+ g_pDevObjUsr = NULL;
+ IoDeleteDevice(g_pDevObjSys);
+ g_pDevObjSys = NULL;
+}
+
+
+/**
+ * Driver entry point.
+ *
+ * @returns appropriate status code.
+ * @param pDrvObj Pointer to driver object.
+ * @param pRegPath Registry base path.
+ */
+NTSTATUS _stdcall DriverEntry(PDRIVER_OBJECT pDrvObj, PUNICODE_STRING pRegPath)
+{
+ RT_NOREF1(pRegPath);
+
+ /*
+ * Sanity checks.
+ */
+#ifdef VBOXDRV_WITH_FAST_IO
+ if (g_VBoxDrvFastIoDispatch.FastIoDeviceControl != VBoxDrvNtFastIoDeviceControl)
+ {
+ DbgPrint("VBoxDrv: FastIoDeviceControl=%p instead of %p\n",
+ g_VBoxDrvFastIoDispatch.FastIoDeviceControl, VBoxDrvNtFastIoDeviceControl);
+ return STATUS_INTERNAL_ERROR;
+ }
+#endif
+
+ /*
+ * Figure out if we can use NonPagedPoolNx or not.
+ */
+ ULONG ulMajorVersion, ulMinorVersion, ulBuildNumber;
+ PsGetVersion(&ulMajorVersion, &ulMinorVersion, &ulBuildNumber, NULL);
+ if (ulMajorVersion > 6 || (ulMajorVersion == 6 && ulMinorVersion >= 2)) /* >= 6.2 (W8)*/
+ g_enmNonPagedPoolType = NonPagedPoolNx;
+
+ /*
+ * Query options first so any overflows on unpatched machines will do less
+ * harm (see MS11-011 / 2393802 / 2011-03-18).
+ *
+ * Unfortunately, pRegPath isn't documented as zero terminated, even if it
+ * quite likely always is, so we have to make a copy here.
+ */
+ NTSTATUS rcNt;
+ PWSTR pwszCopy = (PWSTR)ExAllocatePoolWithTag(g_enmNonPagedPoolType, pRegPath->Length + sizeof(WCHAR), 'VBox');
+ if (pwszCopy)
+ {
+ memcpy(pwszCopy, pRegPath->Buffer, pRegPath->Length);
+ pwszCopy[pRegPath->Length / sizeof(WCHAR)] = '\0';
+ rcNt = RtlQueryRegistryValues(RTL_REGISTRY_ABSOLUTE | RTL_REGISTRY_OPTIONAL, pwszCopy,
+ g_aRegValues, NULL /*pvContext*/, NULL /*pvEnv*/);
+ ExFreePoolWithTag(pwszCopy, 'VBox');
+ /* Probably safe to ignore rcNt here. */
+ }
+
+ /*
+ * Resolve methods we want but isn't available everywhere.
+ */
+ UNICODE_STRING RoutineName;
+ RtlInitUnicodeString(&RoutineName, L"KeQueryMaximumGroupCount");
+ g_pfnKeQueryMaximumGroupCount = (PFNKEQUERYMAXIMUMGROUPCOUNT)MmGetSystemRoutineAddress(&RoutineName);
+
+ RtlInitUnicodeString(&RoutineName, L"KeGetProcessorIndexFromNumber");
+ g_pfnKeGetProcessorIndexFromNumber = (PFNKEGETPROCESSORINDEXFROMNUMBER)MmGetSystemRoutineAddress(&RoutineName);
+
+ RtlInitUnicodeString(&RoutineName, L"KeGetProcessorNumberFromIndex");
+ g_pfnKeGetProcessorNumberFromIndex = (PFNKEGETPROCESSORNUMBERFROMINDEX)MmGetSystemRoutineAddress(&RoutineName);
+
+ Assert( (g_pfnKeGetProcessorNumberFromIndex != NULL) == (g_pfnKeGetProcessorIndexFromNumber != NULL)
+ && (g_pfnKeGetProcessorNumberFromIndex != NULL) == (g_pfnKeQueryMaximumGroupCount != NULL)); /* all or nothing. */
+
+ /*
+ * Initialize the runtime (IPRT).
+ */
+ int vrc = RTR0Init(0);
+ if (RT_SUCCESS(vrc))
+ {
+ Log(("VBoxDrv::DriverEntry\n"));
+
+#ifdef VBOX_WITH_HARDENING
+ /*
+ * Initialize process protection.
+ */
+ rcNt = supdrvNtProtectInit();
+ if (NT_SUCCESS(rcNt))
+#endif
+ {
+#ifdef VBOXDRV_WITH_SID_TO_UID_MAPPING
+ /*
+ * Create the spinlock for the SID -> UID mappings.
+ */
+ vrc = RTSpinlockCreate(&g_hNtUserIdLock, RTSPINLOCK_FLAGS_INTERRUPT_UNSAFE, "NtUserId");
+ if (RT_SUCCESS(vrc))
+#endif
+ {
+ /*
+ * Create device.
+ * (That means creating a device object and a symbolic link so the DOS
+ * subsystems (OS/2, win32, ++) can access the device.)
+ */
+ rcNt = vboxdrvNtCreateDevices(pDrvObj);
+ if (NT_SUCCESS(rcNt))
+ {
+ /*
+ * Initialize the device extension.
+ */
+ PSUPDRVDEVEXT pDevExt = (PSUPDRVDEVEXT)g_pDevObjSys->DeviceExtension;
+ memset(pDevExt, 0, sizeof(*pDevExt));
+
+ vrc = supdrvInitDevExt(pDevExt, sizeof(SUPDRVSESSION));
+ if (!vrc)
+ {
+ /*
+ * Setup the driver entry points in pDrvObj.
+ */
+ pDrvObj->DriverUnload = VBoxDrvNtUnload;
+ pDrvObj->MajorFunction[IRP_MJ_CREATE] = VBoxDrvNtCreate;
+ pDrvObj->MajorFunction[IRP_MJ_CLEANUP] = VBoxDrvNtCleanup;
+ pDrvObj->MajorFunction[IRP_MJ_CLOSE] = VBoxDrvNtClose;
+ pDrvObj->MajorFunction[IRP_MJ_DEVICE_CONTROL] = VBoxDrvNtDeviceControl;
+ pDrvObj->MajorFunction[IRP_MJ_INTERNAL_DEVICE_CONTROL] = VBoxDrvNtInternalDeviceControl;
+ pDrvObj->MajorFunction[IRP_MJ_READ] = VBoxDrvNtRead;
+ pDrvObj->MajorFunction[IRP_MJ_WRITE] = VBoxDrvNtNotSupportedStub;
+
+#ifdef VBOXDRV_WITH_FAST_IO
+ /* Fast I/O to speed up guest execution roundtrips. */
+ pDrvObj->FastIoDispatch = (PFAST_IO_DISPATCH)&g_VBoxDrvFastIoDispatch;
+#endif
+
+ /*
+ * Register ourselves for power state changes. We don't
+ * currently care if this fails.
+ */
+ UNICODE_STRING CallbackName;
+ RtlInitUnicodeString(&CallbackName, L"\\Callback\\PowerState");
+
+ OBJECT_ATTRIBUTES Attr;
+ InitializeObjectAttributes(&Attr, &CallbackName, OBJ_CASE_INSENSITIVE, NULL, NULL);
+
+ rcNt = ExCreateCallback(&pDevExt->pObjPowerCallback, &Attr, TRUE, TRUE);
+ if (rcNt == STATUS_SUCCESS)
+ pDevExt->hPowerCallback = ExRegisterCallback(pDevExt->pObjPowerCallback,
+ VBoxPowerDispatchCallback,
+ g_pDevObjSys);
+
+ /*
+ * Done! Returning success!
+ */
+ Log(("VBoxDrv::DriverEntry returning STATUS_SUCCESS\n"));
+ return STATUS_SUCCESS;
+ }
+
+ /*
+ * Failed. Clean up.
+ */
+ Log(("supdrvInitDevExit failed with vrc=%d!\n", vrc));
+ rcNt = VBoxDrvNtErr2NtStatus(vrc);
+
+ vboxdrvNtDestroyDevices();
+ }
+#ifdef VBOXDRV_WITH_SID_TO_UID_MAPPING
+ RTSpinlockDestroy(g_hNtUserIdLock);
+ g_hNtUserIdLock = NIL_RTSPINLOCK;
+#endif
+ }
+#ifdef VBOXDRV_WITH_SID_TO_UID_MAPPING
+ else
+ rcNt = VBoxDrvNtErr2NtStatus(vrc);
+#endif
+#ifdef VBOX_WITH_HARDENING
+ supdrvNtProtectTerm();
+#endif
+ }
+ RTTermRunCallbacks(RTTERMREASON_UNLOAD, 0);
+ RTR0Term();
+ }
+ else
+ {
+ Log(("RTR0Init failed with vrc=%d!\n", vrc));
+ rcNt = VBoxDrvNtErr2NtStatus(vrc);
+ }
+ if (NT_SUCCESS(rcNt))
+ rcNt = STATUS_INVALID_PARAMETER;
+ return rcNt;
+}
+
+
+/**
+ * Unload the driver.
+ *
+ * @param pDrvObj Driver object.
+ */
+void _stdcall VBoxDrvNtUnload(PDRIVER_OBJECT pDrvObj)
+{
+ PSUPDRVDEVEXT pDevExt = (PSUPDRVDEVEXT)g_pDevObjSys->DeviceExtension;
+
+ Log(("VBoxDrvNtUnload at irql %d\n", KeGetCurrentIrql()));
+
+ /* Clean up the power callback registration. */
+ if (pDevExt->hPowerCallback)
+ ExUnregisterCallback(pDevExt->hPowerCallback);
+ if (pDevExt->pObjPowerCallback)
+ ObDereferenceObject(pDevExt->pObjPowerCallback);
+
+ /*
+ * We ASSUME that it's not possible to unload a driver with open handles.
+ */
+ supdrvDeleteDevExt(pDevExt);
+#ifdef VBOXDRV_WITH_SID_TO_UID_MAPPING
+ RTSpinlockDestroy(g_hNtUserIdLock);
+ g_hNtUserIdLock = NIL_RTSPINLOCK;
+#endif
+#ifdef VBOX_WITH_HARDENING
+ supdrvNtProtectTerm();
+#endif
+ RTTermRunCallbacks(RTTERMREASON_UNLOAD, 0);
+ RTR0Term();
+ vboxdrvNtDestroyDevices();
+
+ NOREF(pDrvObj);
+}
+
+#ifdef VBOXDRV_WITH_SID_TO_UID_MAPPING
+
+/**
+ * Worker for supdrvNtUserIdMakeForSession.
+ */
+static bool supdrvNtUserIdMakeUid(PSUPDRVNTUSERID pNtUserId)
+{
+ pNtUserId->UidCore.Key = pNtUserId->HashCore.Key;
+ for (uint32_t cTries = 0; cTries < _4K; cTries++)
+ {
+ bool fRc = RTAvlU32Insert(&g_NtUserIdUidTree, &pNtUserId->UidCore);
+ if (fRc)
+ return true;
+ pNtUserId->UidCore.Key += pNtUserId->cchSid | 1;
+ }
+ return false;
+}
+
+
+/**
+ * Try create a RTUID value for the session.
+ *
+ * @returns VBox status code.
+ * @param pSession The session to try set SUPDRVSESSION::Uid for.
+ */
+static int supdrvNtUserIdMakeForSession(PSUPDRVSESSION pSession)
+{
+ /*
+ * Get the current security context and query the User SID for it.
+ */
+ SECURITY_SUBJECT_CONTEXT Ctx = { NULL, SecurityIdentification, NULL, NULL };
+ SeCaptureSubjectContext(&Ctx);
+
+ int rc;
+ TOKEN_USER *pTokenUser = NULL;
+ NTSTATUS rcNt = SeQueryInformationToken(SeQuerySubjectContextToken(&Ctx) /* or always PrimaryToken?*/,
+ TokenUser, (PVOID *)&pTokenUser);
+ if (NT_SUCCESS(rcNt))
+ {
+ /*
+ * Convert the user SID to a string to make it easier to handle, then prepare
+ * a user ID entry for it as that way we can combine lookup and insertion and
+ * avoid needing to deal with races.
+ */
+ UNICODE_STRING UniStr = RTNT_NULL_UNISTR();
+ rcNt = RtlConvertSidToUnicodeString(&UniStr, pTokenUser->User.Sid, TRUE /*AllocateDesitnationString*/);
+ if (NT_SUCCESS(rcNt))
+ {
+ size_t cchSid = 0;
+ rc = RTUtf16CalcUtf8LenEx(UniStr.Buffer, UniStr.Length / sizeof(RTUTF16), &cchSid);
+ if (RT_SUCCESS(rc))
+ {
+ PSUPDRVNTUSERID const pNtUserIdNew = (PSUPDRVNTUSERID)RTMemAlloc(RT_UOFFSETOF_DYN(SUPDRVNTUSERID, szSid[cchSid + 1]));
+ if (pNtUserIdNew)
+ {
+ char *pszSid = pNtUserIdNew->szSid;
+ rc = RTUtf16ToUtf8Ex(UniStr.Buffer, UniStr.Length / sizeof(RTUTF16), &pszSid, cchSid + 1, NULL);
+ if (RT_SUCCESS(rc))
+ {
+ pNtUserIdNew->HashCore.Key = RTStrHash1(pNtUserIdNew->szSid);
+ pNtUserIdNew->cchSid = (uint16_t)cchSid;
+ pNtUserIdNew->cRefs = 1;
+ Log5Func(("pNtUserId=%p cchSid=%u hash=%#x '%s'\n", pNtUserIdNew, cchSid, pNtUserIdNew->HashCore.Key, pszSid));
+
+ /*
+ * Do the lookup / insert.
+ */
+ RTSpinlockAcquire(g_hNtUserIdLock);
+ AssertCompileMemberOffset(SUPDRVNTUSERID, HashCore, 0);
+ PSUPDRVNTUSERID pNtUserId = (PSUPDRVNTUSERID)RTAvllU32Get(&g_NtUserIdHashTree, pNtUserIdNew->HashCore.Key);
+ if (pNtUserId)
+ {
+ /* Match the strings till we reach the end of the collision list. */
+ PSUPDRVNTUSERID const pNtUserIdHead = pNtUserId;
+ while ( pNtUserId
+ && ( pNtUserId->cchSid != cchSid
+ || memcmp(pNtUserId->szSid, pNtUserId->szSid, cchSid) != 0))
+ pNtUserId = (PSUPDRVNTUSERID)pNtUserId->HashCore.pList;
+ if (pNtUserId)
+ {
+ /* Found matching: Retain reference and free the new entry we prepared. */
+ uint32_t const cRefs = ASMAtomicIncU32(&pNtUserId->cRefs);
+ Assert(cRefs < _16K); RT_NOREF(cRefs);
+ RTSpinlockRelease(g_hNtUserIdLock);
+ Log5Func(("Using %p / %#x instead\n", pNtUserId, pNtUserId->UidCore.Key));
+ }
+ else
+ {
+ /* No match: Try insert prepared entry after the head node. */
+ if (supdrvNtUserIdMakeUid(pNtUserIdNew))
+ {
+ pNtUserIdNew->HashCore.pList = pNtUserIdHead->HashCore.pList;
+ pNtUserIdHead->HashCore.pList = &pNtUserIdNew->HashCore;
+ pNtUserId = pNtUserIdNew;
+ }
+ RTSpinlockRelease(g_hNtUserIdLock);
+ if (pNtUserId)
+ Log5Func(("Using %p / %#x (the prepared one)\n", pNtUserId, pNtUserId->UidCore.Key));
+ else
+ LogRelFunc(("supdrvNtUserIdMakeForSession: failed to insert new\n"));
+ }
+ }
+ else
+ {
+ /* No matching hash: Try insert the prepared entry. */
+ pNtUserIdNew->UidCore.Key = pNtUserIdNew->HashCore.Key;
+ if (supdrvNtUserIdMakeUid(pNtUserIdNew))
+ {
+ RTAvllU32Insert(&g_NtUserIdHashTree, &pNtUserIdNew->HashCore);
+ pNtUserId = pNtUserIdNew;
+ }
+ RTSpinlockRelease(g_hNtUserIdLock);
+ if (pNtUserId)
+ Log5Func(("Using %p / %#x (the prepared one, no conflict)\n", pNtUserId, pNtUserId->UidCore.Key));
+ else
+ LogRelFunc(("failed to insert!! WTF!?!\n"));
+ }
+
+ if (pNtUserId != pNtUserIdNew)
+ RTMemFree(pNtUserIdNew);
+
+ /*
+ * Update the session info.
+ */
+ pSession->pNtUserId = pNtUserId;
+ pSession->Uid = pNtUserId ? (RTUID)pNtUserId->UidCore.Key : NIL_RTUID;
+ }
+ else
+ RTMemFree(pNtUserIdNew);
+ }
+ else
+ rc = VERR_NO_MEMORY;
+ }
+ RtlFreeUnicodeString(&UniStr);
+ }
+ else
+ {
+ rc = RTErrConvertFromNtStatus(rcNt);
+ LogFunc(("RtlConvertSidToUnicodeString failed: %#x / %Rrc\n", rcNt, rc));
+ }
+ ExFreePool(pTokenUser);
+ }
+ else
+ {
+ rc = RTErrConvertFromNtStatus(rcNt);
+ LogFunc(("SeQueryInformationToken failed: %#x / %Rrc\n", rcNt, rc));
+ }
+
+ SeReleaseSubjectContext(&Ctx);
+ return rc;
+}
+
+
+/**
+ * Releases a reference to @a pNtUserId.
+ *
+ * @param pNtUserId The NT user ID entry to release.
+ */
+static void supdrvNtUserIdRelease(PSUPDRVNTUSERID pNtUserId)
+{
+ if (pNtUserId)
+ {
+ uint32_t const cRefs = ASMAtomicDecU32(&pNtUserId->cRefs);
+ Log5Func(("%p / %#x: cRefs=%d\n", pNtUserId, pNtUserId->cRefs));
+ Assert(cRefs < _8K);
+ if (cRefs == 0)
+ {
+ RTSpinlockAcquire(g_hNtUserIdLock);
+ if (pNtUserId->cRefs == 0)
+ {
+ PAVLLU32NODECORE pAssert1 = RTAvllU32RemoveNode(&g_NtUserIdHashTree, &pNtUserId->HashCore);
+ PAVLU32NODECORE pAssert2 = RTAvlU32Remove(&g_NtUserIdUidTree, pNtUserId->UidCore.Key);
+
+ RTSpinlockRelease(g_hNtUserIdLock);
+
+ Assert(pAssert1 == &pNtUserId->HashCore);
+ Assert(pAssert2 == &pNtUserId->UidCore);
+ RT_NOREF(pAssert1, pAssert2);
+
+ RTMemFree(pNtUserId);
+ }
+ else
+ RTSpinlockRelease(g_hNtUserIdLock);
+ }
+ }
+}
+
+#endif /* VBOXDRV_WITH_SID_TO_UID_MAPPING */
+
+/**
+ * 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) supdrvNtCompleteRequestEx(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) supdrvNtCompleteRequest(NTSTATUS rcNt, PIRP pIrp)
+{
+ return supdrvNtCompleteRequestEx(rcNt, 0 /*uInfo*/, pIrp);
+}
+
+
+/**
+ * Create (i.e. Open) file entry point.
+ *
+ * @param pDevObj Device object.
+ * @param pIrp Request packet.
+ */
+NTSTATUS _stdcall VBoxDrvNtCreate(PDEVICE_OBJECT pDevObj, PIRP pIrp)
+{
+ Log(("VBoxDrvNtCreate: RequestorMode=%d\n", pIrp->RequestorMode));
+ PIO_STACK_LOCATION pStack = IoGetCurrentIrpStackLocation(pIrp);
+ PFILE_OBJECT pFileObj = pStack->FileObject;
+ PSUPDRVDEVEXT pDevExt = SUPDRVNT_GET_DEVEXT(pDevObj);
+
+ /*
+ * We are not remotely similar to a directory...
+ * (But this is possible.)
+ */
+ if (pStack->Parameters.Create.Options & FILE_DIRECTORY_FILE)
+ return supdrvNtCompleteRequest(STATUS_NOT_A_DIRECTORY, pIrp);
+
+ /*
+ * Don't create a session for kernel clients, they'll close the handle
+ * immediately and work with the file object via
+ * VBoxDrvNtInternalDeviceControl. The first request will be one to
+ * create a session.
+ */
+ NTSTATUS rcNt;
+ if (pIrp->RequestorMode == KernelMode)
+ {
+ if (pDevObj == g_pDevObjSys)
+ return supdrvNtCompleteRequestEx(STATUS_SUCCESS, FILE_OPENED, pIrp);
+
+ rcNt = STATUS_ACCESS_DENIED;
+ }
+#ifdef VBOX_WITH_HARDENING
+ /*
+ * Anyone can open the error device.
+ */
+ else if (pDevObj == g_pDevObjErrorInfo)
+ {
+ pFileObj->FsContext = NULL;
+ return supdrvNtCompleteRequestEx(STATUS_SUCCESS, FILE_OPENED, pIrp);
+ }
+#endif
+ else
+ {
+#if defined(VBOX_WITH_HARDENING) && !defined(VBOX_WITHOUT_DEBUGGER_CHECKS)
+ /*
+ * Make sure no debuggers are attached to non-user processes.
+ */
+ if ( pDevObj != g_pDevObjUsr
+ && supdrvNtIsDebuggerAttached())
+ {
+ LogRel(("vboxdrv: Process %p is being debugged, access to vboxdrv / vboxdrvu declined.\n",
+ PsGetProcessId(PsGetCurrentProcess())));
+ rcNt = STATUS_TRUST_FAILURE;
+ }
+ else
+#endif
+ {
+ int rc = VINF_SUCCESS;
+
+#ifdef VBOX_WITH_HARDENING
+ /*
+ * Access to the stub device is only granted to processes which
+ * passes verification.
+ *
+ * Note! The stub device has no need for a SUPDRVSESSION structure,
+ * so the it uses the SUPDRVNTPROTECT directly instead.
+ */
+ if (pDevObj == g_pDevObjStub)
+ {
+ PSUPDRVNTPROTECT pNtProtect = NULL;
+ rc = supdrvNtProtectCreate(&pNtProtect, PsGetProcessId(PsGetCurrentProcess()),
+ kSupDrvNtProtectKind_StubUnverified, true /*fLink*/);
+ if (RT_SUCCESS(rc))
+ {
+ rc = supdrvNtProtectFindAssociatedCsrss(pNtProtect);
+ if (RT_SUCCESS(rc))
+ rc = supdrvNtProtectVerifyProcess(pNtProtect);
+ if (RT_SUCCESS(rc))
+ {
+ pFileObj->FsContext = pNtProtect; /* Keeps reference. */
+ return supdrvNtCompleteRequestEx(STATUS_SUCCESS, FILE_OPENED, pIrp);
+ }
+
+ supdrvNtProtectRelease(pNtProtect);
+ }
+ LogRel(("vboxdrv: Declined %p access to VBoxDrvStub: rc=%d\n", PsGetProcessId(PsGetCurrentProcess()), rc));
+ }
+ /*
+ * Unrestricted access is only granted to a process in the
+ * VmProcessUnconfirmed state that checks out correctly and is
+ * allowed to transition to VmProcessConfirmed. Again, only one
+ * session per process.
+ */
+ else if (pDevObj != g_pDevObjUsr)
+ {
+ PSUPDRVNTPROTECT pNtProtect = supdrvNtProtectLookup(PsGetProcessId(PsGetCurrentProcess()));
+ if (pNtProtect)
+ {
+ if (pNtProtect->enmProcessKind == kSupDrvNtProtectKind_VmProcessUnconfirmed)
+ {
+ rc = supdrvNtProtectVerifyProcess(pNtProtect);
+ if (RT_SUCCESS(rc))
+ {
+ /* Create a session. */
+ PSUPDRVSESSION pSession;
+ rc = supdrvCreateSession(pDevExt, true /*fUser*/, pDevObj == g_pDevObjSys /*fUnrestricted*/,
+ &pSession);
+ if (RT_SUCCESS(rc))
+ {
+#ifdef VBOXDRV_WITH_SID_TO_UID_MAPPING
+ rc = supdrvNtUserIdMakeForSession(pSession);
+ if (RT_SUCCESS(rc))
+#endif
+ rc = supdrvSessionHashTabInsert(pDevExt, pSession, (PSUPDRVSESSION *)&pFileObj->FsContext, NULL);
+ supdrvSessionRelease(pSession);
+ if (RT_SUCCESS(rc))
+ {
+ pSession->pNtProtect = pNtProtect; /* Keeps reference. */
+ return supdrvNtCompleteRequestEx(STATUS_SUCCESS, FILE_OPENED, pIrp);
+ }
+ }
+
+ /* No second attempt. */
+ RTSpinlockAcquire(g_hNtProtectLock);
+ if (pNtProtect->enmProcessKind == kSupDrvNtProtectKind_VmProcessConfirmed)
+ pNtProtect->enmProcessKind = kSupDrvNtProtectKind_VmProcessDead;
+ RTSpinlockRelease(g_hNtProtectLock);
+
+ LogRel(("vboxdrv: supdrvCreateSession failed for process %p: rc=%d.\n",
+ PsGetProcessId(PsGetCurrentProcess()), rc));
+ }
+ else
+ LogRel(("vboxdrv: Process %p failed process verification: rc=%d.\n",
+ PsGetProcessId(PsGetCurrentProcess()), rc));
+ }
+ else
+ {
+ LogRel(("vboxdrv: %p is not a budding VM process (enmProcessKind=%d).\n",
+ PsGetProcessId(PsGetCurrentProcess()), pNtProtect->enmProcessKind));
+ rc = VERR_SUPDRV_NOT_BUDDING_VM_PROCESS_2;
+ }
+ supdrvNtProtectRelease(pNtProtect);
+ }
+ else
+ {
+ LogRel(("vboxdrv: %p is not a budding VM process.\n", PsGetProcessId(PsGetCurrentProcess())));
+ rc = VERR_SUPDRV_NOT_BUDDING_VM_PROCESS_1;
+ }
+ }
+ /*
+ * Call common code to create an unprivileged session.
+ */
+ else
+ {
+ PSUPDRVSESSION pSession;
+ rc = supdrvCreateSession(pDevExt, true /*fUser*/, false /*fUnrestricted*/, &pSession);
+ if (RT_SUCCESS(rc))
+ {
+#ifdef VBOXDRV_WITH_SID_TO_UID_MAPPING
+ rc = supdrvNtUserIdMakeForSession(pSession);
+ if (RT_SUCCESS(rc))
+#endif
+ rc = supdrvSessionHashTabInsert(pDevExt, pSession, (PSUPDRVSESSION *)&pFileObj->FsContext, NULL);
+ supdrvSessionRelease(pSession);
+ if (RT_SUCCESS(rc))
+ {
+ pFileObj->FsContext = pSession; /* Keeps reference. No race. */
+ pSession->pNtProtect = NULL;
+ return supdrvNtCompleteRequestEx(STATUS_SUCCESS, FILE_OPENED, pIrp);
+ }
+ }
+ }
+
+#else /* !VBOX_WITH_HARDENING */
+ /*
+ * Call common code to create a session.
+ */
+ pFileObj->FsContext = NULL;
+ PSUPDRVSESSION pSession;
+ rc = supdrvCreateSession(pDevExt, true /*fUser*/, pDevObj == g_pDevObjSys /*fUnrestricted*/, &pSession);
+ if (RT_SUCCESS(rc))
+ {
+# ifdef VBOXDRV_WITH_SID_TO_UID_MAPPING
+ rc = supdrvNtUserIdMakeForSession(pSession);
+ if (RT_SUCCESS(rc))
+# endif
+ rc = supdrvSessionHashTabInsert(pDevExt, pSession, (PSUPDRVSESSION *)&pFileObj->FsContext, NULL);
+ supdrvSessionRelease(pSession);
+ if (RT_SUCCESS(rc))
+ return supdrvNtCompleteRequestEx(STATUS_SUCCESS, FILE_OPENED, pIrp);
+
+ }
+#endif /* !VBOX_WITH_HARDENING */
+
+ /* bail out */
+ rcNt = VBoxDrvNtErr2NtStatus(rc);
+ }
+ }
+
+ Assert(!NT_SUCCESS(rcNt));
+ pFileObj->FsContext = NULL;
+ return supdrvNtCompleteRequest(rcNt, pIrp); /* Note. the IoStatus is completely ignored on error. */
+}
+
+
+/**
+ * Clean up file handle entry point.
+ *
+ * This is called when the last handle reference is released, or something like
+ * that. In the case of IoGetDeviceObjectPointer, this is called as it closes
+ * the handle, however it will go on using the file object afterwards...
+ *
+ * @param pDevObj Device object.
+ * @param pIrp Request packet.
+ */
+NTSTATUS _stdcall VBoxDrvNtCleanup(PDEVICE_OBJECT pDevObj, PIRP pIrp)
+{
+ PSUPDRVDEVEXT pDevExt = SUPDRVNT_GET_DEVEXT(pDevObj);
+ PIO_STACK_LOCATION pStack = IoGetCurrentIrpStackLocation(pIrp);
+ PFILE_OBJECT pFileObj = pStack->FileObject;
+
+#ifdef VBOX_WITH_HARDENING
+ if (pDevObj == g_pDevObjStub)
+ {
+ PSUPDRVNTPROTECT pNtProtect = (PSUPDRVNTPROTECT)pFileObj->FsContext;
+ Log(("VBoxDrvNtCleanup: pDevExt=%p pFileObj=%p pNtProtect=%p\n", pDevExt, pFileObj, pNtProtect));
+ if (pNtProtect)
+ {
+ supdrvNtProtectRelease(pNtProtect);
+ pFileObj->FsContext = NULL;
+ }
+ }
+ else if (pDevObj == g_pDevObjErrorInfo)
+ supdrvNtErrorInfoCleanupProcess(PsGetCurrentProcessId());
+ else
+#endif
+ {
+ PSUPDRVSESSION pSession = supdrvSessionHashTabLookup(pDevExt, RTProcSelf(), RTR0ProcHandleSelf(),
+ (PSUPDRVSESSION *)&pFileObj->FsContext);
+ Log(("VBoxDrvNtCleanup: pDevExt=%p pFileObj=%p pSession=%p\n", pDevExt, pFileObj, pSession));
+ if (pSession)
+ {
+ supdrvSessionHashTabRemove(pDevExt, pSession, NULL);
+ supdrvSessionRelease(pSession); /* Drops the reference from supdrvSessionHashTabLookup. */
+ }
+ }
+
+ return supdrvNtCompleteRequest(STATUS_SUCCESS, pIrp);
+}
+
+
+/**
+ * Close file entry point.
+ *
+ * @param pDevObj Device object.
+ * @param pIrp Request packet.
+ */
+NTSTATUS _stdcall VBoxDrvNtClose(PDEVICE_OBJECT pDevObj, PIRP pIrp)
+{
+ PSUPDRVDEVEXT pDevExt = SUPDRVNT_GET_DEVEXT(pDevObj);
+ PIO_STACK_LOCATION pStack = IoGetCurrentIrpStackLocation(pIrp);
+ PFILE_OBJECT pFileObj = pStack->FileObject;
+
+#ifdef VBOX_WITH_HARDENING
+ if (pDevObj == g_pDevObjStub)
+ {
+ PSUPDRVNTPROTECT pNtProtect = (PSUPDRVNTPROTECT)pFileObj->FsContext;
+ Log(("VBoxDrvNtClose: pDevExt=%p pFileObj=%p pNtProtect=%p\n", pDevExt, pFileObj, pNtProtect));
+ if (pNtProtect)
+ {
+ supdrvNtProtectRelease(pNtProtect);
+ pFileObj->FsContext = NULL;
+ }
+ }
+ else if (pDevObj == g_pDevObjErrorInfo)
+ supdrvNtErrorInfoCleanupProcess(PsGetCurrentProcessId());
+ else
+#endif
+ {
+ PSUPDRVSESSION pSession = supdrvSessionHashTabLookup(pDevExt, RTProcSelf(), RTR0ProcHandleSelf(),
+ (PSUPDRVSESSION *)&pFileObj->FsContext);
+ Log(("VBoxDrvNtCleanup: pDevExt=%p pFileObj=%p pSession=%p\n", pDevExt, pFileObj, pSession));
+ if (pSession)
+ {
+ supdrvSessionHashTabRemove(pDevExt, pSession, NULL);
+ supdrvSessionRelease(pSession); /* Drops the reference from supdrvSessionHashTabLookup. */
+ }
+ }
+
+ return supdrvNtCompleteRequest(STATUS_SUCCESS, pIrp);
+}
+
+
+#ifdef VBOXDRV_WITH_FAST_IO
+/**
+ * Fast I/O device control callback.
+ *
+ * This performs no buffering, neither on the way in or out.
+ *
+ * @returns TRUE if handled, FALSE if the normal I/O control routine should be
+ * called.
+ * @param pFileObj The file object.
+ * @param fWait Whether it's a blocking call
+ * @param pvInput The input buffer as specified by the user.
+ * @param cbInput The size of the input buffer.
+ * @param pvOutput The output buffer as specfied by the user.
+ * @param cbOutput The size of the output buffer.
+ * @param uCmd The I/O command/function being invoked.
+ * @param pIoStatus Where to return the status of the operation.
+ * @param pDevObj The device object..
+ */
+static BOOLEAN _stdcall VBoxDrvNtFastIoDeviceControl(PFILE_OBJECT pFileObj, BOOLEAN fWait, PVOID pvInput, ULONG cbInput,
+ PVOID pvOutput, ULONG cbOutput, ULONG uCmd,
+ PIO_STATUS_BLOCK pIoStatus, PDEVICE_OBJECT pDevObj)
+{
+ RT_NOREF1(fWait);
+
+ /*
+ * Only the normal devices, not the stub or error info ones.
+ */
+ if (pDevObj != g_pDevObjSys && pDevObj != g_pDevObjUsr)
+ {
+ pIoStatus->Status = STATUS_NOT_SUPPORTED;
+ pIoStatus->Information = 0;
+ return TRUE;
+ }
+
+ /*
+ * Check the input a little bit and get a the session references.
+ */
+ PSUPDRVDEVEXT pDevExt = SUPDRVNT_GET_DEVEXT(pDevObj);
+ PSUPDRVSESSION pSession = supdrvSessionHashTabLookup(pDevExt, RTProcSelf(), RTR0ProcHandleSelf(),
+ (PSUPDRVSESSION *)&pFileObj->FsContext);
+ if (!pSession)
+ {
+ pIoStatus->Status = STATUS_TRUST_FAILURE;
+ pIoStatus->Information = 0;
+ return TRUE;
+ }
+
+ if (pSession->fUnrestricted)
+ {
+#if defined(VBOX_WITH_HARDENING) && !defined(VBOX_WITHOUT_DEBUGGER_CHECKS)
+ if (supdrvNtIsDebuggerAttached())
+ {
+ pIoStatus->Status = STATUS_TRUST_FAILURE;
+ pIoStatus->Information = 0;
+ supdrvSessionRelease(pSession);
+ return TRUE;
+ }
+#endif
+
+ /*
+ * Deal with the 2-3 high-speed IOCtl that takes their arguments from
+ * the session and iCmd, and does not return anything.
+ */
+ if ( (uCmd & 3) == METHOD_NEITHER
+ && (uint32_t)((uCmd - SUP_IOCTL_FAST_DO_FIRST) >> 2) < (uint32_t)32)
+ {
+ int rc = supdrvIOCtlFast((uCmd - SUP_IOCTL_FAST_DO_FIRST) >> 2,
+ (unsigned)(uintptr_t)pvOutput/* VMCPU id */,
+ pDevExt, pSession);
+ pIoStatus->Status = RT_SUCCESS(rc) ? STATUS_SUCCESS : STATUS_INVALID_PARAMETER;
+ pIoStatus->Information = 0; /* Could be used to pass rc if we liked. */
+ supdrvSessionRelease(pSession);
+ return TRUE;
+ }
+ }
+
+ /*
+ * The normal path.
+ */
+ NTSTATUS rcNt;
+ unsigned cbOut = 0;
+ int rc = 0;
+ Log2(("VBoxDrvNtFastIoDeviceControl(%p): ioctl=%#x pvIn=%p cbIn=%#x pvOut=%p cbOut=%#x pSession=%p\n",
+ pDevExt, uCmd, pvInput, cbInput, pvOutput, cbOutput, pSession));
+
+# ifdef RT_ARCH_AMD64
+ /* Don't allow 32-bit processes to do any I/O controls. */
+ if (!IoIs32bitProcess(NULL))
+# endif
+ {
+ /*
+ * In this fast I/O device control path we have to do our own buffering.
+ */
+ /* Verify that the I/O control function matches our pattern. */
+ if ((uCmd & 0x3) == METHOD_BUFFERED)
+ {
+ /* Get the header so we can validate it a little bit against the
+ parameters before allocating any memory kernel for the reqest. */
+ SUPREQHDR Hdr;
+ if (cbInput >= sizeof(Hdr) && cbOutput >= sizeof(Hdr))
+ {
+ __try
+ {
+ RtlCopyMemory(&Hdr, pvInput, sizeof(Hdr));
+ rcNt = STATUS_SUCCESS;
+ }
+ __except(EXCEPTION_EXECUTE_HANDLER)
+ {
+ rcNt = GetExceptionCode();
+ Hdr.cbIn = Hdr.cbOut = 0; /* shut up MSC */
+ }
+ }
+ else
+ {
+ Hdr.cbIn = Hdr.cbOut = 0; /* shut up MSC */
+ rcNt = STATUS_INVALID_PARAMETER;
+ }
+ if (NT_SUCCESS(rcNt))
+ {
+ /* Verify that the sizes in the request header are correct. */
+ ULONG cbBuf = RT_MAX(cbInput, cbOutput);
+ if ( cbInput == Hdr.cbIn
+ && cbOutput == Hdr.cbOut
+ && cbBuf < _1M*16)
+ {
+ /* Allocate a buffer and copy all the input into it. */
+ PSUPREQHDR pHdr = (PSUPREQHDR)ExAllocatePoolWithTag(g_enmNonPagedPoolType, cbBuf, 'VBox');
+ if (pHdr)
+ {
+ __try
+ {
+ RtlCopyMemory(pHdr, pvInput, cbInput);
+ if (cbInput < cbBuf)
+ RtlZeroMemory((uint8_t *)pHdr + cbInput, cbBuf - cbInput);
+ if (!memcmp(pHdr, &Hdr, sizeof(Hdr)))
+ rcNt = STATUS_SUCCESS;
+ else
+ rcNt = STATUS_INVALID_PARAMETER;
+ }
+ __except(EXCEPTION_EXECUTE_HANDLER)
+ {
+ rcNt = GetExceptionCode();
+ }
+ if (NT_SUCCESS(rcNt))
+ {
+ /*
+ * Now call the common code to do the real work.
+ */
+ rc = supdrvIOCtl(uCmd, pDevExt, pSession, pHdr, cbBuf);
+ if (RT_SUCCESS(rc))
+ {
+ /*
+ * Copy back the result.
+ */
+ cbOut = pHdr->cbOut;
+ if (cbOut > cbOutput)
+ {
+ cbOut = cbOutput;
+ OSDBGPRINT(("VBoxDrvNtFastIoDeviceControl: too much output! %#x > %#x; uCmd=%#x!\n",
+ pHdr->cbOut, cbOut, uCmd));
+ }
+ if (cbOut)
+ {
+ __try
+ {
+ RtlCopyMemory(pvOutput, pHdr, cbOut);
+ rcNt = STATUS_SUCCESS;
+ }
+ __except(EXCEPTION_EXECUTE_HANDLER)
+ {
+ rcNt = GetExceptionCode();
+ }
+ }
+ else
+ rcNt = STATUS_SUCCESS;
+ }
+ else if (rc == VERR_INVALID_PARAMETER)
+ rcNt = STATUS_INVALID_PARAMETER;
+ else
+ rcNt = STATUS_NOT_SUPPORTED;
+ Log2(("VBoxDrvNtFastIoDeviceControl: returns %#x cbOut=%d rc=%#x\n", rcNt, cbOut, rc));
+ }
+ else
+ Log(("VBoxDrvNtFastIoDeviceControl: Error reading %u bytes of user memory at %p (uCmd=%#x)\n",
+ cbInput, pvInput, uCmd));
+ ExFreePoolWithTag(pHdr, 'VBox');
+ }
+ else
+ rcNt = STATUS_NO_MEMORY;
+ }
+ else
+ {
+ Log(("VBoxDrvNtFastIoDeviceControl: Mismatching sizes (%#x) - Hdr=%#lx/%#lx Irp=%#lx/%#lx!\n",
+ uCmd, Hdr.cbIn, Hdr.cbOut, cbInput, cbOutput));
+ rcNt = STATUS_INVALID_PARAMETER;
+ }
+ }
+ }
+ else
+ {
+ Log(("VBoxDrvNtFastIoDeviceControl: not buffered request (%#x) - not supported\n", uCmd));
+ rcNt = STATUS_NOT_SUPPORTED;
+ }
+ }
+# ifdef RT_ARCH_AMD64
+ else
+ {
+ Log(("VBoxDrvNtFastIoDeviceControl: WOW64 req - not supported\n"));
+ rcNt = STATUS_NOT_SUPPORTED;
+ }
+# endif
+
+ /* complete the request. */
+ pIoStatus->Status = rcNt;
+ pIoStatus->Information = cbOut;
+ supdrvSessionRelease(pSession);
+ return TRUE; /* handled. */
+}
+#endif /* VBOXDRV_WITH_FAST_IO */
+
+
+/**
+ * Device I/O Control entry point.
+ *
+ * @param pDevObj Device object.
+ * @param pIrp Request packet.
+ */
+NTSTATUS _stdcall VBoxDrvNtDeviceControl(PDEVICE_OBJECT pDevObj, PIRP pIrp)
+{
+ VBOXDRV_COMPLETE_IRP_AND_RETURN_IF_STUB_OR_ERROR_INFO_DEV(pDevObj, pIrp);
+
+ PSUPDRVDEVEXT pDevExt = SUPDRVNT_GET_DEVEXT(pDevObj);
+ PIO_STACK_LOCATION pStack = IoGetCurrentIrpStackLocation(pIrp);
+ PSUPDRVSESSION pSession = supdrvSessionHashTabLookup(pDevExt, RTProcSelf(), RTR0ProcHandleSelf(),
+ (PSUPDRVSESSION *)&pStack->FileObject->FsContext);
+
+ if (!RT_VALID_PTR(pSession))
+ return supdrvNtCompleteRequest(STATUS_TRUST_FAILURE, pIrp);
+
+ /*
+ * 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)
+ {
+#if defined(VBOX_WITH_HARDENING) && !defined(VBOX_WITHOUT_DEBUGGER_CHECKS)
+ if (supdrvNtIsDebuggerAttached())
+ {
+ supdrvSessionRelease(pSession);
+ return supdrvNtCompleteRequest(STATUS_TRUST_FAILURE, pIrp);
+ }
+#endif
+
+ ULONG uCmd = pStack->Parameters.DeviceIoControl.IoControlCode;
+ if ( (uCmd & 3) == METHOD_NEITHER
+ && (uint32_t)((uCmd - SUP_IOCTL_FAST_DO_FIRST) >> 2) < (uint32_t)32)
+ {
+ int rc = supdrvIOCtlFast((uCmd - SUP_IOCTL_FAST_DO_FIRST) >> 2,
+ (unsigned)(uintptr_t)pIrp->UserBuffer /* VMCPU id */,
+ pDevExt, pSession);
+
+ /* Complete the I/O request. */
+ supdrvSessionRelease(pSession);
+ return supdrvNtCompleteRequest(RT_SUCCESS(rc) ? STATUS_SUCCESS : STATUS_INVALID_PARAMETER, pIrp);
+ }
+ }
+
+ return VBoxDrvNtDeviceControlSlow(pDevExt, pSession, pIrp, pStack);
+}
+
+
+/**
+ * Worker for VBoxDrvNtDeviceControl that takes the slow IOCtl functions.
+ *
+ * @returns NT status code.
+ *
+ * @param pDevExt Device extension.
+ * @param pSession The session.
+ * @param pIrp Request packet.
+ * @param pStack The stack location containing the DeviceControl parameters.
+ */
+static int VBoxDrvNtDeviceControlSlow(PSUPDRVDEVEXT pDevExt, PSUPDRVSESSION pSession, PIRP pIrp, PIO_STACK_LOCATION pStack)
+{
+ NTSTATUS rcNt;
+ uint32_t cbOut = 0;
+ int rc = 0;
+ Log2(("VBoxDrvNtDeviceControlSlow(%p,%p): ioctl=%#x pBuf=%p cbIn=%#x cbOut=%#x pSession=%p\n",
+ pDevExt, pIrp, pStack->Parameters.DeviceIoControl.IoControlCode,
+ pIrp->AssociatedIrp.SystemBuffer, pStack->Parameters.DeviceIoControl.InputBufferLength,
+ pStack->Parameters.DeviceIoControl.OutputBufferLength, pSession));
+
+#ifdef RT_ARCH_AMD64
+ /* Don't allow 32-bit processes to do any I/O controls. */
+ if (!IoIs32bitProcess(pIrp))
+#endif
+ {
+ /* Verify that it's a buffered CTL. */
+ if ((pStack->Parameters.DeviceIoControl.IoControlCode & 0x3) == METHOD_BUFFERED)
+ {
+ /* Verify that the sizes in the request header are correct. */
+ PSUPREQHDR pHdr = (PSUPREQHDR)pIrp->AssociatedIrp.SystemBuffer;
+ if ( pStack->Parameters.DeviceIoControl.InputBufferLength >= sizeof(*pHdr)
+ && pStack->Parameters.DeviceIoControl.InputBufferLength == pHdr->cbIn
+ && pStack->Parameters.DeviceIoControl.OutputBufferLength == pHdr->cbOut)
+ {
+ /* 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 = supdrvIOCtl(pStack->Parameters.DeviceIoControl.IoControlCode, pDevExt, pSession, pHdr,
+ RT_MAX(pHdr->cbIn, pHdr->cbOut));
+ if (!rc)
+ {
+ rcNt = STATUS_SUCCESS;
+ cbOut = pHdr->cbOut;
+ if (cbOut > pStack->Parameters.DeviceIoControl.OutputBufferLength)
+ {
+ cbOut = pStack->Parameters.DeviceIoControl.OutputBufferLength;
+ OSDBGPRINT(("VBoxDrvNtDeviceControlSlow: too much output! %#x > %#x; uCmd=%#x!\n",
+ pHdr->cbOut, cbOut, pStack->Parameters.DeviceIoControl.IoControlCode));
+ }
+ }
+ else
+ rcNt = STATUS_INVALID_PARAMETER;
+ Log2(("VBoxDrvNtDeviceControlSlow: returns %#x cbOut=%d rc=%#x\n", rcNt, cbOut, rc));
+ }
+ else
+ {
+ Log(("VBoxDrvNtDeviceControlSlow: Mismatching sizes (%#x) - Hdr=%#lx/%#lx Irp=%#lx/%#lx!\n",
+ pStack->Parameters.DeviceIoControl.IoControlCode,
+ pStack->Parameters.DeviceIoControl.InputBufferLength >= sizeof(*pHdr) ? pHdr->cbIn : 0,
+ pStack->Parameters.DeviceIoControl.InputBufferLength >= sizeof(*pHdr) ? pHdr->cbOut : 0,
+ pStack->Parameters.DeviceIoControl.InputBufferLength,
+ pStack->Parameters.DeviceIoControl.OutputBufferLength));
+ rcNt = STATUS_INVALID_PARAMETER;
+ }
+ }
+ else
+ {
+ Log(("VBoxDrvNtDeviceControlSlow: not buffered request (%#x) - not supported\n",
+ pStack->Parameters.DeviceIoControl.IoControlCode));
+ rcNt = STATUS_NOT_SUPPORTED;
+ }
+ }
+#ifdef RT_ARCH_AMD64
+ else
+ {
+ Log(("VBoxDrvNtDeviceControlSlow: WOW64 req - not supported\n"));
+ rcNt = STATUS_NOT_SUPPORTED;
+ }
+#endif
+
+ /* complete the request. */
+ pIrp->IoStatus.Status = rcNt;
+ pIrp->IoStatus.Information = cbOut;
+ supdrvSessionRelease(pSession);
+ IoCompleteRequest(pIrp, IO_NO_INCREMENT);
+ return rcNt;
+}
+
+
+/**
+ * Internal Device I/O Control entry point, used for IDC.
+ *
+ * @param pDevObj Device object.
+ * @param pIrp Request packet.
+ */
+NTSTATUS _stdcall VBoxDrvNtInternalDeviceControl(PDEVICE_OBJECT pDevObj, PIRP pIrp)
+{
+ VBOXDRV_COMPLETE_IRP_AND_RETURN_IF_STUB_OR_ERROR_INFO_DEV(pDevObj, pIrp);
+
+ PSUPDRVDEVEXT pDevExt = SUPDRVNT_GET_DEVEXT(pDevObj);
+ PIO_STACK_LOCATION pStack = IoGetCurrentIrpStackLocation(pIrp);
+ PFILE_OBJECT pFileObj = pStack ? pStack->FileObject : NULL;
+ PSUPDRVSESSION pSession = pFileObj ? (PSUPDRVSESSION)pFileObj->FsContext : NULL;
+ NTSTATUS rcNt;
+ unsigned cbOut = 0;
+ int rc = 0;
+ Log2(("VBoxDrvNtInternalDeviceControl(%p,%p): ioctl=%#x pBuf=%p cbIn=%#x cbOut=%#x pSession=%p\n",
+ pDevExt, pIrp, pStack->Parameters.DeviceIoControl.IoControlCode,
+ pIrp->AssociatedIrp.SystemBuffer, pStack->Parameters.DeviceIoControl.InputBufferLength,
+ pStack->Parameters.DeviceIoControl.OutputBufferLength, pSession));
+
+ /* Verify that it's a buffered CTL. */
+ if ((pStack->Parameters.DeviceIoControl.IoControlCode & 0x3) == METHOD_BUFFERED)
+ {
+ /* Verify the pDevExt in the session. */
+ if ( pStack->Parameters.DeviceIoControl.IoControlCode != SUPDRV_IDC_REQ_CONNECT
+ ? RT_VALID_PTR(pSession) && pSession->pDevExt == pDevExt
+ : !pSession
+ )
+ {
+ /* Verify that the size in the request header is correct. */
+ PSUPDRVIDCREQHDR pHdr = (PSUPDRVIDCREQHDR)pIrp->AssociatedIrp.SystemBuffer;
+ if ( pStack->Parameters.DeviceIoControl.InputBufferLength >= sizeof(*pHdr)
+ && pStack->Parameters.DeviceIoControl.InputBufferLength == pHdr->cb
+ && pStack->Parameters.DeviceIoControl.OutputBufferLength == pHdr->cb)
+ {
+ /*
+ * Call the generic code.
+ *
+ * Note! Connect and disconnect requires some extra attention
+ * in order to get the session handling right.
+ */
+ if (pStack->Parameters.DeviceIoControl.IoControlCode == SUPDRV_IDC_REQ_DISCONNECT)
+ pFileObj->FsContext = NULL;
+
+ rc = supdrvIDC(pStack->Parameters.DeviceIoControl.IoControlCode, pDevExt, pSession, pHdr);
+ if (!rc)
+ {
+ if (pStack->Parameters.DeviceIoControl.IoControlCode == SUPDRV_IDC_REQ_CONNECT)
+ pFileObj->FsContext = ((PSUPDRVIDCREQCONNECT)pHdr)->u.Out.pSession;
+
+ rcNt = STATUS_SUCCESS;
+ cbOut = pHdr->cb;
+ }
+ else
+ {
+ rcNt = STATUS_INVALID_PARAMETER;
+ if (pStack->Parameters.DeviceIoControl.IoControlCode == SUPDRV_IDC_REQ_DISCONNECT)
+ pFileObj->FsContext = pSession;
+ }
+ Log2(("VBoxDrvNtInternalDeviceControl: returns %#x/rc=%#x\n", rcNt, rc));
+ }
+ else
+ {
+ Log(("VBoxDrvNtInternalDeviceControl: Mismatching sizes (%#x) - Hdr=%#lx Irp=%#lx/%#lx!\n",
+ pStack->Parameters.DeviceIoControl.IoControlCode,
+ pStack->Parameters.DeviceIoControl.InputBufferLength >= sizeof(*pHdr) ? pHdr->cb : 0,
+ pStack->Parameters.DeviceIoControl.InputBufferLength,
+ pStack->Parameters.DeviceIoControl.OutputBufferLength));
+ rcNt = STATUS_INVALID_PARAMETER;
+ }
+ }
+ else
+ rcNt = STATUS_NOT_SUPPORTED;
+ }
+ else
+ {
+ Log(("VBoxDrvNtInternalDeviceControl: not buffered request (%#x) - not supported\n",
+ pStack->Parameters.DeviceIoControl.IoControlCode));
+ rcNt = STATUS_NOT_SUPPORTED;
+ }
+
+ /* complete the request. */
+ pIrp->IoStatus.Status = rcNt;
+ pIrp->IoStatus.Information = cbOut;
+ IoCompleteRequest(pIrp, IO_NO_INCREMENT);
+ return rcNt;
+}
+
+
+/**
+ * Implementation of the read major function for VBoxDrvErrorInfo.
+ *
+ * This is a stub function for the other devices.
+ *
+ * @returns NT status code.
+ * @param pDevObj The device object.
+ * @param pIrp The I/O request packet.
+ */
+NTSTATUS _stdcall VBoxDrvNtRead(PDEVICE_OBJECT pDevObj, PIRP pIrp)
+{
+ Log(("VBoxDrvNtRead\n"));
+ RT_NOREF1(pDevObj);
+
+ NTSTATUS rcNt;
+ pIrp->IoStatus.Information = 0;
+
+#ifdef VBOX_WITH_HARDENING
+ /*
+ * VBoxDrvErrorInfo?
+ */
+ if (pDevObj == g_pDevObjErrorInfo)
+ {
+ PIO_STACK_LOCATION pStack = IoGetCurrentIrpStackLocation(pIrp);
+ if ( pStack
+ && (pIrp->Flags & IRP_BUFFERED_IO))
+ {
+ /*
+ * Look up the process error information.
+ */
+ HANDLE hCurThreadId = PsGetCurrentThreadId();
+ HANDLE hCurProcessId = PsGetCurrentProcessId();
+ int rc = RTSemMutexRequestNoResume(g_hErrorInfoLock, RT_INDEFINITE_WAIT);
+ if (RT_SUCCESS(rc))
+ {
+ PSUPDRVNTERRORINFO pMatch = NULL;
+ PSUPDRVNTERRORINFO pCur;
+ RTListForEach(&g_ErrorInfoHead, pCur, SUPDRVNTERRORINFO, ListEntry)
+ {
+ if ( pCur->hProcessId == hCurProcessId
+ && pCur->hThreadId == hCurThreadId)
+ {
+ pMatch = pCur;
+ break;
+ }
+ }
+
+ /*
+ * Did we find error info and is the caller requesting data within it?
+ * If so, check the destination buffer and copy the data into it.
+ */
+ if ( pMatch
+ && pStack->Parameters.Read.ByteOffset.QuadPart < pMatch->cchErrorInfo
+ && pStack->Parameters.Read.ByteOffset.QuadPart >= 0)
+ {
+ PVOID pvDstBuf = pIrp->AssociatedIrp.SystemBuffer;
+ if (pvDstBuf)
+ {
+ uint32_t offRead = (uint32_t)pStack->Parameters.Read.ByteOffset.QuadPart;
+ uint32_t cbToRead = pMatch->cchErrorInfo - offRead;
+ if (cbToRead < pStack->Parameters.Read.Length)
+ RT_BZERO((uint8_t *)pvDstBuf + cbToRead, pStack->Parameters.Read.Length - cbToRead);
+ else
+ cbToRead = pStack->Parameters.Read.Length;
+ memcpy(pvDstBuf, &pMatch->szErrorInfo[offRead], cbToRead);
+ pIrp->IoStatus.Information = cbToRead;
+
+ rcNt = STATUS_SUCCESS;
+ }
+ else
+ rcNt = STATUS_INVALID_ADDRESS;
+ }
+ /*
+ * End of file. Free the info.
+ */
+ else if (pMatch)
+ {
+ RTListNodeRemove(&pMatch->ListEntry);
+ RTMemFree(pMatch);
+ rcNt = STATUS_END_OF_FILE;
+ }
+ /*
+ * We found no error info. Return EOF.
+ */
+ else
+ rcNt = STATUS_END_OF_FILE;
+
+ RTSemMutexRelease(g_hErrorInfoLock);
+ }
+ else
+ rcNt = STATUS_UNSUCCESSFUL;
+
+ /* Paranoia: Clear the buffer on failure. */
+ if (!NT_SUCCESS(rcNt))
+ {
+ PVOID pvDstBuf = pIrp->AssociatedIrp.SystemBuffer;
+ if ( pvDstBuf
+ && pStack->Parameters.Read.Length)
+ RT_BZERO(pvDstBuf, pStack->Parameters.Read.Length);
+ }
+ }
+ else
+ rcNt = STATUS_INVALID_PARAMETER;
+ }
+ else
+#endif /* VBOX_WITH_HARDENING */
+ {
+ /*
+ * Stub.
+ */
+ rcNt = STATUS_NOT_SUPPORTED;
+ }
+
+ /*
+ * Complete the request.
+ */
+ pIrp->IoStatus.Status = rcNt;
+ IoCompleteRequest(pIrp, IO_NO_INCREMENT);
+ return rcNt;
+}
+
+
+/**
+ * Stub function for functions we don't implemented.
+ *
+ * @returns STATUS_NOT_SUPPORTED
+ * @param pDevObj Device object.
+ * @param pIrp IRP.
+ */
+NTSTATUS _stdcall VBoxDrvNtNotSupportedStub(PDEVICE_OBJECT pDevObj, PIRP pIrp)
+{
+ Log(("VBoxDrvNtNotSupportedStub\n"));
+ NOREF(pDevObj);
+
+ pIrp->IoStatus.Information = 0;
+ pIrp->IoStatus.Status = STATUS_NOT_SUPPORTED;
+ IoCompleteRequest(pIrp, IO_NO_INCREMENT);
+
+ return STATUS_NOT_SUPPORTED;
+}
+
+
+/**
+ * ExRegisterCallback handler for power events
+ *
+ * @param pCallbackContext User supplied parameter (pDevObj)
+ * @param pvArgument1 First argument
+ * @param pvArgument2 Second argument
+ */
+VOID _stdcall VBoxPowerDispatchCallback(PVOID pCallbackContext, PVOID pvArgument1, PVOID pvArgument2)
+{
+ /*PDEVICE_OBJECT pDevObj = (PDEVICE_OBJECT)pCallbackContext;*/ RT_NOREF1(pCallbackContext);
+ Log(("VBoxPowerDispatchCallback: %x %x\n", pvArgument1, pvArgument2));
+
+ /* Power change imminent? */
+ if ((uintptr_t)pvArgument1 == PO_CB_SYSTEM_STATE_LOCK)
+ {
+ if (pvArgument2 == NULL)
+ Log(("VBoxPowerDispatchCallback: about to go into suspend mode!\n"));
+ else
+ Log(("VBoxPowerDispatchCallback: resumed!\n"));
+
+ /* Inform any clients that have registered themselves with IPRT. */
+ RTPowerSignalEvent(pvArgument2 == NULL ? RTPOWEREVENT_SUSPEND : RTPOWEREVENT_RESUME);
+ }
+}
+
+
+/**
+ * Called to clean up the session structure before it's freed.
+ *
+ * @param pDevExt The device globals.
+ * @param pSession The session that's being cleaned up.
+ */
+void VBOXCALL supdrvOSCleanupSession(PSUPDRVDEVEXT pDevExt, PSUPDRVSESSION pSession)
+{
+#ifdef VBOX_WITH_HARDENING
+ if (pSession->pNtProtect)
+ {
+ supdrvNtProtectRelease(pSession->pNtProtect);
+ pSession->pNtProtect = NULL;
+ }
+ RT_NOREF1(pDevExt);
+#else
+ RT_NOREF2(pDevExt, pSession);
+#endif
+#ifdef VBOXDRV_WITH_SID_TO_UID_MAPPING
+ if (pSession->pNtUserId)
+ {
+ supdrvNtUserIdRelease(pSession->pNtUserId);
+ pSession->pNtUserId = NULL;
+ }
+#endif
+}
+
+
+void VBOXCALL supdrvOSSessionHashTabInserted(PSUPDRVDEVEXT pDevExt, PSUPDRVSESSION pSession, void *pvUser)
+{
+ NOREF(pDevExt); NOREF(pSession); NOREF(pvUser);
+}
+
+
+void VBOXCALL supdrvOSSessionHashTabRemoved(PSUPDRVDEVEXT pDevExt, PSUPDRVSESSION pSession, void *pvUser)
+{
+ NOREF(pDevExt); NOREF(pSession); NOREF(pvUser);
+}
+
+
+size_t VBOXCALL supdrvOSGipGetGroupTableSize(PSUPDRVDEVEXT pDevExt)
+{
+ NOREF(pDevExt);
+ uint32_t cMaxCpus = RTMpGetCount();
+ uint32_t cGroups = RTMpGetMaxCpuGroupCount();
+
+ return cGroups * RT_UOFFSETOF(SUPGIPCPUGROUP, aiCpuSetIdxs)
+ + RT_SIZEOFMEMB(SUPGIPCPUGROUP, aiCpuSetIdxs[0]) * cMaxCpus;
+}
+
+
+int VBOXCALL supdrvOSInitGipGroupTable(PSUPDRVDEVEXT pDevExt, PSUPGLOBALINFOPAGE pGip, size_t cbGipCpuGroups)
+{
+ Assert(cbGipCpuGroups > 0); NOREF(cbGipCpuGroups); NOREF(pDevExt);
+
+ unsigned const cGroups = RTMpGetMaxCpuGroupCount();
+ AssertReturn(cGroups > 0 && cGroups < RT_ELEMENTS(pGip->aoffCpuGroup), VERR_INTERNAL_ERROR_2);
+ pGip->cPossibleCpuGroups = cGroups;
+
+ PSUPGIPCPUGROUP pGroup = (PSUPGIPCPUGROUP)&pGip->aCPUs[pGip->cCpus];
+ for (uint32_t idxGroup = 0; idxGroup < cGroups; idxGroup++)
+ {
+ uint32_t cActive = 0;
+ uint32_t const cMax = RTMpGetCpuGroupCounts(idxGroup, &cActive);
+ uint32_t const cbNeeded = RT_UOFFSETOF_DYN(SUPGIPCPUGROUP, aiCpuSetIdxs[cMax]);
+ uintptr_t const offGroup = (uintptr_t)pGroup - (uintptr_t)pGip;
+ AssertReturn(cbNeeded <= cbGipCpuGroups, VERR_INTERNAL_ERROR_3);
+ AssertReturn(cActive <= cMax, VERR_INTERNAL_ERROR_4);
+ AssertReturn(offGroup == (uint32_t)offGroup, VERR_INTERNAL_ERROR_5);
+
+ pGip->aoffCpuGroup[idxGroup] = offGroup;
+ pGroup->cMembers = cActive;
+ pGroup->cMaxMembers = cMax;
+ for (uint32_t idxMember = 0; idxMember < cMax; idxMember++)
+ {
+ pGroup->aiCpuSetIdxs[idxMember] = RTMpSetIndexFromCpuGroupMember(idxGroup, idxMember);
+ Assert((unsigned)pGroup->aiCpuSetIdxs[idxMember] < pGip->cPossibleCpus);
+ }
+
+ /* advance. */
+ cbGipCpuGroups -= cbNeeded;
+ pGroup = (PSUPGIPCPUGROUP)&pGroup->aiCpuSetIdxs[cMax];
+ }
+
+ return VINF_SUCCESS;
+}
+
+
+void VBOXCALL supdrvOSGipInitGroupBitsForCpu(PSUPDRVDEVEXT pDevExt, PSUPGLOBALINFOPAGE pGip, PSUPGIPCPU pGipCpu)
+{
+ NOREF(pDevExt);
+
+ /*
+ * Translate the CPU index into a group and member.
+ */
+ PROCESSOR_NUMBER ProcNum = { 0, (UCHAR)pGipCpu->iCpuSet, 0 };
+ if (g_pfnKeGetProcessorNumberFromIndex)
+ {
+ NTSTATUS rcNt = g_pfnKeGetProcessorNumberFromIndex(pGipCpu->iCpuSet, &ProcNum);
+ if (NT_SUCCESS(rcNt))
+ Assert(ProcNum.Group < g_pfnKeQueryMaximumGroupCount());
+ else
+ {
+ AssertFailed();
+ ProcNum.Group = 0;
+ ProcNum.Number = pGipCpu->iCpuSet;
+ }
+ }
+ pGipCpu->iCpuGroup = ProcNum.Group;
+ pGipCpu->iCpuGroupMember = ProcNum.Number;
+
+ /*
+ * Update the group info. Just do this wholesale for now (doesn't scale well).
+ */
+ for (uint32_t idxGroup = 0; idxGroup < pGip->cPossibleCpuGroups; idxGroup++)
+ {
+ uint32_t offGroup = pGip->aoffCpuGroup[idxGroup];
+ if (offGroup != UINT32_MAX)
+ {
+ PSUPGIPCPUGROUP pGroup = (PSUPGIPCPUGROUP)((uintptr_t)pGip + offGroup);
+ uint32_t cActive = 0;
+ uint32_t cMax = RTMpGetCpuGroupCounts(idxGroup, &cActive);
+
+ AssertStmt(cMax == pGroup->cMaxMembers, cMax = pGroup->cMaxMembers);
+ AssertStmt(cActive <= cMax, cActive = cMax);
+ if (pGroup->cMembers != cActive)
+ ASMAtomicWriteU16(&pGroup->cMembers, cActive);
+
+ for (uint32_t idxMember = 0; idxMember < cMax; idxMember++)
+ {
+ int idxCpuSet = RTMpSetIndexFromCpuGroupMember(idxGroup, idxMember);
+ AssertMsg((unsigned)idxCpuSet < pGip->cPossibleCpus,
+ ("%d vs %d for %u.%u\n", idxCpuSet, pGip->cPossibleCpus, idxGroup, idxMember));
+
+ if (pGroup->aiCpuSetIdxs[idxMember] != idxCpuSet)
+ ASMAtomicWriteS16(&pGroup->aiCpuSetIdxs[idxMember], idxCpuSet);
+ }
+ }
+ }
+}
+
+
+/**
+ * Initializes any OS specific object creator fields.
+ */
+void VBOXCALL supdrvOSObjInitCreator(PSUPDRVOBJ pObj, PSUPDRVSESSION pSession)
+{
+ NOREF(pObj);
+ NOREF(pSession);
+}
+
+
+/**
+ * Checks if the session can access the object.
+ *
+ * @returns true if a decision has been made.
+ * @returns false if the default access policy should be applied.
+ *
+ * @param pObj The object in question.
+ * @param pSession The session wanting to access the object.
+ * @param pszObjName The object name, can be NULL.
+ * @param prc Where to store the result when returning true.
+ */
+bool VBOXCALL supdrvOSObjCanAccess(PSUPDRVOBJ pObj, PSUPDRVSESSION pSession, const char *pszObjName, int *prc)
+{
+ NOREF(pObj);
+ NOREF(pSession);
+ NOREF(pszObjName);
+ NOREF(prc);
+ return false;
+}
+
+
+/**
+ * Force async tsc mode (stub).
+ */
+bool VBOXCALL supdrvOSGetForcedAsyncTscMode(PSUPDRVDEVEXT pDevExt)
+{
+ RT_NOREF1(pDevExt);
+ return g_Options.fOptForceAsyncTsc != 0;
+}
+
+
+/**
+ * Whether the host takes CPUs offline during a suspend/resume operation.
+ */
+bool VBOXCALL supdrvOSAreCpusOfflinedOnSuspend(void)
+{
+ return false;
+}
+
+
+/**
+ * Whether the hardware TSC has been synchronized by the OS.
+ */
+bool VBOXCALL supdrvOSAreTscDeltasInSync(void)
+{
+ /* If IPRT didn't find KeIpiGenericCall we pretend windows(, the firmware,
+ or whoever) always configures TSCs perfectly. */
+ return !RTMpOnPairIsConcurrentExecSupported();
+}
+
+
+#define MY_SystemLoadGdiDriverInSystemSpaceInformation 54
+#define MY_SystemUnloadGdiDriverInformation 27
+
+typedef struct MYSYSTEMGDIDRIVERINFO
+{
+ UNICODE_STRING Name; /**< In: image file name. */
+ PVOID ImageAddress; /**< Out: the load address. */
+ PVOID SectionPointer; /**< Out: section object. */
+ PVOID EntryPointer; /**< Out: entry point address. */
+ PVOID ExportSectionPointer; /**< Out: export directory/section. */
+ ULONG ImageLength; /**< Out: SizeOfImage. */
+} MYSYSTEMGDIDRIVERINFO;
+
+extern "C" __declspec(dllimport) NTSTATUS NTAPI ZwSetSystemInformation(ULONG, PVOID, ULONG);
+
+int VBOXCALL supdrvOSLdrOpen(PSUPDRVDEVEXT pDevExt, PSUPDRVLDRIMAGE pImage, const char *pszFilename)
+{
+ pImage->pvNtSectionObj = NULL;
+ pImage->hMemLock = NIL_RTR0MEMOBJ;
+
+#ifdef VBOX_WITHOUT_NATIVE_R0_LOADER
+# ifndef RT_ARCH_X86
+# error "VBOX_WITHOUT_NATIVE_R0_LOADER is only safe on x86."
+# endif
+ NOREF(pDevExt); NOREF(pszFilename); NOREF(pImage);
+ return VERR_NOT_SUPPORTED;
+
+#else
+ /*
+ * Convert the filename from DOS UTF-8 to NT UTF-16.
+ */
+ size_t cwcFilename;
+ int rc = RTStrCalcUtf16LenEx(pszFilename, RTSTR_MAX, &cwcFilename);
+ if (RT_FAILURE(rc))
+ return rc;
+
+ PRTUTF16 pwcsFilename = (PRTUTF16)RTMemTmpAlloc((4 + cwcFilename + 1) * sizeof(RTUTF16));
+ if (!pwcsFilename)
+ return VERR_NO_TMP_MEMORY;
+
+ pwcsFilename[0] = '\\';
+ pwcsFilename[1] = '?';
+ pwcsFilename[2] = '?';
+ pwcsFilename[3] = '\\';
+ PRTUTF16 pwcsTmp = &pwcsFilename[4];
+ rc = RTStrToUtf16Ex(pszFilename, RTSTR_MAX, &pwcsTmp, cwcFilename + 1, NULL);
+ if (RT_SUCCESS(rc))
+ {
+ /*
+ * Try load it.
+ */
+ MYSYSTEMGDIDRIVERINFO Info;
+ RtlInitUnicodeString(&Info.Name, pwcsFilename);
+ Info.ImageAddress = NULL;
+ Info.SectionPointer = NULL;
+ Info.EntryPointer = NULL;
+ Info.ExportSectionPointer = NULL;
+ Info.ImageLength = 0;
+
+ NTSTATUS rcNt = ZwSetSystemInformation(MY_SystemLoadGdiDriverInSystemSpaceInformation, &Info, sizeof(Info));
+ if (NT_SUCCESS(rcNt))
+ {
+ pImage->pvImage = Info.ImageAddress;
+ pImage->pvNtSectionObj = Info.SectionPointer;
+ Log(("ImageAddress=%p SectionPointer=%p ImageLength=%#x cbImageBits=%#x rcNt=%#x '%ls'\n",
+ Info.ImageAddress, Info.SectionPointer, Info.ImageLength, pImage->cbImageBits, rcNt, Info.Name.Buffer));
+# ifdef DEBUG_bird
+ SUPR0Printf("ImageAddress=%p SectionPointer=%p ImageLength=%#x cbImageBits=%#x rcNt=%#x '%ls'\n",
+ Info.ImageAddress, Info.SectionPointer, Info.ImageLength, pImage->cbImageBits, rcNt, Info.Name.Buffer);
+# endif
+ if (pImage->cbImageBits == Info.ImageLength)
+ {
+ /*
+ * Lock down the entire image, just to be on the safe side.
+ */
+ rc = RTR0MemObjLockKernel(&pImage->hMemLock, pImage->pvImage, pImage->cbImageBits, RTMEM_PROT_READ);
+ if (RT_FAILURE(rc))
+ {
+ pImage->hMemLock = NIL_RTR0MEMOBJ;
+ supdrvOSLdrUnload(pDevExt, pImage);
+ }
+ }
+ else
+ {
+ supdrvOSLdrUnload(pDevExt, pImage);
+ rc = VERR_LDR_MISMATCH_NATIVE;
+ }
+ }
+ else
+ {
+ Log(("rcNt=%#x '%ls'\n", rcNt, pwcsFilename));
+ SUPR0Printf("VBoxDrv: rcNt=%x '%ws'\n", rcNt, pwcsFilename);
+ switch (rcNt)
+ {
+ case /* 0xc0000003 */ STATUS_INVALID_INFO_CLASS:
+# ifdef RT_ARCH_AMD64
+ /* Unwind will crash and BSOD, so no fallback here! */
+ rc = VERR_NOT_IMPLEMENTED;
+# else
+ /*
+ * Use the old way of loading the modules.
+ *
+ * Note! We do *NOT* try class 26 because it will probably
+ * not work correctly on terminal servers and such.
+ */
+ rc = VERR_NOT_SUPPORTED;
+# endif
+ break;
+ case /* 0xc0000034 */ STATUS_OBJECT_NAME_NOT_FOUND:
+ rc = VERR_MODULE_NOT_FOUND;
+ break;
+ case /* 0xC0000263 */ STATUS_DRIVER_ENTRYPOINT_NOT_FOUND:
+ rc = VERR_LDR_IMPORTED_SYMBOL_NOT_FOUND;
+ break;
+ case /* 0xC0000428 */ STATUS_INVALID_IMAGE_HASH:
+ rc = VERR_LDR_IMAGE_HASH;
+ break;
+ case /* 0xC000010E */ STATUS_IMAGE_ALREADY_LOADED:
+ Log(("WARNING: see @bugref{4853} for cause of this failure on Windows 7 x64\n"));
+ rc = VERR_ALREADY_LOADED;
+ break;
+ default:
+ rc = VERR_LDR_GENERAL_FAILURE;
+ break;
+ }
+
+ pImage->pvNtSectionObj = NULL;
+ }
+ }
+
+ RTMemTmpFree(pwcsFilename);
+ NOREF(pDevExt);
+ return rc;
+#endif
+}
+
+
+void VBOXCALL supdrvOSLdrNotifyOpened(PSUPDRVDEVEXT pDevExt, PSUPDRVLDRIMAGE pImage, const char *pszFilename)
+{
+ NOREF(pDevExt); NOREF(pImage); NOREF(pszFilename);
+}
+
+
+void VBOXCALL supdrvOSLdrNotifyUnloaded(PSUPDRVDEVEXT pDevExt, PSUPDRVLDRIMAGE pImage)
+{
+ NOREF(pDevExt); NOREF(pImage);
+}
+
+
+/**
+ * Common worker for supdrvOSLdrQuerySymbol and supdrvOSLdrValidatePointer.
+ *
+ * @note Similar code in rtR0DbgKrnlNtParseModule.
+ */
+static int supdrvOSLdrValidatePointerOrQuerySymbol(PSUPDRVLDRIMAGE pImage, void *pv, const char *pszSymbol,
+ size_t cchSymbol, void **ppvSymbol)
+{
+ AssertReturn(pImage->pvNtSectionObj, VERR_INVALID_STATE);
+ Assert(pszSymbol || !ppvSymbol);
+
+ /*
+ * Locate the export directory in the loaded image.
+ */
+ uint8_t const *pbMapping = (uint8_t const *)pImage->pvImage;
+ uint32_t const cbMapping = pImage->cbImageBits;
+ uint32_t const uRvaToValidate = (uint32_t)((uintptr_t)pv - (uintptr_t)pbMapping);
+ AssertReturn(uRvaToValidate < cbMapping || ppvSymbol, VERR_INTERNAL_ERROR_3);
+
+ uint32_t const offNtHdrs = *(uint16_t *)pbMapping == IMAGE_DOS_SIGNATURE
+ ? ((IMAGE_DOS_HEADER const *)pbMapping)->e_lfanew
+ : 0;
+ AssertLogRelReturn(offNtHdrs + sizeof(IMAGE_NT_HEADERS) < cbMapping, VERR_INTERNAL_ERROR_5);
+
+ IMAGE_NT_HEADERS const *pNtHdrs = (IMAGE_NT_HEADERS const *)((uintptr_t)pbMapping + offNtHdrs);
+ AssertLogRelReturn(pNtHdrs->Signature == IMAGE_NT_SIGNATURE, VERR_INVALID_EXE_SIGNATURE);
+ AssertLogRelReturn(pNtHdrs->OptionalHeader.Magic == IMAGE_NT_OPTIONAL_HDR_MAGIC, VERR_BAD_EXE_FORMAT);
+ AssertLogRelReturn(pNtHdrs->OptionalHeader.NumberOfRvaAndSizes == IMAGE_NUMBEROF_DIRECTORY_ENTRIES, VERR_BAD_EXE_FORMAT);
+
+ uint32_t const offEndSectHdrs = offNtHdrs
+ + sizeof(*pNtHdrs)
+ + pNtHdrs->FileHeader.NumberOfSections * sizeof(IMAGE_SECTION_HEADER);
+ AssertReturn(offEndSectHdrs < cbMapping, VERR_BAD_EXE_FORMAT);
+
+ /*
+ * Find the export directory.
+ */
+ IMAGE_DATA_DIRECTORY ExpDir = pNtHdrs->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT];
+ if (!ExpDir.Size)
+ {
+ SUPR0Printf("SUPDrv: No exports in %s!\n", pImage->szName);
+ return ppvSymbol ? VERR_SYMBOL_NOT_FOUND : VERR_NOT_FOUND;
+ }
+ AssertReturn( ExpDir.Size >= sizeof(IMAGE_EXPORT_DIRECTORY)
+ && ExpDir.VirtualAddress >= offEndSectHdrs
+ && ExpDir.VirtualAddress < cbMapping
+ && ExpDir.VirtualAddress + ExpDir.Size <= cbMapping, VERR_BAD_EXE_FORMAT);
+
+ IMAGE_EXPORT_DIRECTORY const *pExpDir = (IMAGE_EXPORT_DIRECTORY const *)&pbMapping[ExpDir.VirtualAddress];
+
+ uint32_t const cNamedExports = pExpDir->NumberOfNames;
+ AssertReturn(cNamedExports < _1M, VERR_BAD_EXE_FORMAT);
+ AssertReturn(pExpDir->NumberOfFunctions < _1M, VERR_BAD_EXE_FORMAT);
+ if (pExpDir->NumberOfFunctions == 0 || cNamedExports == 0)
+ {
+ SUPR0Printf("SUPDrv: No exports in %s!\n", pImage->szName);
+ return ppvSymbol ? VERR_SYMBOL_NOT_FOUND : VERR_NOT_FOUND;
+ }
+
+ uint32_t const cExports = RT_MAX(cNamedExports, pExpDir->NumberOfFunctions);
+
+ AssertReturn( pExpDir->AddressOfFunctions >= offEndSectHdrs
+ && pExpDir->AddressOfFunctions < cbMapping
+ && pExpDir->AddressOfFunctions + cExports * sizeof(uint32_t) <= cbMapping,
+ VERR_BAD_EXE_FORMAT);
+ uint32_t const * const paoffExports = (uint32_t const *)&pbMapping[pExpDir->AddressOfFunctions];
+
+ AssertReturn( pExpDir->AddressOfNames >= offEndSectHdrs
+ && pExpDir->AddressOfNames < cbMapping
+ && pExpDir->AddressOfNames + cNamedExports * sizeof(uint32_t) <= cbMapping,
+ VERR_BAD_EXE_FORMAT);
+ uint32_t const * const paoffNamedExports = (uint32_t const *)&pbMapping[pExpDir->AddressOfNames];
+
+ AssertReturn( pExpDir->AddressOfNameOrdinals >= offEndSectHdrs
+ && pExpDir->AddressOfNameOrdinals < cbMapping
+ && pExpDir->AddressOfNameOrdinals + cNamedExports * sizeof(uint32_t) <= cbMapping,
+ VERR_BAD_EXE_FORMAT);
+ uint16_t const * const pau16NameOrdinals = (uint16_t const *)&pbMapping[pExpDir->AddressOfNameOrdinals];
+
+ /*
+ * Validate the entrypoint RVA by scanning the export table.
+ */
+ uint32_t iExportOrdinal = UINT32_MAX;
+ if (!ppvSymbol)
+ {
+ for (uint32_t i = 0; i < cExports; i++)
+ if (paoffExports[i] == uRvaToValidate)
+ {
+ iExportOrdinal = i;
+ break;
+ }
+ if (iExportOrdinal == UINT32_MAX)
+ {
+ SUPR0Printf("SUPDrv: No export with rva %#x (%s) in %s!\n", uRvaToValidate, pszSymbol, pImage->szName);
+ return VERR_NOT_FOUND;
+ }
+ }
+
+ /*
+ * Can we validate the symbol name too or should we find a name?
+ * If so, just do a linear search.
+ */
+ if (pszSymbol && (RT_C_IS_UPPER(*pszSymbol) || ppvSymbol))
+ {
+ for (uint32_t i = 0; i < cNamedExports; i++)
+ {
+ uint32_t const offName = paoffNamedExports[i];
+ AssertReturn(offName < cbMapping, VERR_BAD_EXE_FORMAT);
+ uint32_t const cchMaxName = cbMapping - offName;
+ const char * const pszName = (const char *)&pbMapping[offName];
+ const char * const pszEnd = (const char *)memchr(pszName, '\0', cchMaxName);
+ AssertReturn(pszEnd, VERR_BAD_EXE_FORMAT);
+
+ if ( cchSymbol == (size_t)(pszEnd - pszName)
+ && memcmp(pszName, pszSymbol, cchSymbol) == 0)
+ {
+ if (ppvSymbol)
+ {
+ iExportOrdinal = pau16NameOrdinals[i];
+ if ( iExportOrdinal < cExports
+ && paoffExports[iExportOrdinal] < cbMapping)
+ {
+ *ppvSymbol = (void *)(paoffExports[iExportOrdinal] + pbMapping);
+ return VINF_SUCCESS;
+ }
+ }
+ else if (pau16NameOrdinals[i] == iExportOrdinal)
+ return VINF_SUCCESS;
+ else
+ SUPR0Printf("SUPDrv: Different exports found for %s and rva %#x in %s: %#x vs %#x\n",
+ pszSymbol, uRvaToValidate, pImage->szName, pau16NameOrdinals[i], iExportOrdinal);
+ return VERR_LDR_BAD_FIXUP;
+ }
+ }
+ if (!ppvSymbol)
+ SUPR0Printf("SUPDrv: No export named %s (%#x) in %s!\n", pszSymbol, uRvaToValidate, pImage->szName);
+ return VERR_SYMBOL_NOT_FOUND;
+ }
+ return VINF_SUCCESS;
+}
+
+
+int VBOXCALL supdrvOSLdrValidatePointer(PSUPDRVDEVEXT pDevExt, PSUPDRVLDRIMAGE pImage, void *pv,
+ const uint8_t *pbImageBits, const char *pszSymbol)
+{
+ RT_NOREF(pDevExt, pbImageBits);
+ return supdrvOSLdrValidatePointerOrQuerySymbol(pImage, pv, pszSymbol, pszSymbol ? strlen(pszSymbol) : 0, NULL);
+}
+
+
+int VBOXCALL supdrvOSLdrQuerySymbol(PSUPDRVDEVEXT pDevExt, PSUPDRVLDRIMAGE pImage,
+ const char *pszSymbol, size_t cchSymbol, void **ppvSymbol)
+{
+ RT_NOREF(pDevExt);
+ AssertReturn(ppvSymbol, VERR_INVALID_PARAMETER);
+ AssertReturn(pszSymbol, VERR_INVALID_PARAMETER);
+ return supdrvOSLdrValidatePointerOrQuerySymbol(pImage, NULL, pszSymbol, cchSymbol, ppvSymbol);
+}
+
+
+/**
+ * memcmp + errormsg + log.
+ *
+ * @returns Same as memcmp.
+ * @param pImage The image.
+ * @param pbImageBits The image bits ring-3 uploads.
+ * @param uRva The RVA to start comparing at.
+ * @param cb The number of bytes to compare.
+ * @param pReq The load request.
+ */
+static int supdrvNtCompare(PSUPDRVLDRIMAGE pImage, const uint8_t *pbImageBits, uint32_t uRva, uint32_t cb, PSUPLDRLOAD pReq)
+{
+ int iDiff = memcmp((uint8_t const *)pImage->pvImage + uRva, pbImageBits + uRva, cb);
+ if (iDiff)
+ {
+ uint32_t cbLeft = cb;
+ const uint8_t *pbNativeBits = (const uint8_t *)pImage->pvImage;
+ for (size_t off = uRva; cbLeft > 0; off++, cbLeft--)
+ if (pbNativeBits[off] != pbImageBits[off])
+ {
+ /* Note! We need to copy image bits into a temporary stack buffer here as we'd
+ otherwise risk overwriting them while formatting the error message. */
+ uint8_t abBytes[64];
+ memcpy(abBytes, &pbImageBits[off], RT_MIN(64, cbLeft));
+ supdrvLdrLoadError(VERR_LDR_MISMATCH_NATIVE, pReq,
+ "Mismatch at %#x (%p) of %s loaded at %p:\n"
+ "ntld: %.*Rhxs\n"
+ "iprt: %.*Rhxs",
+ off, &pbNativeBits[off], pImage->szName, pImage->pvImage,
+ RT_MIN(64, cbLeft), &pbNativeBits[off],
+ RT_MIN(64, cbLeft), &abBytes[0]);
+ SUPR0Printf("VBoxDrv: %s", pReq->u.Out.szError);
+ break;
+ }
+ }
+ return iDiff;
+}
+
+/** Image compare exclusion regions. */
+typedef struct SUPDRVNTEXCLREGIONS
+{
+ /** Number of regions. */
+ uint32_t cRegions;
+ /** The regions. */
+ struct SUPDRVNTEXCLREGION
+ {
+ uint32_t uRva;
+ uint32_t cb;
+ } aRegions[20];
+} SUPDRVNTEXCLREGIONS;
+
+/**
+ * Adds an exclusion region to the collection.
+ */
+static bool supdrvNtAddExclRegion(SUPDRVNTEXCLREGIONS *pRegions, uint32_t uRvaRegion, uint32_t cbRegion)
+{
+ uint32_t const cRegions = pRegions->cRegions;
+ AssertReturn(cRegions + 1 <= RT_ELEMENTS(pRegions->aRegions), false);
+ uint32_t i = 0;
+ for (; i < cRegions; i++)
+ if (uRvaRegion < pRegions->aRegions[i].uRva)
+ break;
+ if (i != cRegions)
+ memmove(&pRegions->aRegions[i + 1], &pRegions->aRegions[i], (cRegions - i) * sizeof(pRegions->aRegions[0]));
+ pRegions->aRegions[i].uRva = uRvaRegion;
+ pRegions->aRegions[i].cb = cbRegion;
+ pRegions->cRegions++;
+ return true;
+}
+
+
+int VBOXCALL supdrvOSLdrLoad(PSUPDRVDEVEXT pDevExt, PSUPDRVLDRIMAGE pImage, const uint8_t *pbImageBits, PSUPLDRLOAD pReq)
+{
+ NOREF(pDevExt);
+ if (pImage->pvNtSectionObj)
+ {
+ /*
+ * Usually, the entire image matches exactly.
+ */
+ if (!memcmp(pImage->pvImage, pbImageBits, pImage->cbImageBits))
+ return VINF_SUCCESS;
+
+ /*
+ * On Windows 10 the ImageBase member of the optional header is sometimes
+ * updated with the actual load address and sometimes not.
+ * On older windows versions (builds <= 9200?), a user mode address is
+ * sometimes found in the image base field after upgrading to VC++ 14.2.
+ */
+ uint32_t const offNtHdrs = *(uint16_t *)pbImageBits == IMAGE_DOS_SIGNATURE
+ ? ((IMAGE_DOS_HEADER const *)pbImageBits)->e_lfanew
+ : 0;
+ AssertLogRelReturn(offNtHdrs + sizeof(IMAGE_NT_HEADERS) < pImage->cbImageBits, VERR_INTERNAL_ERROR_5);
+ IMAGE_NT_HEADERS const *pNtHdrsIprt = (IMAGE_NT_HEADERS const *)(pbImageBits + offNtHdrs);
+ IMAGE_NT_HEADERS const *pNtHdrsNtLd = (IMAGE_NT_HEADERS const *)((uintptr_t)pImage->pvImage + offNtHdrs);
+
+ uint32_t const offImageBase = offNtHdrs + RT_UOFFSETOF(IMAGE_NT_HEADERS, OptionalHeader.ImageBase);
+ uint32_t const cbImageBase = RT_SIZEOFMEMB(IMAGE_NT_HEADERS, OptionalHeader.ImageBase);
+ if ( pNtHdrsNtLd->OptionalHeader.ImageBase != pNtHdrsIprt->OptionalHeader.ImageBase
+ && pNtHdrsIprt->Signature == IMAGE_NT_SIGNATURE
+ && pNtHdrsIprt->OptionalHeader.Magic == IMAGE_NT_OPTIONAL_HDR_MAGIC
+ && !memcmp(pImage->pvImage, pbImageBits, offImageBase)
+ && !memcmp((uint8_t const *)pImage->pvImage + offImageBase + cbImageBase,
+ pbImageBits + offImageBase + cbImageBase,
+ pImage->cbImageBits - offImageBase - cbImageBase))
+ return VINF_SUCCESS;
+
+ /*
+ * On Windows Server 2003 (sp2 x86) both import thunk tables are fixed
+ * up and we typically get a mismatch in the INIT section.
+ *
+ * So, lets see if everything matches when excluding the
+ * OriginalFirstThunk tables and (maybe) the ImageBase member.
+ * For simplicity the max number of exclusion regions is set to 16.
+ */
+ if ( pNtHdrsIprt->Signature == IMAGE_NT_SIGNATURE
+ && pNtHdrsIprt->OptionalHeader.Magic == IMAGE_NT_OPTIONAL_HDR_MAGIC
+ && pNtHdrsIprt->OptionalHeader.NumberOfRvaAndSizes > IMAGE_DIRECTORY_ENTRY_IMPORT
+ && pNtHdrsIprt->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].Size >= sizeof(IMAGE_IMPORT_DESCRIPTOR)
+ && pNtHdrsIprt->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress > sizeof(IMAGE_NT_HEADERS)
+ && pNtHdrsIprt->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress < pImage->cbImageBits
+ && pNtHdrsIprt->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_LOAD_CONFIG].Size >= sizeof(IMAGE_LOAD_CONFIG_DIRECTORY)
+ && pNtHdrsIprt->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_LOAD_CONFIG].VirtualAddress > sizeof(IMAGE_NT_HEADERS)
+ && pNtHdrsIprt->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_LOAD_CONFIG].VirtualAddress < pImage->cbImageBits)
+ {
+ SUPDRVNTEXCLREGIONS ExcludeRegions;
+ ExcludeRegions.cRegions = 0;
+
+ /* ImageBase: */
+ if (pNtHdrsNtLd->OptionalHeader.ImageBase != pNtHdrsIprt->OptionalHeader.ImageBase)
+ supdrvNtAddExclRegion(&ExcludeRegions, offImageBase, cbImageBase);
+
+ /* Imports: */
+ uint32_t cImpsLeft = pNtHdrsIprt->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].Size
+ / sizeof(IMAGE_IMPORT_DESCRIPTOR);
+ uint32_t offImps = pNtHdrsIprt->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress;
+ AssertLogRelReturn(offImps + cImpsLeft * sizeof(IMAGE_IMPORT_DESCRIPTOR) <= pImage->cbImageBits, VERR_INTERNAL_ERROR_3);
+ IMAGE_IMPORT_DESCRIPTOR const *pImp = (IMAGE_IMPORT_DESCRIPTOR const *)(pbImageBits + offImps);
+ while ( cImpsLeft-- > 0
+ && ExcludeRegions.cRegions < RT_ELEMENTS(ExcludeRegions.aRegions))
+ {
+ uint32_t uRvaThunk = pImp->OriginalFirstThunk;
+ if ( uRvaThunk > sizeof(IMAGE_NT_HEADERS)
+ && uRvaThunk <= pImage->cbImageBits - sizeof(IMAGE_THUNK_DATA)
+ && uRvaThunk != pImp->FirstThunk)
+ {
+ /* Find the size of the thunk table. */
+ IMAGE_THUNK_DATA const *paThunk = (IMAGE_THUNK_DATA const *)(pbImageBits + uRvaThunk);
+ uint32_t cMaxThunks = (pImage->cbImageBits - uRvaThunk) / sizeof(IMAGE_THUNK_DATA);
+ uint32_t cThunks = 0;
+ while (cThunks < cMaxThunks && paThunk[cThunks].u1.Function != 0)
+ cThunks++;
+ supdrvNtAddExclRegion(&ExcludeRegions, uRvaThunk, cThunks * sizeof(IMAGE_THUNK_DATA));
+ }
+
+#if 0 /* Useful for VMMR0 hacking, not for production use. See also SUPDrvLdr.cpp. */
+ /* Exclude the other thunk table if ntoskrnl.exe. */
+ uint32_t uRvaName = pImp->Name;
+ if ( uRvaName > sizeof(IMAGE_NT_HEADERS)
+ && uRvaName < pImage->cbImageBits - sizeof("ntoskrnl.exe")
+ && memcmp(&pbImageBits[uRvaName], RT_STR_TUPLE("ntoskrnl.exe")) == 0)
+ {
+ uRvaThunk = pImp->FirstThunk;
+ if ( uRvaThunk > sizeof(IMAGE_NT_HEADERS)
+ && uRvaThunk <= pImage->cbImageBits - sizeof(IMAGE_THUNK_DATA))
+ {
+ /* Find the size of the thunk table. */
+ IMAGE_THUNK_DATA const *paThunk = (IMAGE_THUNK_DATA const *)(pbImageBits + uRvaThunk);
+ uint32_t cMaxThunks = (pImage->cbImageBits - uRvaThunk) / sizeof(IMAGE_THUNK_DATA);
+ uint32_t cThunks = 0;
+ while (cThunks < cMaxThunks && paThunk[cThunks].u1.Function != 0)
+ cThunks++;
+ supdrvNtAddExclRegion(&ExcludeRegions, uRvaThunk, cThunks * sizeof(IMAGE_THUNK_DATA));
+ }
+ }
+#endif
+
+ /* advance */
+ pImp++;
+ }
+
+ /* Exclude the security cookie if present. */
+ uint32_t const cbCfg = pNtHdrsIprt->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_LOAD_CONFIG].Size;
+ uint32_t const offCfg = pNtHdrsIprt->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_LOAD_CONFIG].VirtualAddress;
+ IMAGE_LOAD_CONFIG_DIRECTORY const * const pCfg = (IMAGE_LOAD_CONFIG_DIRECTORY const *)&pbImageBits[offCfg];
+ if ( pCfg->Size >= RT_UOFFSET_AFTER(IMAGE_LOAD_CONFIG_DIRECTORY, SecurityCookie)
+ && pCfg->SecurityCookie != NULL)
+ supdrvNtAddExclRegion(&ExcludeRegions, (uintptr_t)pCfg->SecurityCookie - (uintptr_t)pImage->pvImage, sizeof(void *));
+
+ /* Also exclude the GuardCFCheckFunctionPointer and GuardCFDispatchFunctionPointer pointer variables. */
+ if ( pCfg->Size >= RT_UOFFSET_AFTER(IMAGE_LOAD_CONFIG_DIRECTORY, GuardCFCheckFunctionPointer)
+ && pCfg->GuardCFCheckFunctionPointer != NULL)
+ supdrvNtAddExclRegion(&ExcludeRegions, (uintptr_t)pCfg->GuardCFCheckFunctionPointer - (uintptr_t)pImage->pvImage, sizeof(void *));
+ if ( pCfg->Size >= RT_UOFFSET_AFTER(IMAGE_LOAD_CONFIG_DIRECTORY, GuardCFDispatchFunctionPointer)
+ && pCfg->GuardCFDispatchFunctionPointer != NULL)
+ supdrvNtAddExclRegion(&ExcludeRegions, (uintptr_t)pCfg->GuardCFDispatchFunctionPointer - (uintptr_t)pImage->pvImage, sizeof(void *));
+
+ /* Ditto for the XFG variants: */
+ if ( pCfg->Size >= RT_UOFFSET_AFTER(IMAGE_LOAD_CONFIG_DIRECTORY, GuardXFGCheckFunctionPointer)
+ && pCfg->GuardXFGCheckFunctionPointer != NULL)
+ supdrvNtAddExclRegion(&ExcludeRegions, (uintptr_t)pCfg->GuardXFGCheckFunctionPointer - (uintptr_t)pImage->pvImage, sizeof(void *));
+ if ( pCfg->Size >= RT_UOFFSET_AFTER(IMAGE_LOAD_CONFIG_DIRECTORY, GuardXFGDispatchFunctionPointer)
+ && pCfg->GuardXFGDispatchFunctionPointer != NULL)
+ supdrvNtAddExclRegion(&ExcludeRegions, (uintptr_t)pCfg->GuardXFGDispatchFunctionPointer - (uintptr_t)pImage->pvImage, sizeof(void *));
+
+ /** @todo What about GuardRFVerifyStackPointerFunctionPointer and
+ * GuardRFFailureRoutineFunctionPointer? Ignore for now as the compiler we're
+ * using (19.26.28805) sets them to zero from what I can tell. */
+
+ /*
+ * Ok, do the comparison.
+ */
+ int iDiff = 0;
+ uint32_t uRvaNext = 0;
+ for (unsigned i = 0; !iDiff && i < ExcludeRegions.cRegions; i++)
+ {
+ if (uRvaNext < ExcludeRegions.aRegions[i].uRva)
+ iDiff = supdrvNtCompare(pImage, pbImageBits, uRvaNext, ExcludeRegions.aRegions[i].uRva - uRvaNext, pReq);
+ uRvaNext = ExcludeRegions.aRegions[i].uRva + ExcludeRegions.aRegions[i].cb;
+ }
+ if (!iDiff && uRvaNext < pImage->cbImageBits)
+ iDiff = supdrvNtCompare(pImage, pbImageBits, uRvaNext, pImage->cbImageBits - uRvaNext, pReq);
+ if (!iDiff)
+ {
+ /*
+ * If there is a cookie init export, call it.
+ *
+ * This typically just does:
+ * __security_cookie = (rdtsc ^ &__security_cookie) & 0xffffffffffff;
+ * __security_cookie_complement = ~__security_cookie;
+ */
+ PFNRT pfnModuleInitSecurityCookie = NULL;
+ int rcSym = supdrvOSLdrQuerySymbol(pDevExt, pImage, RT_STR_TUPLE("ModuleInitSecurityCookie"),
+ (void **)&pfnModuleInitSecurityCookie);
+ if (RT_SUCCESS(rcSym) && pfnModuleInitSecurityCookie)
+ pfnModuleInitSecurityCookie();
+
+ return VINF_SUCCESS;
+ }
+ }
+ else
+ supdrvNtCompare(pImage, pbImageBits, 0, pImage->cbImageBits, pReq);
+ return VERR_LDR_MISMATCH_NATIVE;
+ }
+ return supdrvLdrLoadError(VERR_INTERNAL_ERROR_4, pReq, "No NT section object! Impossible!");
+}
+
+
+void VBOXCALL supdrvOSLdrUnload(PSUPDRVDEVEXT pDevExt, PSUPDRVLDRIMAGE pImage)
+{
+ if (pImage->pvNtSectionObj)
+ {
+ if (pImage->hMemLock != NIL_RTR0MEMOBJ)
+ {
+ RTR0MemObjFree(pImage->hMemLock, false /*fFreeMappings*/);
+ pImage->hMemLock = NIL_RTR0MEMOBJ;
+ }
+
+ NTSTATUS rcNt = ZwSetSystemInformation(MY_SystemUnloadGdiDriverInformation,
+ &pImage->pvNtSectionObj, sizeof(pImage->pvNtSectionObj));
+ if (rcNt != STATUS_SUCCESS)
+ SUPR0Printf("VBoxDrv: failed to unload '%s', rcNt=%#x\n", pImage->szName, rcNt);
+ pImage->pvNtSectionObj = NULL;
+ }
+ NOREF(pDevExt);
+}
+
+
+void VBOXCALL supdrvOSLdrRetainWrapperModule(PSUPDRVDEVEXT pDevExt, PSUPDRVLDRIMAGE pImage)
+{
+ RT_NOREF(pDevExt, pImage);
+ AssertFailed();
+}
+
+
+void VBOXCALL supdrvOSLdrReleaseWrapperModule(PSUPDRVDEVEXT pDevExt, PSUPDRVLDRIMAGE pImage)
+{
+ RT_NOREF(pDevExt, pImage);
+ AssertFailed();
+}
+
+
+#ifdef SUPDRV_WITH_MSR_PROBER
+
+#if 1
+/** @todo make this selectable. */
+# define AMD_MSR_PASSCODE 0x9c5a203a
+#else
+# define ASMRdMsrEx(a, b, c) ASMRdMsr(a)
+# define ASMWrMsrEx(a, b, c) ASMWrMsr(a,c)
+#endif
+
+
+/**
+ * Argument package used by supdrvOSMsrProberRead and supdrvOSMsrProberWrite.
+ */
+typedef struct SUPDRVNTMSPROBERARGS
+{
+ uint32_t uMsr;
+ uint64_t uValue;
+ bool fGp;
+} SUPDRVNTMSPROBERARGS;
+
+/** @callback_method_impl{FNRTMPWORKER, Worker for supdrvOSMsrProberRead.} */
+static DECLCALLBACK(void) supdrvNtMsProberReadOnCpu(RTCPUID idCpu, void *pvUser1, void *pvUser2)
+{
+ /*
+ * rdmsr and wrmsr faults can be caught even with interrupts disabled.
+ * (At least on 32-bit XP.)
+ */
+ SUPDRVNTMSPROBERARGS *pArgs = (SUPDRVNTMSPROBERARGS *)pvUser1; NOREF(idCpu); NOREF(pvUser2);
+ RTCCUINTREG fOldFlags = ASMIntDisableFlags();
+ __try
+ {
+ pArgs->uValue = ASMRdMsrEx(pArgs->uMsr, AMD_MSR_PASSCODE);
+ pArgs->fGp = false;
+ }
+ __except(EXCEPTION_EXECUTE_HANDLER)
+ {
+ pArgs->fGp = true;
+ pArgs->uValue = 0;
+ }
+ ASMSetFlags(fOldFlags);
+}
+
+
+int VBOXCALL supdrvOSMsrProberRead(uint32_t uMsr, RTCPUID idCpu, uint64_t *puValue)
+{
+ SUPDRVNTMSPROBERARGS Args;
+ Args.uMsr = uMsr;
+ Args.uValue = 0;
+ Args.fGp = true;
+
+ if (idCpu == NIL_RTCPUID)
+ supdrvNtMsProberReadOnCpu(idCpu, &Args, NULL);
+ else
+ {
+ int rc = RTMpOnSpecific(idCpu, supdrvNtMsProberReadOnCpu, &Args, NULL);
+ if (RT_FAILURE(rc))
+ return rc;
+ }
+
+ if (Args.fGp)
+ return VERR_ACCESS_DENIED;
+ *puValue = Args.uValue;
+ return VINF_SUCCESS;
+}
+
+
+/** @callback_method_impl{FNRTMPWORKER, Worker for supdrvOSMsrProberWrite.} */
+static DECLCALLBACK(void) supdrvNtMsProberWriteOnCpu(RTCPUID idCpu, void *pvUser1, void *pvUser2)
+{
+ /*
+ * rdmsr and wrmsr faults can be caught even with interrupts disabled.
+ * (At least on 32-bit XP.)
+ */
+ SUPDRVNTMSPROBERARGS *pArgs = (SUPDRVNTMSPROBERARGS *)pvUser1; NOREF(idCpu); NOREF(pvUser2);
+ RTCCUINTREG fOldFlags = ASMIntDisableFlags();
+ __try
+ {
+ ASMWrMsrEx(pArgs->uMsr, AMD_MSR_PASSCODE, pArgs->uValue);
+ pArgs->fGp = false;
+ }
+ __except(EXCEPTION_EXECUTE_HANDLER)
+ {
+ pArgs->fGp = true;
+ }
+ ASMSetFlags(fOldFlags);
+}
+
+int VBOXCALL supdrvOSMsrProberWrite(uint32_t uMsr, RTCPUID idCpu, uint64_t uValue)
+{
+ SUPDRVNTMSPROBERARGS Args;
+ Args.uMsr = uMsr;
+ Args.uValue = uValue;
+ Args.fGp = true;
+
+ if (idCpu == NIL_RTCPUID)
+ supdrvNtMsProberWriteOnCpu(idCpu, &Args, NULL);
+ else
+ {
+ int rc = RTMpOnSpecific(idCpu, supdrvNtMsProberWriteOnCpu, &Args, NULL);
+ if (RT_FAILURE(rc))
+ return rc;
+ }
+
+ if (Args.fGp)
+ return VERR_ACCESS_DENIED;
+ return VINF_SUCCESS;
+}
+
+/** @callback_method_impl{FNRTMPWORKER, Worker for supdrvOSMsrProberModify.} */
+static DECLCALLBACK(void) supdrvNtMsProberModifyOnCpu(RTCPUID idCpu, void *pvUser1, void *pvUser2)
+{
+ PSUPMSRPROBER pReq = (PSUPMSRPROBER)pvUser1;
+ register uint32_t uMsr = pReq->u.In.uMsr;
+ bool const fFaster = pReq->u.In.enmOp == SUPMSRPROBEROP_MODIFY_FASTER;
+ uint64_t uBefore = 0;
+ uint64_t uWritten = 0;
+ uint64_t uAfter = 0;
+ bool fBeforeGp = true;
+ bool fModifyGp = true;
+ bool fAfterGp = true;
+ bool fRestoreGp = true;
+ RTCCUINTREG fOldFlags;
+ RT_NOREF2(idCpu, pvUser2);
+
+ /*
+ * Do the job.
+ */
+ fOldFlags = ASMIntDisableFlags();
+ ASMCompilerBarrier(); /* paranoia */
+ if (!fFaster)
+ ASMWriteBackAndInvalidateCaches();
+
+ __try
+ {
+ uBefore = ASMRdMsrEx(uMsr, AMD_MSR_PASSCODE);
+ fBeforeGp = false;
+ }
+ __except(EXCEPTION_EXECUTE_HANDLER)
+ {
+ fBeforeGp = true;
+ }
+ if (!fBeforeGp)
+ {
+ register uint64_t uRestore = uBefore;
+
+ /* Modify. */
+ uWritten = uRestore;
+ uWritten &= pReq->u.In.uArgs.Modify.fAndMask;
+ uWritten |= pReq->u.In.uArgs.Modify.fOrMask;
+ __try
+ {
+ ASMWrMsrEx(uMsr, AMD_MSR_PASSCODE, uWritten);
+ fModifyGp = false;
+ }
+ __except(EXCEPTION_EXECUTE_HANDLER)
+ {
+ fModifyGp = true;
+ }
+
+ /* Read modified value. */
+ __try
+ {
+ uAfter = ASMRdMsrEx(uMsr, AMD_MSR_PASSCODE);
+ fAfterGp = false;
+ }
+ __except(EXCEPTION_EXECUTE_HANDLER)
+ {
+ fAfterGp = true;
+ }
+
+ /* Restore original value. */
+ __try
+ {
+ ASMWrMsrEx(uMsr, AMD_MSR_PASSCODE, uRestore);
+ fRestoreGp = false;
+ }
+ __except(EXCEPTION_EXECUTE_HANDLER)
+ {
+ fRestoreGp = true;
+ }
+
+ /* Invalid everything we can. */
+ if (!fFaster)
+ {
+ ASMWriteBackAndInvalidateCaches();
+ ASMReloadCR3();
+ ASMNopPause();
+ }
+ }
+
+ ASMCompilerBarrier(); /* paranoia */
+ ASMSetFlags(fOldFlags);
+
+ /*
+ * Write out the results.
+ */
+ pReq->u.Out.uResults.Modify.uBefore = uBefore;
+ pReq->u.Out.uResults.Modify.uWritten = uWritten;
+ pReq->u.Out.uResults.Modify.uAfter = uAfter;
+ pReq->u.Out.uResults.Modify.fBeforeGp = fBeforeGp;
+ pReq->u.Out.uResults.Modify.fModifyGp = fModifyGp;
+ pReq->u.Out.uResults.Modify.fAfterGp = fAfterGp;
+ pReq->u.Out.uResults.Modify.fRestoreGp = fRestoreGp;
+ RT_ZERO(pReq->u.Out.uResults.Modify.afReserved);
+}
+
+
+int VBOXCALL supdrvOSMsrProberModify(RTCPUID idCpu, PSUPMSRPROBER pReq)
+{
+ if (idCpu == NIL_RTCPUID)
+ {
+ supdrvNtMsProberModifyOnCpu(idCpu, pReq, NULL);
+ return VINF_SUCCESS;
+ }
+ return RTMpOnSpecific(idCpu, supdrvNtMsProberModifyOnCpu, pReq, NULL);
+}
+
+#endif /* SUPDRV_WITH_MSR_PROBER */
+
+
+/**
+ * Converts an IPRT error code to an nt status code.
+ *
+ * @returns corresponding nt status code.
+ * @param rc IPRT error status code.
+ */
+static NTSTATUS VBoxDrvNtErr2NtStatus(int rc)
+{
+ switch (rc)
+ {
+ case VINF_SUCCESS: return STATUS_SUCCESS;
+ case VERR_GENERAL_FAILURE: return STATUS_NOT_SUPPORTED;
+ case VERR_INVALID_PARAMETER: return STATUS_INVALID_PARAMETER;
+ case VERR_INVALID_MAGIC: return STATUS_UNKNOWN_REVISION;
+ case VERR_INVALID_HANDLE: return STATUS_INVALID_HANDLE;
+ case VERR_INVALID_POINTER: return STATUS_INVALID_ADDRESS;
+ case VERR_LOCK_FAILED: return STATUS_NOT_LOCKED;
+ case VERR_ALREADY_LOADED: return STATUS_IMAGE_ALREADY_LOADED;
+ case VERR_PERMISSION_DENIED: return STATUS_ACCESS_DENIED;
+ case VERR_VERSION_MISMATCH: return STATUS_REVISION_MISMATCH;
+ }
+
+ if (rc < 0)
+ {
+ if (((uint32_t)rc & UINT32_C(0xffff0000)) == UINT32_C(0xffff0000))
+ return (NTSTATUS)( ((uint32_t)rc & UINT32_C(0xffff)) | SUP_NT_STATUS_BASE );
+ }
+ return STATUS_UNSUCCESSFUL;
+}
+
+
+SUPR0DECL(int) SUPR0PrintfV(const char *pszFormat, va_list va)
+{
+ char szMsg[384];
+ size_t cch = RTStrPrintfV(szMsg, sizeof(szMsg) - 1, pszFormat, va);
+ szMsg[sizeof(szMsg) - 1] = '\0';
+
+ RTLogWriteDebugger(szMsg, cch);
+ return 0;
+}
+
+
+SUPR0DECL(uint32_t) SUPR0GetKernelFeatures(void)
+{
+ return 0;
+}
+
+
+SUPR0DECL(bool) SUPR0FpuBegin(bool fCtxHook)
+{
+ RT_NOREF(fCtxHook);
+ return false;
+}
+
+
+SUPR0DECL(void) SUPR0FpuEnd(bool fCtxHook)
+{
+ RT_NOREF(fCtxHook);
+}
+
+
+SUPR0DECL(int) SUPR0IoCtlSetupForHandle(PSUPDRVSESSION pSession, intptr_t hHandle, uint32_t fFlags, PSUPR0IOCTLCTX *ppCtx)
+{
+ /*
+ * Validate input.
+ */
+ AssertPtrReturn(ppCtx, VERR_INVALID_POINTER);
+ *ppCtx = NULL;
+ AssertReturn(SUP_IS_SESSION_VALID(pSession), VERR_INVALID_PARAMETER);
+ AssertReturn(!fFlags, VERR_INVALID_FLAGS);
+
+ /*
+ * Turn the partition handle into a file object and related device object
+ * so that we can issue direct I/O control calls to the pair later.
+ */
+ PFILE_OBJECT pFileObject = NULL;
+ OBJECT_HANDLE_INFORMATION HandleInfo = { 0, 0 };
+ NTSTATUS rcNt = ObReferenceObjectByHandle((HANDLE)hHandle, /*FILE_WRITE_DATA*/0, *IoFileObjectType,
+ UserMode, (void **)&pFileObject, &HandleInfo);
+ if (!NT_SUCCESS(rcNt))
+ return RTErrConvertFromNtStatus(rcNt);
+ AssertPtrReturn(pFileObject, VERR_INTERNAL_ERROR_3);
+
+ PDEVICE_OBJECT pDevObject = IoGetRelatedDeviceObject(pFileObject);
+ AssertMsgReturnStmt(RT_VALID_PTR(pDevObject), ("pDevObject=%p\n", pDevObject),
+ ObDereferenceObject(pFileObject), VERR_INTERNAL_ERROR_2);
+
+ /*
+ * Allocate a context structure and fill it in.
+ */
+ PSUPR0IOCTLCTX pCtx = (PSUPR0IOCTLCTX)RTMemAllocZ(sizeof(*pCtx));
+ if (pCtx)
+ {
+ pCtx->u32Magic = SUPR0IOCTLCTX_MAGIC;
+ pCtx->cRefs = 1;
+ pCtx->pFileObject = pFileObject;
+ pCtx->pDeviceObject = pDevObject;
+
+ PDRIVER_OBJECT pDrvObject = pDevObject->DriverObject;
+ if ( RT_VALID_PTR(pDrvObject->FastIoDispatch)
+ && RT_VALID_PTR(pDrvObject->FastIoDispatch->FastIoDeviceControl))
+ pCtx->pfnFastIoDeviceControl = pDrvObject->FastIoDispatch->FastIoDeviceControl;
+ else
+ pCtx->pfnFastIoDeviceControl = NULL;
+ *ppCtx = pCtx;
+ return VINF_SUCCESS;
+ }
+
+ ObDereferenceObject(pFileObject);
+ return VERR_NO_MEMORY;
+}
+
+
+/**
+ * I/O control destructor for NT.
+ *
+ * @param pCtx The context to destroy.
+ */
+static void supdrvNtIoCtlContextDestroy(PSUPR0IOCTLCTX pCtx)
+{
+ PFILE_OBJECT pFileObject = pCtx->pFileObject;
+ pCtx->pfnFastIoDeviceControl = NULL;
+ pCtx->pFileObject = NULL;
+ pCtx->pDeviceObject = NULL;
+ ASMAtomicWriteU32(&pCtx->u32Magic, ~SUPR0IOCTLCTX_MAGIC);
+
+ if (RT_VALID_PTR(pFileObject))
+ ObDereferenceObject(pFileObject);
+ RTMemFree(pCtx);
+}
+
+
+SUPR0DECL(int) SUPR0IoCtlCleanup(PSUPR0IOCTLCTX pCtx)
+{
+ if (pCtx != NULL)
+ {
+ AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
+ AssertReturn(pCtx->u32Magic == SUPR0IOCTLCTX_MAGIC, VERR_INVALID_PARAMETER);
+
+ uint32_t cRefs = ASMAtomicDecU32(&pCtx->cRefs);
+ Assert(cRefs < _4K);
+ if (cRefs == 0)
+ supdrvNtIoCtlContextDestroy(pCtx);
+ }
+ return VINF_SUCCESS;
+}
+
+
+SUPR0DECL(int) SUPR0IoCtlPerform(PSUPR0IOCTLCTX pCtx, uintptr_t uFunction,
+ void *pvInput, RTR3PTR pvInputUser, size_t cbInput,
+ void *pvOutput, RTR3PTR pvOutputUser, size_t cbOutput,
+ int32_t *piNativeRc)
+{
+ AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
+ AssertReturn(pCtx->u32Magic == SUPR0IOCTLCTX_MAGIC, VERR_INVALID_PARAMETER);
+
+ /* Reference the context. */
+ uint32_t cRefs = ASMAtomicIncU32(&pCtx->cRefs);
+ Assert(cRefs > 1 && cRefs < _4K);
+
+ /*
+ * Try fast I/O control path first.
+ */
+ IO_STATUS_BLOCK Ios = RTNT_IO_STATUS_BLOCK_INITIALIZER;
+ if (pCtx->pfnFastIoDeviceControl)
+ {
+ /* Must pass user addresses here as that's what's being expected. */
+ BOOLEAN fHandled = pCtx->pfnFastIoDeviceControl(pCtx->pFileObject,
+ TRUE /*Wait*/,
+ (void *)pvInputUser, (ULONG)cbInput,
+ (void *)pvOutputUser, (ULONG)cbOutput,
+ uFunction,
+ &Ios,
+ pCtx->pDeviceObject);
+ if (fHandled)
+ {
+ /* Relase the context. */
+ cRefs = ASMAtomicDecU32(&pCtx->cRefs);
+ Assert(cRefs < _4K);
+ if (cRefs == 0)
+ supdrvNtIoCtlContextDestroy(pCtx);
+
+ /* Set/convert status and return. */
+ if (piNativeRc)
+ {
+ *piNativeRc = Ios.Status;
+ return VINF_SUCCESS;
+ }
+ if (NT_SUCCESS(Ios.Status))
+ return VINF_SUCCESS;
+ return RTErrConvertFromNtStatus(Ios.Status);
+ }
+
+ /*
+ * Fall back on IRP if not handled.
+ *
+ * Note! Perhaps we should rather fail, because VID.SYS will crash getting
+ * the partition ID with the code below. It tries to zero the output
+ * buffer as if it were as system buffer...
+ */
+ RTNT_IO_STATUS_BLOCK_REINIT(&Ios);
+ }
+
+ /*
+ * For directly accessed buffers we must supply user mode addresses or
+ * we'll fail ProbeForWrite validation.
+ */
+ switch (uFunction & 3)
+ {
+ case METHOD_BUFFERED:
+ /* For buffered accesses, we can supply kernel buffers. */
+ break;
+
+ case METHOD_IN_DIRECT:
+ pvInput = (void *)pvInputUser;
+ break;
+
+ case METHOD_NEITHER:
+ pvInput = (void *)pvInputUser;
+ RT_FALL_THRU();
+
+ case METHOD_OUT_DIRECT:
+ pvOutput = (void *)pvOutputUser;
+ break;
+ }
+
+ /*
+ * Build the request.
+ */
+ int rc;
+ KEVENT Event;
+ KeInitializeEvent(&Event, NotificationEvent, FALSE);
+
+ PIRP pIrp = IoBuildDeviceIoControlRequest(uFunction, pCtx->pDeviceObject,
+ pvInput, (ULONG)cbInput, pvOutput, (ULONG)cbOutput,
+ FALSE /* InternalDeviceControl */, &Event, &Ios);
+ if (pIrp)
+ {
+ IoGetNextIrpStackLocation(pIrp)->FileObject = pCtx->pFileObject;
+
+ /*
+ * Make the call.
+ */
+ NTSTATUS rcNt = IoCallDriver(pCtx->pDeviceObject, pIrp);
+ if (rcNt == STATUS_PENDING)
+ {
+ rcNt = KeWaitForSingleObject(&Event, /* Object */
+ Executive, /* WaitReason */
+ KernelMode, /* WaitMode */
+ FALSE, /* Alertable */
+ NULL); /* TimeOut */
+ AssertMsg(rcNt == STATUS_SUCCESS, ("rcNt=%#x\n", rcNt));
+ rcNt = Ios.Status;
+ }
+ else if (NT_SUCCESS(rcNt) && Ios.Status != STATUS_SUCCESS)
+ rcNt = Ios.Status;
+
+ /* Set/convert return code. */
+ if (piNativeRc)
+ {
+ *piNativeRc = rcNt;
+ rc = VINF_SUCCESS;
+ }
+ else if (NT_SUCCESS(rcNt))
+ rc = VINF_SUCCESS;
+ else
+ rc = RTErrConvertFromNtStatus(rcNt);
+ }
+ else
+ {
+ if (piNativeRc)
+ *piNativeRc = STATUS_NO_MEMORY;
+ rc = VERR_NO_MEMORY;
+ }
+
+ /* Relase the context. */
+ cRefs = ASMAtomicDecU32(&pCtx->cRefs);
+ Assert(cRefs < _4K);
+ if (cRefs == 0)
+ supdrvNtIoCtlContextDestroy(pCtx);
+
+ return rc;
+}
+
+
+#ifdef VBOX_WITH_HARDENING
+
+/** @name Identifying Special Processes: CSRSS.EXE
+ * @{ */
+
+
+/**
+ * Checks if the process is a system32 process by the given name.
+ *
+ * @returns true / false.
+ * @param pProcess The process to check.
+ * @param pszName The lower case process name (no path!).
+ */
+static bool supdrvNtProtectIsSystem32ProcessMatch(PEPROCESS pProcess, const char *pszName)
+{
+ Assert(strlen(pszName) < 16); /* see buffer below */
+
+ /*
+ * This test works on XP+.
+ */
+ const char *pszImageFile = (const char *)PsGetProcessImageFileName(pProcess);
+ if (!pszImageFile)
+ return false;
+
+ if (RTStrICmp(pszImageFile, pszName) != 0)
+ return false;
+
+ /*
+ * This test requires a Vista+ API.
+ */
+ if (g_pfnPsReferenceProcessFilePointer)
+ {
+ PFILE_OBJECT pFile = NULL;
+ NTSTATUS rcNt = g_pfnPsReferenceProcessFilePointer(pProcess, &pFile);
+ if (!NT_SUCCESS(rcNt))
+ return false;
+
+ union
+ {
+ OBJECT_NAME_INFORMATION Info;
+ uint8_t abBuffer[sizeof(g_System32NtPath) + 16 * sizeof(WCHAR)];
+ } Buf;
+ ULONG cbIgn;
+ rcNt = ObQueryNameString(pFile, &Buf.Info, sizeof(Buf) - sizeof(WCHAR), &cbIgn);
+ ObDereferenceObject(pFile);
+ if (!NT_SUCCESS(rcNt))
+ return false;
+
+ /* Terminate the name. */
+ PRTUTF16 pwszName = Buf.Info.Name.Buffer;
+ pwszName[Buf.Info.Name.Length / sizeof(RTUTF16)] = '\0';
+
+ /* Match the name against the system32 directory path. */
+ uint32_t cbSystem32 = g_System32NtPath.UniStr.Length;
+ if (Buf.Info.Name.Length < cbSystem32)
+ return false;
+ if (memcmp(pwszName, g_System32NtPath.UniStr.Buffer, cbSystem32))
+ return false;
+ pwszName += cbSystem32 / sizeof(RTUTF16);
+ if (*pwszName++ != '\\')
+ return false;
+
+ /* Compare the name. */
+ const char *pszRight = pszName;
+ for (;;)
+ {
+ WCHAR wchLeft = *pwszName++;
+ char chRight = *pszRight++;
+ Assert(chRight == RT_C_TO_LOWER(chRight));
+
+ if ( wchLeft != chRight
+ && RT_C_TO_LOWER(wchLeft) != chRight)
+ return false;
+ if (!chRight)
+ break;
+ }
+ }
+
+ return true;
+}
+
+
+/**
+ * Checks if the current process is likely to be CSRSS.
+ *
+ * @returns true/false.
+ * @param pProcess The process.
+ */
+static bool supdrvNtProtectIsCsrssByProcess(PEPROCESS pProcess)
+{
+ /*
+ * On Windows 8.1 CSRSS.EXE is a protected process.
+ */
+ if (g_pfnPsIsProtectedProcessLight)
+ {
+ if (!g_pfnPsIsProtectedProcessLight(pProcess))
+ return false;
+ }
+
+ /*
+ * The name tests.
+ */
+ if (!supdrvNtProtectIsSystem32ProcessMatch(pProcess, "csrss.exe"))
+ return false;
+
+ /** @todo Could extend the CSRSS.EXE check with that the TokenUser of the
+ * current process must be "NT AUTHORITY\SYSTEM" (S-1-5-18). */
+
+ return true;
+}
+
+
+/**
+ * Helper for supdrvNtProtectGetAlpcPortObjectType that tries out a name.
+ *
+ * @returns true if done, false if not.
+ * @param pwszPortNm The port path.
+ * @param ppObjType The object type return variable, updated when
+ * returning true.
+ */
+static bool supdrvNtProtectGetAlpcPortObjectType2(PCRTUTF16 pwszPortNm, POBJECT_TYPE *ppObjType)
+{
+ bool fDone = false;
+
+ UNICODE_STRING UniStrPortNm;
+ UniStrPortNm.Buffer = (WCHAR *)pwszPortNm;
+ UniStrPortNm.Length = (USHORT)(RTUtf16Len(pwszPortNm) * sizeof(WCHAR));
+ UniStrPortNm.MaximumLength = UniStrPortNm.Length + sizeof(WCHAR);
+
+ OBJECT_ATTRIBUTES ObjAttr;
+ InitializeObjectAttributes(&ObjAttr, &UniStrPortNm, OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, NULL, NULL);
+
+ HANDLE hPort;
+ NTSTATUS rcNt = g_pfnZwAlpcCreatePort(&hPort, &ObjAttr, NULL /*pPortAttribs*/);
+ if (NT_SUCCESS(rcNt))
+ {
+ PVOID pvObject;
+ rcNt = ObReferenceObjectByHandle(hPort, 0 /*DesiredAccess*/, NULL /*pObjectType*/,
+ KernelMode, &pvObject, NULL /*pHandleInfo*/);
+ if (NT_SUCCESS(rcNt))
+ {
+ POBJECT_TYPE pObjType = g_pfnObGetObjectType(pvObject);
+ if (pObjType)
+ {
+ SUPR0Printf("vboxdrv: ALPC Port Object Type %p (vs %p)\n", pObjType, *ppObjType);
+ *ppObjType = pObjType;
+ fDone = true;
+ }
+ ObDereferenceObject(pvObject);
+ }
+ NtClose(hPort);
+ }
+ return fDone;
+}
+
+
+/**
+ * Attempts to retrieve the ALPC Port object type.
+ *
+ * We've had at least three reports that using LpcPortObjectType when trying to
+ * get at the ApiPort object results in STATUS_OBJECT_TYPE_MISMATCH errors.
+ * It's not known who has modified LpcPortObjectType or AlpcPortObjectType (not
+ * exported) so that it differs from the actual ApiPort type, or maybe this
+ * unknown entity is intercepting our attempt to reference the port and
+ * tries to mislead us. The paranoid explanataion is of course that some evil
+ * root kit like software is messing with the OS, however, it's possible that
+ * this is valid kernel behavior that 99.8% of our users and 100% of the
+ * developers are not triggering for some reason.
+ *
+ * The code here creates an ALPC port object and gets it's type. It will cache
+ * the result in g_pAlpcPortObjectType2 on success.
+ *
+ * @returns Object type.
+ * @param uSessionId The session id.
+ * @param pszSessionId The session id formatted as a string.
+ */
+static POBJECT_TYPE supdrvNtProtectGetAlpcPortObjectType(uint32_t uSessionId, const char *pszSessionId)
+{
+ POBJECT_TYPE pObjType = *LpcPortObjectType;
+
+ if ( g_pfnZwAlpcCreatePort
+ && g_pfnObGetObjectType)
+ {
+ int rc;
+ ssize_t cchTmp; NOREF(cchTmp);
+ char szTmp[16];
+ RTUTF16 wszPortNm[128];
+ size_t offRand;
+
+ /*
+ * First attempt is in the session directory.
+ */
+ rc = RTUtf16CopyAscii(wszPortNm, RT_ELEMENTS(wszPortNm), "\\Sessions\\");
+ rc |= RTUtf16CatAscii(wszPortNm, RT_ELEMENTS(wszPortNm), pszSessionId);
+ rc |= RTUtf16CatAscii(wszPortNm, RT_ELEMENTS(wszPortNm), "\\VBoxDrv-");
+ cchTmp = RTStrFormatU32(szTmp, sizeof(szTmp), (uint32_t)(uintptr_t)PsGetProcessId(PsGetCurrentProcess()), 16, 0, 0, 0);
+ Assert(cchTmp > 0);
+ rc |= RTUtf16CatAscii(wszPortNm, RT_ELEMENTS(wszPortNm), szTmp);
+ rc |= RTUtf16CatAscii(wszPortNm, RT_ELEMENTS(wszPortNm), "-");
+ offRand = RTUtf16Len(wszPortNm);
+ cchTmp = RTStrFormatU32(szTmp, sizeof(szTmp), RTRandU32(), 16, 0, 0, 0);
+ Assert(cchTmp > 0);
+ rc |= RTUtf16CatAscii(wszPortNm, RT_ELEMENTS(wszPortNm), szTmp);
+ AssertRCSuccess(rc);
+
+ bool fDone = supdrvNtProtectGetAlpcPortObjectType2(wszPortNm, &pObjType);
+ if (!fDone)
+ {
+ wszPortNm[offRand] = '\0';
+ cchTmp = RTStrFormatU32(szTmp, sizeof(szTmp), RTRandU32(), 16, 0, 0, 0); Assert(cchTmp > 0);
+ rc |= RTUtf16CatAscii(wszPortNm, RT_ELEMENTS(wszPortNm), szTmp);
+ AssertRCSuccess(rc);
+
+ fDone = supdrvNtProtectGetAlpcPortObjectType2(wszPortNm, &pObjType);
+ }
+ if (!fDone)
+ {
+ /*
+ * Try base names.
+ */
+ if (uSessionId == 0)
+ rc = RTUtf16CopyAscii(wszPortNm, RT_ELEMENTS(wszPortNm), "\\BaseNamedObjects\\VBoxDrv-");
+ else
+ {
+ rc = RTUtf16CopyAscii(wszPortNm, RT_ELEMENTS(wszPortNm), "\\Sessions\\");
+ rc |= RTUtf16CatAscii(wszPortNm, RT_ELEMENTS(wszPortNm), pszSessionId);
+ rc |= RTUtf16CatAscii(wszPortNm, RT_ELEMENTS(wszPortNm), "\\BaseNamedObjects\\VBoxDrv-");
+ }
+ cchTmp = RTStrFormatU32(szTmp, sizeof(szTmp), (uint32_t)(uintptr_t)PsGetProcessId(PsGetCurrentProcess()), 16, 0, 0, 0);
+ Assert(cchTmp > 0);
+ rc |= RTUtf16CatAscii(wszPortNm, RT_ELEMENTS(wszPortNm), szTmp);
+ rc |= RTUtf16CatAscii(wszPortNm, RT_ELEMENTS(wszPortNm), "-");
+ offRand = RTUtf16Len(wszPortNm);
+ cchTmp = RTStrFormatU32(szTmp, sizeof(szTmp), RTRandU32(), 16, 0, 0, 0);
+ Assert(cchTmp > 0);
+ rc |= RTUtf16CatAscii(wszPortNm, RT_ELEMENTS(wszPortNm), szTmp);
+ AssertRCSuccess(rc);
+
+ fDone = supdrvNtProtectGetAlpcPortObjectType2(wszPortNm, &pObjType);
+ if (!fDone)
+ {
+ wszPortNm[offRand] = '\0';
+ cchTmp = RTStrFormatU32(szTmp, sizeof(szTmp), RTRandU32(), 16, 0, 0, 0);
+ Assert(cchTmp > 0);
+ rc |= RTUtf16CatAscii(wszPortNm, RT_ELEMENTS(wszPortNm), szTmp);
+ AssertRCSuccess(rc);
+
+ fDone = supdrvNtProtectGetAlpcPortObjectType2(wszPortNm, &pObjType);
+ }
+ }
+
+ /* Cache the result in g_pAlpcPortObjectType2. */
+ if ( g_pAlpcPortObjectType2 == NULL
+ && pObjType != g_pAlpcPortObjectType1
+ && fDone)
+ g_pAlpcPortObjectType2 = pObjType;
+
+ }
+
+ return pObjType;
+}
+
+
+/**
+ * Called in the context of VBoxDrvNtCreate to determin the CSRSS for the
+ * current process.
+ *
+ * The Client/Server Runtime Subsystem (CSRSS) process needs to be allowed some
+ * additional access right so we need to make 101% sure we correctly identify
+ * the CSRSS process a process is associated with.
+ *
+ * @returns IPRT status code.
+ * @param pNtProtect The NT protected process structure. The
+ * hCsrssPid member will be updated on success.
+ */
+static int supdrvNtProtectFindAssociatedCsrss(PSUPDRVNTPROTECT pNtProtect)
+{
+ Assert(pNtProtect->AvlCore.Key == PsGetCurrentProcessId());
+ Assert(pNtProtect->pCsrssProcess == NULL);
+ Assert(pNtProtect->hCsrssPid == NULL);
+
+ /*
+ * We'll try use the ApiPort LPC object for the session we're in to track
+ * down the CSRSS process. So, we start by constructing a path to it.
+ */
+ int rc;
+ uint32_t uSessionId = PsGetProcessSessionId(PsGetCurrentProcess());
+ char szSessionId[16];
+ WCHAR wszApiPort[48];
+ if (uSessionId == 0)
+ {
+ szSessionId[0] = '0';
+ szSessionId[1] = '\0';
+ rc = RTUtf16CopyAscii(wszApiPort, RT_ELEMENTS(wszApiPort), "\\Windows\\ApiPort");
+ }
+ else
+ {
+ ssize_t cchTmp = RTStrFormatU32(szSessionId, sizeof(szSessionId), uSessionId, 10, 0, 0, 0);
+ AssertReturn(cchTmp > 0, (int)cchTmp);
+ rc = RTUtf16CopyAscii(wszApiPort, RT_ELEMENTS(wszApiPort), "\\Sessions\\");
+ if (RT_SUCCESS(rc))
+ rc = RTUtf16CatAscii(wszApiPort, RT_ELEMENTS(wszApiPort), szSessionId);
+ if (RT_SUCCESS(rc))
+ rc = RTUtf16CatAscii(wszApiPort, RT_ELEMENTS(wszApiPort), "\\Windows\\ApiPort");
+ }
+ AssertRCReturn(rc, rc);
+
+ UNICODE_STRING ApiPortStr;
+ ApiPortStr.Buffer = wszApiPort;
+ ApiPortStr.Length = (USHORT)(RTUtf16Len(wszApiPort) * sizeof(RTUTF16));
+ ApiPortStr.MaximumLength = ApiPortStr.Length + sizeof(RTUTF16);
+
+ /*
+ * The object cannot be opened, but we can reference it by name.
+ */
+ void *pvApiPortObj = NULL;
+ NTSTATUS rcNt = ObReferenceObjectByName(&ApiPortStr,
+ 0,
+ NULL /*pAccessState*/,
+ STANDARD_RIGHTS_READ,
+ g_pAlpcPortObjectType1,
+ KernelMode,
+ NULL /*pvParseContext*/,
+ &pvApiPortObj);
+ if ( rcNt == STATUS_OBJECT_TYPE_MISMATCH
+ && g_pAlpcPortObjectType2 != NULL)
+ rcNt = ObReferenceObjectByName(&ApiPortStr,
+ 0,
+ NULL /*pAccessState*/,
+ STANDARD_RIGHTS_READ,
+ g_pAlpcPortObjectType2,
+ KernelMode,
+ NULL /*pvParseContext*/,
+ &pvApiPortObj);
+ if ( rcNt == STATUS_OBJECT_TYPE_MISMATCH
+ && g_pfnObGetObjectType
+ && g_pfnZwAlpcCreatePort)
+ rcNt = ObReferenceObjectByName(&ApiPortStr,
+ 0,
+ NULL /*pAccessState*/,
+ STANDARD_RIGHTS_READ,
+ supdrvNtProtectGetAlpcPortObjectType(uSessionId, szSessionId),
+ KernelMode,
+ NULL /*pvParseContext*/,
+ &pvApiPortObj);
+ if (!NT_SUCCESS(rcNt))
+ {
+ SUPR0Printf("vboxdrv: Error opening '%ls': %#x\n", wszApiPort, rcNt);
+ return rcNt == STATUS_OBJECT_TYPE_MISMATCH ? VERR_SUPDRV_APIPORT_OPEN_ERROR_TYPE : VERR_SUPDRV_APIPORT_OPEN_ERROR;
+ }
+
+ /*
+ * Query the processes in the system so we can locate CSRSS.EXE candidates.
+ * Note! Attempts at using SystemSessionProcessInformation failed with
+ * STATUS_ACCESS_VIOLATION.
+ * Note! The 32 bytes on the size of to counteract the allocation header
+ * that rtR0MemAllocEx slaps on everything.
+ */
+ ULONG cbNeeded = _64K - 32;
+ uint32_t cbBuf;
+ uint8_t *pbBuf = NULL;
+ do
+ {
+ cbBuf = RT_ALIGN(cbNeeded + _4K, _64K) - 32;
+ pbBuf = (uint8_t *)RTMemAlloc(cbBuf);
+ if (!pbBuf)
+ break;
+
+ cbNeeded = 0;
+#if 0 /* doesn't work. */
+ SYSTEM_SESSION_PROCESS_INFORMATION Req;
+ Req.SessionId = uSessionId;
+ Req.BufferLength = cbBuf;
+ Req.Buffer = pbBuf;
+ rcNt = NtQuerySystemInformation(SystemSessionProcessInformation, &Req, sizeof(Req), &cbNeeded);
+#else
+ rcNt = NtQuerySystemInformation(SystemProcessInformation, pbBuf, cbBuf, &cbNeeded);
+#endif
+ if (NT_SUCCESS(rcNt))
+ break;
+
+ RTMemFree(pbBuf);
+ pbBuf = NULL;
+ } while ( rcNt == STATUS_INFO_LENGTH_MISMATCH
+ && cbNeeded > cbBuf
+ && cbNeeded < 32U*_1M);
+
+ if ( pbBuf
+ && NT_SUCCESS(rcNt)
+ && cbNeeded >= sizeof(SYSTEM_PROCESS_INFORMATION))
+ {
+ /*
+ * Walk the returned data and look for the process associated with the
+ * ApiPort object. The ApiPort object keeps the EPROCESS address of
+ * the owner process (i.e. CSRSS) relatively early in the structure. On
+ * 64-bit windows 8.1 it's at offset 0x18. So, obtain the EPROCESS
+ * pointer to likely CSRSS processes and check for a match in the first
+ * 0x40 bytes of the ApiPort object.
+ */
+ rc = VERR_SUPDRV_CSRSS_NOT_FOUND;
+ for (uint32_t offBuf = 0; offBuf <= cbNeeded - sizeof(SYSTEM_PROCESS_INFORMATION);)
+ {
+ PRTNT_SYSTEM_PROCESS_INFORMATION pProcInfo = (PRTNT_SYSTEM_PROCESS_INFORMATION)&pbBuf[offBuf];
+ if ( pProcInfo->ProcessName.Length == 9 * sizeof(WCHAR)
+ && pProcInfo->NumberOfThreads > 2 /* Very low guess. */
+ && pProcInfo->HandleCount > 32 /* Very low guess, I hope. */
+ && (uintptr_t)pProcInfo->ProcessName.Buffer - (uintptr_t)pbBuf < cbNeeded
+ && RT_C_TO_LOWER(pProcInfo->ProcessName.Buffer[0]) == 'c'
+ && RT_C_TO_LOWER(pProcInfo->ProcessName.Buffer[1]) == 's'
+ && RT_C_TO_LOWER(pProcInfo->ProcessName.Buffer[2]) == 'r'
+ && RT_C_TO_LOWER(pProcInfo->ProcessName.Buffer[3]) == 's'
+ && RT_C_TO_LOWER(pProcInfo->ProcessName.Buffer[4]) == 's'
+ && pProcInfo->ProcessName.Buffer[5] == '.'
+ && RT_C_TO_LOWER(pProcInfo->ProcessName.Buffer[6]) == 'e'
+ && RT_C_TO_LOWER(pProcInfo->ProcessName.Buffer[7]) == 'x'
+ && RT_C_TO_LOWER(pProcInfo->ProcessName.Buffer[8]) == 'e' )
+ {
+
+ /* Get the process structure and perform some more thorough
+ process checks. */
+ PEPROCESS pProcess;
+ rcNt = PsLookupProcessByProcessId(pProcInfo->UniqueProcessId, &pProcess);
+ if (NT_SUCCESS(rcNt))
+ {
+ if (supdrvNtProtectIsCsrssByProcess(pProcess))
+ {
+ if (PsGetProcessSessionId(pProcess) == uSessionId)
+ {
+ /* Final test, check the ApiPort.
+ Note! The old LPC (pre Vista) objects has the PID
+ much earlier in the structure. Might be
+ worth looking for it instead. */
+ bool fThatsIt = false;
+ __try
+ {
+ PEPROCESS *ppPortProc = (PEPROCESS *)pvApiPortObj;
+ uint32_t cTests = g_uNtVerCombined >= SUP_NT_VER_VISTA ? 16 : 38; /* ALPC since Vista. */
+ do
+ {
+ fThatsIt = *ppPortProc == pProcess;
+ ppPortProc++;
+ } while (!fThatsIt && --cTests > 0);
+ }
+ __except(EXCEPTION_EXECUTE_HANDLER)
+ {
+ fThatsIt = false;
+ }
+ if (fThatsIt)
+ {
+ /* Ok, we found it! Keep the process structure
+ reference as well as the PID so we can
+ safely identify it later on. */
+ pNtProtect->hCsrssPid = pProcInfo->UniqueProcessId;
+ pNtProtect->pCsrssProcess = pProcess;
+ rc = VINF_SUCCESS;
+ break;
+ }
+ }
+ }
+
+ ObDereferenceObject(pProcess);
+ }
+ }
+
+ /* Advance. */
+ if (!pProcInfo->NextEntryOffset)
+ break;
+ offBuf += pProcInfo->NextEntryOffset;
+ }
+ }
+ else
+ rc = VERR_SUPDRV_SESSION_PROCESS_ENUM_ERROR;
+ RTMemFree(pbBuf);
+ ObDereferenceObject(pvApiPortObj);
+ return rc;
+}
+
+
+/**
+ * Checks that the given process is the CSRSS process associated with protected
+ * process.
+ *
+ * @returns true / false.
+ * @param pNtProtect The NT protection structure.
+ * @param pCsrss The process structure of the alleged CSRSS.EXE
+ * process.
+ */
+static bool supdrvNtProtectIsAssociatedCsrss(PSUPDRVNTPROTECT pNtProtect, PEPROCESS pCsrss)
+{
+ if (pNtProtect->pCsrssProcess == pCsrss)
+ {
+ if (pNtProtect->hCsrssPid == PsGetProcessId(pCsrss))
+ {
+ return true;
+ }
+ }
+ return false;
+}
+
+
+/**
+ * Checks if the given process is the stupid themes service.
+ *
+ * The caller does some screening of access masks and what not. We do the rest.
+ *
+ * @returns true / false.
+ * @param pNtProtect The NT protection structure.
+ * @param pAnnoyingProcess The process structure of an process that might
+ * happen to be the annoying themes process.
+ */
+static bool supdrvNtProtectIsFrigginThemesService(PSUPDRVNTPROTECT pNtProtect, PEPROCESS pAnnoyingProcess)
+{
+ RT_NOREF1(pNtProtect);
+
+ /*
+ * Check the process name.
+ */
+ if (!supdrvNtProtectIsSystem32ProcessMatch(pAnnoyingProcess, "svchost.exe"))
+ return false;
+
+ /** @todo Come up with more checks. */
+
+ return true;
+}
+
+
+#ifdef VBOX_WITHOUT_DEBUGGER_CHECKS
+/**
+ * Checks if the given process is one of the whitelisted debuggers.
+ *
+ * @returns true / false.
+ * @param pProcess The process to check.
+ */
+static bool supdrvNtProtectIsWhitelistedDebugger(PEPROCESS pProcess)
+{
+ const char *pszImageFile = (const char *)PsGetProcessImageFileName(pProcess);
+ if (!pszImageFile)
+ return false;
+
+ if (pszImageFile[0] == 'w' || pszImageFile[0] == 'W')
+ {
+ if (RTStrICmp(pszImageFile, "windbg.exe") == 0)
+ return true;
+ if (RTStrICmp(pszImageFile, "werfault.exe") == 0)
+ return true;
+ if (RTStrICmp(pszImageFile, "werfaultsecure.exe") == 0)
+ return true;
+ }
+ else if (pszImageFile[0] == 'd' || pszImageFile[0] == 'D')
+ {
+ if (RTStrICmp(pszImageFile, "drwtsn32.exe") == 0)
+ return true;
+ if (RTStrICmp(pszImageFile, "dwwin.exe") == 0)
+ return true;
+ }
+
+ return false;
+}
+#endif /* VBOX_WITHOUT_DEBUGGER_CHECKS */
+
+
+/** @} */
+
+
+/** @name Process Creation Callbacks.
+ * @{ */
+
+
+/**
+ * Cleans up VBoxDrv or VBoxDrvStub error info not collected by the dead process.
+ *
+ * @param hProcessId The ID of the dead process.
+ */
+static void supdrvNtErrorInfoCleanupProcess(HANDLE hProcessId)
+{
+ int rc = RTSemMutexRequestNoResume(g_hErrorInfoLock, RT_INDEFINITE_WAIT);
+ if (RT_SUCCESS(rc))
+ {
+ PSUPDRVNTERRORINFO pCur, pNext;
+ RTListForEachSafe(&g_ErrorInfoHead, pCur, pNext, SUPDRVNTERRORINFO, ListEntry)
+ {
+ if (pCur->hProcessId == hProcessId)
+ {
+ RTListNodeRemove(&pCur->ListEntry);
+ RTMemFree(pCur);
+ }
+ }
+ RTSemMutexRelease(g_hErrorInfoLock);
+ }
+}
+
+
+/**
+ * Common worker used by the process creation hooks as well as the process
+ * handle creation hooks to check if a VM process is being created.
+ *
+ * @returns true if likely to be a VM process, false if not.
+ * @param pNtStub The NT protection structure for the possible
+ * stub process.
+ * @param hParentPid The parent pid.
+ * @param hChildPid The child pid.
+ */
+static bool supdrvNtProtectIsSpawningStubProcess(PSUPDRVNTPROTECT pNtStub, HANDLE hParentPid, HANDLE hChildPid)
+{
+ bool fRc = false;
+ if (pNtStub->AvlCore.Key == hParentPid) /* paranoia */
+ {
+ if (pNtStub->enmProcessKind == kSupDrvNtProtectKind_StubSpawning)
+ {
+ /* Compare short names. */
+ PEPROCESS pStubProcess;
+ NTSTATUS rcNt = PsLookupProcessByProcessId(hParentPid, &pStubProcess);
+ if (NT_SUCCESS(rcNt))
+ {
+ PEPROCESS pChildProcess;
+ rcNt = PsLookupProcessByProcessId(hChildPid, &pChildProcess);
+ if (NT_SUCCESS(rcNt))
+ {
+ const char *pszStub = (const char *)PsGetProcessImageFileName(pStubProcess);
+ const char *pszChild = (const char *)PsGetProcessImageFileName(pChildProcess);
+ fRc = pszStub != NULL
+ && pszChild != NULL
+ && strcmp(pszStub, pszChild) == 0;
+
+ /** @todo check that the full image names matches. */
+
+ ObDereferenceObject(pChildProcess);
+ }
+ ObDereferenceObject(pStubProcess);
+ }
+ }
+ }
+ return fRc;
+}
+
+
+/**
+ * Common code used by the notifies to protect a child process.
+ *
+ * @returns VBox status code.
+ * @param pNtStub The NT protect structure for the parent.
+ * @param hChildPid The child pid.
+ */
+static int supdrvNtProtectProtectNewStubChild(PSUPDRVNTPROTECT pNtParent, HANDLE hChildPid)
+{
+ /*
+ * Create a child protection struction.
+ */
+ PSUPDRVNTPROTECT pNtChild;
+ int rc = supdrvNtProtectCreate(&pNtChild, hChildPid, kSupDrvNtProtectKind_VmProcessUnconfirmed, false /*fLink*/);
+ if (RT_SUCCESS(rc))
+ {
+ pNtChild->fFirstProcessCreateHandle = true;
+ pNtChild->fFirstThreadCreateHandle = true;
+ pNtChild->fCsrssFirstProcessCreateHandle = true;
+ pNtChild->cCsrssFirstProcessDuplicateHandle = ARCH_BITS == 32 ? 2 : 1;
+ pNtChild->fThemesFirstProcessCreateHandle = true;
+ pNtChild->hParentPid = pNtParent->AvlCore.Key;
+ pNtChild->hCsrssPid = pNtParent->hCsrssPid;
+ pNtChild->pCsrssProcess = pNtParent->pCsrssProcess;
+ if (pNtChild->pCsrssProcess)
+ ObReferenceObject(pNtChild->pCsrssProcess);
+
+ /*
+ * Take the spinlock, recheck parent conditions and link things.
+ */
+ RTSpinlockAcquire(g_hNtProtectLock);
+ if (pNtParent->enmProcessKind == kSupDrvNtProtectKind_StubSpawning)
+ {
+ bool fSuccess = RTAvlPVInsert(&g_NtProtectTree, &pNtChild->AvlCore);
+ if (fSuccess)
+ {
+ pNtChild->fInTree = true;
+ pNtParent->u.pChild = pNtChild; /* Parent keeps the initial reference. */
+ pNtParent->enmProcessKind = kSupDrvNtProtectKind_StubParent;
+ pNtChild->u.pParent = pNtParent;
+
+ RTSpinlockRelease(g_hNtProtectLock);
+ return VINF_SUCCESS;
+ }
+
+ rc = VERR_INTERNAL_ERROR_2;
+ }
+ else
+ rc = VERR_WRONG_ORDER;
+ pNtChild->enmProcessKind = kSupDrvNtProtectKind_VmProcessDead;
+ RTSpinlockRelease(g_hNtProtectLock);
+
+ supdrvNtProtectRelease(pNtChild);
+ }
+ return rc;
+}
+
+
+/**
+ * Common process termination code.
+ *
+ * Transitions protected process to the dead states, protecting against handle
+ * PID reuse (esp. with unconfirmed VM processes) and handle cleanup issues.
+ *
+ * @param hDeadPid The PID of the dead process.
+ */
+static void supdrvNtProtectUnprotectDeadProcess(HANDLE hDeadPid)
+{
+ PSUPDRVNTPROTECT pNtProtect = supdrvNtProtectLookup(hDeadPid);
+ if (pNtProtect)
+ {
+ PSUPDRVNTPROTECT pNtChild = NULL;
+
+ RTSpinlockAcquire(g_hNtProtectLock);
+
+ /*
+ * If this is an unconfirmed VM process, we must release the reference
+ * the parent structure holds.
+ */
+ if (pNtProtect->enmProcessKind == kSupDrvNtProtectKind_VmProcessUnconfirmed)
+ {
+ PSUPDRVNTPROTECT pNtParent = pNtProtect->u.pParent;
+ AssertRelease(pNtParent); AssertRelease(pNtParent->u.pChild == pNtProtect);
+ pNtParent->u.pChild = NULL;
+ pNtProtect->u.pParent = NULL;
+ pNtChild = pNtProtect;
+ }
+ /*
+ * If this is a stub exitting before the VM process gets confirmed,
+ * release the protection of the potential VM process as this is not
+ * the prescribed behavior.
+ */
+ else if ( pNtProtect->enmProcessKind == kSupDrvNtProtectKind_StubParent
+ && pNtProtect->u.pChild)
+ {
+ pNtChild = pNtProtect->u.pChild;
+ pNtProtect->u.pChild = NULL;
+ pNtChild->u.pParent = NULL;
+ pNtChild->enmProcessKind = kSupDrvNtProtectKind_VmProcessDead;
+ }
+
+ /*
+ * Transition it to the dead state to prevent it from opening the
+ * support driver again or be posthumously abused as a vm process parent.
+ */
+ if ( pNtProtect->enmProcessKind == kSupDrvNtProtectKind_VmProcessUnconfirmed
+ || pNtProtect->enmProcessKind == kSupDrvNtProtectKind_VmProcessConfirmed)
+ pNtProtect->enmProcessKind = kSupDrvNtProtectKind_VmProcessDead;
+ else if ( pNtProtect->enmProcessKind == kSupDrvNtProtectKind_StubParent
+ || pNtProtect->enmProcessKind == kSupDrvNtProtectKind_StubSpawning
+ || pNtProtect->enmProcessKind == kSupDrvNtProtectKind_StubUnverified)
+ pNtProtect->enmProcessKind = kSupDrvNtProtectKind_StubDead;
+
+ RTSpinlockRelease(g_hNtProtectLock);
+
+ supdrvNtProtectRelease(pNtProtect);
+ supdrvNtProtectRelease(pNtChild);
+
+ /*
+ * Do session cleanups.
+ */
+ AssertReturnVoid((HANDLE)(uintptr_t)RTProcSelf() == hDeadPid);
+ if (g_pDevObjSys)
+ {
+ PSUPDRVDEVEXT pDevExt = (PSUPDRVDEVEXT)g_pDevObjSys->DeviceExtension;
+ PSUPDRVSESSION pSession = supdrvSessionHashTabLookup(pDevExt, (RTPROCESS)(uintptr_t)hDeadPid,
+ RTR0ProcHandleSelf(), NULL);
+ if (pSession)
+ {
+ supdrvSessionHashTabRemove(pDevExt, pSession, NULL);
+ supdrvSessionRelease(pSession); /* Drops the reference from supdrvSessionHashTabLookup. */
+ }
+ }
+ }
+}
+
+
+/**
+ * Common worker for the process creation callback that verifies a new child
+ * being created by the handle creation callback code.
+ *
+ * @param pNtStub The parent.
+ * @param pNtVm The child.
+ * @param fCallerChecks The result of any additional tests the caller made.
+ * This is in order to avoid duplicating the failure
+ * path code.
+ */
+static void supdrvNtProtectVerifyNewChildProtection(PSUPDRVNTPROTECT pNtStub, PSUPDRVNTPROTECT pNtVm, bool fCallerChecks)
+{
+ if ( fCallerChecks
+ && pNtStub->enmProcessKind == kSupDrvNtProtectKind_StubParent
+ && pNtVm->enmProcessKind == kSupDrvNtProtectKind_VmProcessUnconfirmed
+ && pNtVm->u.pParent == pNtStub
+ && pNtStub->u.pChild == pNtVm)
+ {
+ /* Fine, reset the CSRSS hack (fixes ViRobot APT Shield 2.0 issue). */
+ pNtVm->fFirstProcessCreateHandle = true;
+ return;
+ }
+
+ LogRel(("vboxdrv: Misdetected vm stub; hParentPid=%p hChildPid=%p\n", pNtStub->AvlCore.Key, pNtVm->AvlCore.Key));
+ if (pNtStub->enmProcessKind != kSupDrvNtProtectKind_VmProcessConfirmed)
+ supdrvNtProtectUnprotectDeadProcess(pNtVm->AvlCore.Key);
+}
+
+
+/**
+ * Old style callback (since forever).
+ *
+ * @param hParentPid The parent PID.
+ * @param hNewPid The PID of the new child.
+ * @param fCreated TRUE if it's a creation notification,
+ * FALSE if termination.
+ * @remarks ASSUMES this arrives before the handle creation callback.
+ */
+static VOID __stdcall
+supdrvNtProtectCallback_ProcessCreateNotify(HANDLE hParentPid, HANDLE hNewPid, BOOLEAN fCreated)
+{
+ /*
+ * Is it a new process that needs protection?
+ */
+ if (fCreated)
+ {
+ PSUPDRVNTPROTECT pNtStub = supdrvNtProtectLookup(hParentPid);
+ if (pNtStub)
+ {
+ PSUPDRVNTPROTECT pNtVm = supdrvNtProtectLookup(hNewPid);
+ if (!pNtVm)
+ {
+ if (supdrvNtProtectIsSpawningStubProcess(pNtStub, hParentPid, hNewPid))
+ supdrvNtProtectProtectNewStubChild(pNtStub, hNewPid);
+ }
+ else
+ {
+ supdrvNtProtectVerifyNewChildProtection(pNtStub, pNtVm, true);
+ supdrvNtProtectRelease(pNtVm);
+ }
+ supdrvNtProtectRelease(pNtStub);
+ }
+ }
+ /*
+ * Process termination, do clean ups.
+ */
+ else
+ {
+ supdrvNtProtectUnprotectDeadProcess(hNewPid);
+ supdrvNtErrorInfoCleanupProcess(hNewPid);
+ }
+}
+
+
+/**
+ * New style callback (Vista SP1+ / w2k8).
+ *
+ * @param pNewProcess The new process.
+ * @param hNewPid The PID of the new process.
+ * @param pInfo Process creation details. NULL if process
+ * termination notification.
+ * @remarks ASSUMES this arrives before the handle creation callback.
+ */
+static VOID __stdcall
+supdrvNtProtectCallback_ProcessCreateNotifyEx(PEPROCESS pNewProcess, HANDLE hNewPid, PPS_CREATE_NOTIFY_INFO pInfo)
+{
+ RT_NOREF1(pNewProcess);
+
+ /*
+ * Is it a new process that needs protection?
+ */
+ if (pInfo)
+ {
+ PSUPDRVNTPROTECT pNtStub = supdrvNtProtectLookup(pInfo->CreatingThreadId.UniqueProcess);
+
+ Log(("vboxdrv/NewProcessEx: ctx=%04zx/%p pid=%04zx ppid=%04zx ctor=%04zx/%04zx rcNt=%#x %.*ls\n",
+ PsGetProcessId(PsGetCurrentProcess()), PsGetCurrentProcess(),
+ hNewPid, pInfo->ParentProcessId,
+ pInfo->CreatingThreadId.UniqueProcess, pInfo->CreatingThreadId.UniqueThread, pInfo->CreationStatus,
+ pInfo->FileOpenNameAvailable && pInfo->ImageFileName ? (size_t)pInfo->ImageFileName->Length / 2 : 0,
+ pInfo->FileOpenNameAvailable && pInfo->ImageFileName ? pInfo->ImageFileName->Buffer : NULL));
+
+ if (pNtStub)
+ {
+ PSUPDRVNTPROTECT pNtVm = supdrvNtProtectLookup(hNewPid);
+ if (!pNtVm)
+ {
+ /* Parent must be creator. */
+ if (pInfo->CreatingThreadId.UniqueProcess == pInfo->ParentProcessId)
+ {
+ if (supdrvNtProtectIsSpawningStubProcess(pNtStub, pInfo->ParentProcessId, hNewPid))
+ supdrvNtProtectProtectNewStubChild(pNtStub, hNewPid);
+ }
+ }
+ else
+ {
+ /* Parent must be creator (as above). */
+ supdrvNtProtectVerifyNewChildProtection(pNtStub, pNtVm,
+ pInfo->CreatingThreadId.UniqueProcess == pInfo->ParentProcessId);
+ supdrvNtProtectRelease(pNtVm);
+ }
+ supdrvNtProtectRelease(pNtStub);
+ }
+ }
+ /*
+ * Process termination, do clean ups.
+ */
+ else
+ {
+ supdrvNtProtectUnprotectDeadProcess(hNewPid);
+ supdrvNtErrorInfoCleanupProcess(hNewPid);
+ }
+}
+
+/** @} */
+
+
+/** @name Process Handle Callbacks.
+ * @{ */
+
+/** Process rights that we allow for handles to stub and VM processes. */
+# define SUPDRV_NT_ALLOW_PROCESS_RIGHTS \
+ ( PROCESS_TERMINATE \
+ | PROCESS_VM_READ \
+ | PROCESS_QUERY_INFORMATION \
+ | PROCESS_QUERY_LIMITED_INFORMATION \
+ | PROCESS_SUSPEND_RESUME \
+ | DELETE \
+ | READ_CONTROL \
+ | SYNCHRONIZE)
+
+/** Evil process rights. */
+# define SUPDRV_NT_EVIL_PROCESS_RIGHTS \
+ ( PROCESS_CREATE_THREAD \
+ | PROCESS_SET_SESSIONID /*?*/ \
+ | PROCESS_VM_OPERATION \
+ | PROCESS_VM_WRITE \
+ | PROCESS_DUP_HANDLE \
+ | PROCESS_CREATE_PROCESS /*?*/ \
+ | PROCESS_SET_QUOTA /*?*/ \
+ | PROCESS_SET_INFORMATION \
+ | PROCESS_SET_LIMITED_INFORMATION /*?*/ \
+ | 0)
+AssertCompile((SUPDRV_NT_ALLOW_PROCESS_RIGHTS & SUPDRV_NT_EVIL_PROCESS_RIGHTS) == 0);
+
+
+static OB_PREOP_CALLBACK_STATUS __stdcall
+supdrvNtProtectCallback_ProcessHandlePre(PVOID pvUser, POB_PRE_OPERATION_INFORMATION pOpInfo)
+{
+ Assert(pvUser == NULL); RT_NOREF1(pvUser);
+ Assert(pOpInfo->Operation == OB_OPERATION_HANDLE_CREATE || pOpInfo->Operation == OB_OPERATION_HANDLE_DUPLICATE);
+ Assert(pOpInfo->ObjectType == *PsProcessType);
+
+ /*
+ * Protected? Kludge required for NtOpenProcess calls comming in before
+ * the create process hook triggers on Windows 8.1 (possibly others too).
+ */
+ HANDLE hObjPid = PsGetProcessId((PEPROCESS)pOpInfo->Object);
+ PSUPDRVNTPROTECT pNtProtect = supdrvNtProtectLookup(hObjPid);
+ if (!pNtProtect)
+ {
+ HANDLE hParentPid = PsGetProcessInheritedFromUniqueProcessId((PEPROCESS)pOpInfo->Object);
+ PSUPDRVNTPROTECT pNtStub = supdrvNtProtectLookup(hParentPid);
+ if (pNtStub)
+ {
+ if (supdrvNtProtectIsSpawningStubProcess(pNtStub, hParentPid, hObjPid))
+ {
+ supdrvNtProtectProtectNewStubChild(pNtStub, hObjPid);
+ pNtProtect = supdrvNtProtectLookup(hObjPid);
+ }
+ supdrvNtProtectRelease(pNtStub);
+ }
+ }
+ pOpInfo->CallContext = pNtProtect; /* Just for reference. */
+ if (pNtProtect)
+ {
+ /*
+ * Ok, it's a protected process. Strip rights as required or possible.
+ */
+ static ACCESS_MASK const s_fCsrssStupidDesires = 0x1fffff;
+ ACCESS_MASK fAllowedRights = SUPDRV_NT_ALLOW_PROCESS_RIGHTS;
+
+ if (pOpInfo->Operation == OB_OPERATION_HANDLE_CREATE)
+ {
+ /* Don't restrict the process accessing itself. */
+ if ((PEPROCESS)pOpInfo->Object == PsGetCurrentProcess())
+ {
+ pOpInfo->CallContext = NULL; /* don't assert */
+ pNtProtect->fFirstProcessCreateHandle = false;
+
+ Log(("vboxdrv/ProcessHandlePre: %sctx=%04zx/%p wants %#x to %p in pid=%04zx [%d] %s\n",
+ pOpInfo->KernelHandle ? "k" : "", PsGetProcessId(PsGetCurrentProcess()), PsGetCurrentProcess(),
+ pOpInfo->Parameters->CreateHandleInformation.DesiredAccess,
+ pOpInfo->Object, pNtProtect->AvlCore.Key, pNtProtect->enmProcessKind,
+ PsGetProcessImageFileName(PsGetCurrentProcess()) ));
+ }
+#ifdef VBOX_WITHOUT_DEBUGGER_CHECKS
+ /* Allow debuggers full access. */
+ else if (supdrvNtProtectIsWhitelistedDebugger(PsGetCurrentProcess()))
+ {
+ pOpInfo->CallContext = NULL; /* don't assert */
+ pNtProtect->fFirstProcessCreateHandle = false;
+
+ Log(("vboxdrv/ProcessHandlePre: %sctx=%04zx/%p wants %#x to %p in pid=%04zx [%d] %s [debugger]\n",
+ pOpInfo->KernelHandle ? "k" : "", PsGetProcessId(PsGetCurrentProcess()), PsGetCurrentProcess(),
+ pOpInfo->Parameters->CreateHandleInformation.DesiredAccess,
+ pOpInfo->Object, pNtProtect->AvlCore.Key, pNtProtect->enmProcessKind,
+ PsGetProcessImageFileName(PsGetCurrentProcess()) ));
+ }
+#endif
+ else
+ {
+ ACCESS_MASK const fDesiredAccess = pOpInfo->Parameters->CreateHandleInformation.DesiredAccess;
+
+ /* Special case 1 on Vista, 7 & 8:
+ The CreateProcess code passes the handle over to CSRSS.EXE
+ and the code inBaseSrvCreateProcess will duplicate the
+ handle with 0x1fffff as access mask. NtDuplicateObject will
+ fail this call before it ever gets down here.
+
+ Special case 2 on 8.1:
+ The CreateProcess code requires additional rights for
+ something, we'll drop these in the stub code. */
+ if ( pNtProtect->enmProcessKind == kSupDrvNtProtectKind_VmProcessUnconfirmed
+ && pNtProtect->fFirstProcessCreateHandle
+ && pOpInfo->KernelHandle == 0
+ && pNtProtect->hParentPid == PsGetProcessId(PsGetCurrentProcess())
+ && ExGetPreviousMode() != KernelMode)
+ {
+ if ( !pOpInfo->KernelHandle
+ && fDesiredAccess == s_fCsrssStupidDesires)
+ {
+ if (g_uNtVerCombined < SUP_MAKE_NT_VER_SIMPLE(6, 3))
+ fAllowedRights |= s_fCsrssStupidDesires;
+ else
+ fAllowedRights = fAllowedRights
+ | PROCESS_VM_OPERATION
+ | PROCESS_VM_WRITE
+ | PROCESS_SET_INFORMATION
+ | PROCESS_SET_LIMITED_INFORMATION
+ | 0;
+ pOpInfo->CallContext = NULL; /* don't assert this. */
+ }
+ pNtProtect->fFirstProcessCreateHandle = false;
+ }
+
+ /* Special case 3 on 8.1:
+ The interaction between the CreateProcess code and CSRSS.EXE
+ has changed to the better with Windows 8.1. CSRSS.EXE no
+ longer duplicates the process (thread too) handle, but opens
+ it, thus allowing us to do our job. */
+ if ( g_uNtVerCombined >= SUP_MAKE_NT_VER_SIMPLE(6, 3)
+ && pNtProtect->enmProcessKind == kSupDrvNtProtectKind_VmProcessUnconfirmed
+ && pNtProtect->fCsrssFirstProcessCreateHandle
+ && pOpInfo->KernelHandle == 0
+ && ExGetPreviousMode() == UserMode
+ && supdrvNtProtectIsAssociatedCsrss(pNtProtect, PsGetCurrentProcess()) )
+ {
+ pNtProtect->fCsrssFirstProcessCreateHandle = false;
+ if (fDesiredAccess == s_fCsrssStupidDesires)
+ {
+ /* Not needed: PROCESS_CREATE_THREAD, PROCESS_SET_SESSIONID,
+ PROCESS_CREATE_PROCESS */
+ fAllowedRights = fAllowedRights
+ | PROCESS_VM_OPERATION
+ | PROCESS_VM_WRITE
+ | PROCESS_DUP_HANDLE /* Needed for CreateProcess/VBoxTestOGL. */
+ | 0;
+ pOpInfo->CallContext = NULL; /* don't assert this. */
+ }
+ }
+
+ /* Special case 4, Windows 7, Vista, possibly 8, but not 8.1:
+ The Themes service requires PROCESS_DUP_HANDLE access to our
+ process or we won't get any menus and dialogs will be half
+ unreadable. This is _very_ unfortunate and more work will
+ go into making this more secure. */
+ if ( g_uNtVerCombined >= SUP_MAKE_NT_VER_SIMPLE(6, 0)
+ && g_uNtVerCombined < SUP_MAKE_NT_VER_SIMPLE(6, 2)
+ && fDesiredAccess == 0x1478 /* 6.1.7600.16385 (win7_rtm.090713-1255) */
+ && pNtProtect->fThemesFirstProcessCreateHandle
+ && pOpInfo->KernelHandle == 0
+ && ExGetPreviousMode() == UserMode
+ && supdrvNtProtectIsFrigginThemesService(pNtProtect, PsGetCurrentProcess()) )
+ {
+ pNtProtect->fThemesFirstProcessCreateHandle = true; /* Only once! */
+ fAllowedRights |= PROCESS_DUP_HANDLE;
+ pOpInfo->CallContext = NULL; /* don't assert this. */
+ }
+
+ /* Special case 6a, Windows 10+: AudioDG.exe opens the process with the
+ PROCESS_SET_LIMITED_INFORMATION right. It seems like it need it for
+ some myserious and weirdly placed cpu set management of our process.
+ I'd love to understand what that's all about...
+ Currently playing safe and only grand this right, however limited, to
+ audiodg.exe. */
+ if ( g_uNtVerCombined >= SUP_MAKE_NT_VER_SIMPLE(10, 0)
+ && ( fDesiredAccess == PROCESS_SET_LIMITED_INFORMATION
+ || fDesiredAccess == (PROCESS_SET_LIMITED_INFORMATION | PROCESS_QUERY_LIMITED_INFORMATION) /* expected fix #1 */
+ || fDesiredAccess == (PROCESS_SET_LIMITED_INFORMATION | PROCESS_QUERY_INFORMATION) /* expected fix #2 */
+ )
+ && pOpInfo->KernelHandle == 0
+ && ExGetPreviousMode() == UserMode
+ && supdrvNtProtectIsSystem32ProcessMatch(PsGetCurrentProcess(), "audiodg.exe") )
+ {
+ fAllowedRights |= PROCESS_SET_LIMITED_INFORMATION;
+ pOpInfo->CallContext = NULL; /* don't assert this. */
+ }
+
+ Log(("vboxdrv/ProcessHandlePre: %sctx=%04zx/%p wants %#x to %p/pid=%04zx [%d], allow %#x => %#x; %s [prev=%#x]\n",
+ pOpInfo->KernelHandle ? "k" : "", PsGetProcessId(PsGetCurrentProcess()), PsGetCurrentProcess(),
+ fDesiredAccess, pOpInfo->Object, pNtProtect->AvlCore.Key, pNtProtect->enmProcessKind,
+ fAllowedRights, fDesiredAccess & fAllowedRights,
+ PsGetProcessImageFileName(PsGetCurrentProcess()), ExGetPreviousMode() ));
+
+ pOpInfo->Parameters->CreateHandleInformation.DesiredAccess &= fAllowedRights;
+ }
+ }
+ else
+ {
+ /* Don't restrict the process accessing itself. */
+ if ( (PEPROCESS)pOpInfo->Object == PsGetCurrentProcess()
+ && pOpInfo->Parameters->DuplicateHandleInformation.TargetProcess == pOpInfo->Object)
+ {
+ Log(("vboxdrv/ProcessHandlePre: ctx=%04zx/%p[%p] dup from %04zx/%p with %#x to %p in pid=%04zx [%d] %s\n",
+ PsGetProcessId(PsGetCurrentProcess()), PsGetCurrentProcess(),
+ pOpInfo->Parameters->DuplicateHandleInformation.TargetProcess,
+ PsGetProcessId((PEPROCESS)pOpInfo->Parameters->DuplicateHandleInformation.SourceProcess),
+ pOpInfo->Parameters->DuplicateHandleInformation.SourceProcess,
+ pOpInfo->Parameters->DuplicateHandleInformation.DesiredAccess,
+ pOpInfo->Object, pNtProtect->AvlCore.Key, pNtProtect->enmProcessKind,
+ PsGetProcessImageFileName(PsGetCurrentProcess()) ));
+
+ pOpInfo->CallContext = NULL; /* don't assert */
+ }
+ else
+ {
+ ACCESS_MASK const fDesiredAccess = pOpInfo->Parameters->DuplicateHandleInformation.DesiredAccess;
+
+ /* Special case 5 on Vista, 7 & 8:
+ This is the CSRSS.EXE end of special case #1. */
+ if ( g_uNtVerCombined < SUP_MAKE_NT_VER_SIMPLE(6, 3)
+ && pNtProtect->enmProcessKind == kSupDrvNtProtectKind_VmProcessUnconfirmed
+ && pNtProtect->cCsrssFirstProcessDuplicateHandle > 0
+ && pOpInfo->KernelHandle == 0
+ && fDesiredAccess == s_fCsrssStupidDesires
+ && pNtProtect->hParentPid
+ == PsGetProcessId((PEPROCESS)pOpInfo->Parameters->DuplicateHandleInformation.SourceProcess)
+ && pOpInfo->Parameters->DuplicateHandleInformation.TargetProcess == PsGetCurrentProcess()
+ && ExGetPreviousMode() == UserMode
+ && supdrvNtProtectIsAssociatedCsrss(pNtProtect, PsGetCurrentProcess()))
+ {
+ if (ASMAtomicDecS32(&pNtProtect->cCsrssFirstProcessDuplicateHandle) >= 0)
+ {
+ /* Not needed: PROCESS_CREATE_THREAD, PROCESS_SET_SESSIONID,
+ PROCESS_CREATE_PROCESS, PROCESS_DUP_HANDLE */
+ fAllowedRights = fAllowedRights
+ | PROCESS_VM_OPERATION
+ | PROCESS_VM_WRITE
+ | PROCESS_DUP_HANDLE /* Needed for launching VBoxTestOGL. */
+ | 0;
+ pOpInfo->CallContext = NULL; /* don't assert this. */
+ }
+ }
+
+ /* Special case 6b, Windows 10+: AudioDG.exe duplicates the handle it opened above. */
+ if ( g_uNtVerCombined >= SUP_MAKE_NT_VER_SIMPLE(10, 0)
+ && ( fDesiredAccess == PROCESS_SET_LIMITED_INFORMATION
+ || fDesiredAccess == (PROCESS_SET_LIMITED_INFORMATION | PROCESS_QUERY_LIMITED_INFORMATION) /* expected fix #1 */
+ || fDesiredAccess == (PROCESS_SET_LIMITED_INFORMATION | PROCESS_QUERY_INFORMATION) /* expected fix #2 */
+ )
+ && pOpInfo->KernelHandle == 0
+ && ExGetPreviousMode() == UserMode
+ && supdrvNtProtectIsSystem32ProcessMatch(PsGetCurrentProcess(), "audiodg.exe") )
+ {
+ fAllowedRights |= PROCESS_SET_LIMITED_INFORMATION;
+ pOpInfo->CallContext = NULL; /* don't assert this. */
+ }
+
+ Log(("vboxdrv/ProcessHandlePre: %sctx=%04zx/%p[%p] dup from %04zx/%p with %#x to %p in pid=%04zx [%d] %s\n",
+ pOpInfo->KernelHandle ? "k" : "", PsGetProcessId(PsGetCurrentProcess()), PsGetCurrentProcess(),
+ pOpInfo->Parameters->DuplicateHandleInformation.TargetProcess,
+ PsGetProcessId((PEPROCESS)pOpInfo->Parameters->DuplicateHandleInformation.SourceProcess),
+ pOpInfo->Parameters->DuplicateHandleInformation.SourceProcess,
+ fDesiredAccess,
+ pOpInfo->Object, pNtProtect->AvlCore.Key, pNtProtect->enmProcessKind,
+ PsGetProcessImageFileName(PsGetCurrentProcess()) ));
+
+ pOpInfo->Parameters->DuplicateHandleInformation.DesiredAccess &= fAllowedRights;
+ }
+ }
+ supdrvNtProtectRelease(pNtProtect);
+ }
+
+ return OB_PREOP_SUCCESS;
+}
+
+
+static VOID __stdcall
+supdrvNtProtectCallback_ProcessHandlePost(PVOID pvUser, POB_POST_OPERATION_INFORMATION pOpInfo)
+{
+ Assert(pvUser == NULL); RT_NOREF1(pvUser);
+ Assert(pOpInfo->Operation == OB_OPERATION_HANDLE_CREATE || pOpInfo->Operation == OB_OPERATION_HANDLE_DUPLICATE);
+ Assert(pOpInfo->ObjectType == *PsProcessType);
+
+ if ( pOpInfo->CallContext
+ && NT_SUCCESS(pOpInfo->ReturnStatus))
+ {
+ ACCESS_MASK const fGrantedAccess = pOpInfo->Operation == OB_OPERATION_HANDLE_CREATE
+ ? pOpInfo->Parameters->CreateHandleInformation.GrantedAccess
+ : pOpInfo->Parameters->DuplicateHandleInformation.GrantedAccess;
+ AssertReleaseMsg( !(fGrantedAccess & ~( SUPDRV_NT_ALLOW_PROCESS_RIGHTS
+ | WRITE_OWNER | WRITE_DAC /* these two might be forced upon us */
+ | PROCESS_UNKNOWN_4000 /* Seen set on win 8.1 */
+ /*| PROCESS_UNKNOWN_8000 */ ) )
+ || pOpInfo->KernelHandle,
+ ("GrantedAccess=%#x - we allow %#x - we did not allow %#x\n",
+ fGrantedAccess, SUPDRV_NT_ALLOW_PROCESS_RIGHTS, fGrantedAccess & ~SUPDRV_NT_ALLOW_PROCESS_RIGHTS));
+ }
+}
+
+# undef SUPDRV_NT_ALLOW_PROCESS_RIGHTS
+
+/** @} */
+
+
+/** @name Thread Handle Callbacks
+ * @{ */
+
+/* From ntifs.h */
+extern "C" NTKERNELAPI PEPROCESS __stdcall IoThreadToProcess(PETHREAD);
+
+/** Thread rights that we allow for handles to stub and VM processes. */
+# define SUPDRV_NT_ALLOWED_THREAD_RIGHTS \
+ ( THREAD_TERMINATE \
+ | THREAD_GET_CONTEXT \
+ | THREAD_QUERY_INFORMATION \
+ | THREAD_QUERY_LIMITED_INFORMATION \
+ | DELETE \
+ | READ_CONTROL \
+ | SYNCHRONIZE)
+/** @todo consider THREAD_SET_LIMITED_INFORMATION & THREAD_RESUME */
+
+/** Evil thread rights.
+ * @remarks THREAD_RESUME is not included as it seems to be forced upon us by
+ * Windows 8.1, at least for some processes. We dont' actively
+ * allow it though, just tollerate it when forced to. */
+# define SUPDRV_NT_EVIL_THREAD_RIGHTS \
+ ( THREAD_SUSPEND_RESUME \
+ | THREAD_SET_CONTEXT \
+ | THREAD_SET_INFORMATION \
+ | THREAD_SET_LIMITED_INFORMATION /*?*/ \
+ | THREAD_SET_THREAD_TOKEN /*?*/ \
+ | THREAD_IMPERSONATE /*?*/ \
+ | THREAD_DIRECT_IMPERSONATION /*?*/ \
+ /*| THREAD_RESUME - see remarks. */ \
+ | 0)
+AssertCompile((SUPDRV_NT_EVIL_THREAD_RIGHTS & SUPDRV_NT_ALLOWED_THREAD_RIGHTS) == 0);
+
+
+static OB_PREOP_CALLBACK_STATUS __stdcall
+supdrvNtProtectCallback_ThreadHandlePre(PVOID pvUser, POB_PRE_OPERATION_INFORMATION pOpInfo)
+{
+ Assert(pvUser == NULL); RT_NOREF1(pvUser);
+ Assert(pOpInfo->Operation == OB_OPERATION_HANDLE_CREATE || pOpInfo->Operation == OB_OPERATION_HANDLE_DUPLICATE);
+ Assert(pOpInfo->ObjectType == *PsThreadType);
+
+ PEPROCESS pProcess = IoThreadToProcess((PETHREAD)pOpInfo->Object);
+ PSUPDRVNTPROTECT pNtProtect = supdrvNtProtectLookup(PsGetProcessId(pProcess));
+ pOpInfo->CallContext = pNtProtect; /* Just for reference. */
+ if (pNtProtect)
+ {
+ static ACCESS_MASK const s_fCsrssStupidDesires = 0x1fffff;
+ ACCESS_MASK fAllowedRights = SUPDRV_NT_ALLOWED_THREAD_RIGHTS;
+
+ if (pOpInfo->Operation == OB_OPERATION_HANDLE_CREATE)
+ {
+ /* Don't restrict the process accessing its own threads. */
+ if (pProcess == PsGetCurrentProcess())
+ {
+ Log(("vboxdrv/ThreadHandlePre: %sctx=%04zx/%p wants %#x to %p in pid=%04zx [%d] self\n",
+ pOpInfo->KernelHandle ? "k" : "", PsGetProcessId(PsGetCurrentProcess()), PsGetCurrentProcess(),
+ pOpInfo->Parameters->CreateHandleInformation.DesiredAccess,
+ pOpInfo->Object, pNtProtect->AvlCore.Key, pNtProtect->enmProcessKind));
+ pOpInfo->CallContext = NULL; /* don't assert */
+ pNtProtect->fFirstThreadCreateHandle = false;
+ }
+#ifdef VBOX_WITHOUT_DEBUGGER_CHECKS
+ /* Allow debuggers full access. */
+ else if (supdrvNtProtectIsWhitelistedDebugger(PsGetCurrentProcess()))
+ {
+ Log(("vboxdrv/ThreadHandlePre: %sctx=%04zx/%p wants %#x to %p in pid=%04zx [%d] %s [debugger]\n",
+ pOpInfo->KernelHandle ? "k" : "", PsGetProcessId(PsGetCurrentProcess()), PsGetCurrentProcess(),
+ pOpInfo->Parameters->CreateHandleInformation.DesiredAccess,
+ pOpInfo->Object, pNtProtect->AvlCore.Key, pNtProtect->enmProcessKind,
+ PsGetProcessImageFileName(PsGetCurrentProcess()) ));
+ pOpInfo->CallContext = NULL; /* don't assert */
+ }
+#endif
+ else
+ {
+ /* Special case 1 on Vista, 7, 8:
+ The CreateProcess code passes the handle over to CSRSS.EXE
+ and the code inBaseSrvCreateProcess will duplicate the
+ handle with 0x1fffff as access mask. NtDuplicateObject will
+ fail this call before it ever gets down here. */
+ if ( g_uNtVerCombined < SUP_MAKE_NT_VER_SIMPLE(6, 3)
+ && pNtProtect->enmProcessKind == kSupDrvNtProtectKind_VmProcessUnconfirmed
+ && pNtProtect->fFirstThreadCreateHandle
+ && pOpInfo->KernelHandle == 0
+ && ExGetPreviousMode() == UserMode
+ && pNtProtect->hParentPid == PsGetProcessId(PsGetCurrentProcess()) )
+ {
+ if ( !pOpInfo->KernelHandle
+ && pOpInfo->Parameters->CreateHandleInformation.DesiredAccess == s_fCsrssStupidDesires)
+ {
+ fAllowedRights |= s_fCsrssStupidDesires;
+ pOpInfo->CallContext = NULL; /* don't assert this. */
+ }
+ pNtProtect->fFirstThreadCreateHandle = false;
+ }
+
+ /* Special case 2 on 8.1, possibly also Vista, 7, 8:
+ When creating a process like VBoxTestOGL from the VM process,
+ CSRSS.EXE will try talk to the calling thread and, it
+ appears, impersonate it. We unfortunately need to allow
+ this or there will be no 3D support. Typical DbgPrint:
+ "SXS: BasepCreateActCtx() Calling csrss server failed. Status = 0xc00000a5" */
+ SUPDRVNTPROTECTKIND enmProcessKind;
+ if ( g_uNtVerCombined >= SUP_MAKE_NT_VER_COMBINED(6, 0, 0, 0, 0)
+ && ( (enmProcessKind = pNtProtect->enmProcessKind) == kSupDrvNtProtectKind_VmProcessConfirmed
+ || enmProcessKind == kSupDrvNtProtectKind_VmProcessUnconfirmed)
+ && pOpInfo->KernelHandle == 0
+ && ExGetPreviousMode() == UserMode
+ && supdrvNtProtectIsAssociatedCsrss(pNtProtect, PsGetCurrentProcess()) )
+ {
+ fAllowedRights |= THREAD_IMPERSONATE;
+ fAllowedRights |= THREAD_DIRECT_IMPERSONATION;
+ //fAllowedRights |= THREAD_SET_LIMITED_INFORMATION; - try without this one
+ pOpInfo->CallContext = NULL; /* don't assert this. */
+ }
+
+ Log(("vboxdrv/ThreadHandlePre: %sctx=%04zx/%p wants %#x to %p in pid=%04zx [%d], allow %#x => %#x; %s [prev=%#x]\n",
+ pOpInfo->KernelHandle ? "k" : "", PsGetProcessId(PsGetCurrentProcess()), PsGetCurrentProcess(),
+ pOpInfo->Parameters->CreateHandleInformation.DesiredAccess,
+ pOpInfo->Object, pNtProtect->AvlCore.Key, pNtProtect->enmProcessKind, fAllowedRights,
+ pOpInfo->Parameters->CreateHandleInformation.DesiredAccess & fAllowedRights,
+ PsGetProcessImageFileName(PsGetCurrentProcess()), ExGetPreviousMode()));
+
+ pOpInfo->Parameters->CreateHandleInformation.DesiredAccess &= fAllowedRights;
+ }
+ }
+ else
+ {
+ /* Don't restrict the process accessing its own threads. */
+ if ( pProcess == PsGetCurrentProcess()
+ && (PEPROCESS)pOpInfo->Parameters->DuplicateHandleInformation.TargetProcess == pProcess)
+ {
+ Log(("vboxdrv/ThreadHandlePre: %sctx=%04zx/%p[%p] dup from %04zx/%p with %#x to %p in pid=%04zx [%d] self\n",
+ pOpInfo->KernelHandle ? "k" : "", PsGetProcessId(PsGetCurrentProcess()), PsGetCurrentProcess(),
+ pOpInfo->Parameters->DuplicateHandleInformation.TargetProcess,
+ PsGetProcessId((PEPROCESS)pOpInfo->Parameters->DuplicateHandleInformation.SourceProcess),
+ pOpInfo->Parameters->DuplicateHandleInformation.SourceProcess,
+ pOpInfo->Parameters->DuplicateHandleInformation.DesiredAccess,
+ pOpInfo->Object, pNtProtect->AvlCore.Key, pNtProtect->enmProcessKind,
+ PsGetProcessImageFileName(PsGetCurrentProcess()) ));
+ pOpInfo->CallContext = NULL; /* don't assert */
+ }
+ else
+ {
+ /* Special case 3 on Vista, 7, 8:
+ This is the follow up to special case 1. */
+ SUPDRVNTPROTECTKIND enmProcessKind;
+ if ( g_uNtVerCombined >= SUP_MAKE_NT_VER_COMBINED(6, 0, 0, 0, 0)
+ && ( (enmProcessKind = pNtProtect->enmProcessKind) == kSupDrvNtProtectKind_VmProcessConfirmed
+ || enmProcessKind == kSupDrvNtProtectKind_VmProcessUnconfirmed)
+ && pOpInfo->Parameters->DuplicateHandleInformation.TargetProcess == PsGetCurrentProcess()
+ && pOpInfo->KernelHandle == 0
+ && ExGetPreviousMode() == UserMode
+ && supdrvNtProtectIsAssociatedCsrss(pNtProtect, PsGetCurrentProcess()) )
+ {
+ fAllowedRights |= THREAD_IMPERSONATE;
+ fAllowedRights |= THREAD_DIRECT_IMPERSONATION;
+ //fAllowedRights |= THREAD_SET_LIMITED_INFORMATION; - try without this one
+ pOpInfo->CallContext = NULL; /* don't assert this. */
+ }
+
+ Log(("vboxdrv/ThreadHandlePre: %sctx=%04zx/%p[%p] dup from %04zx/%p with %#x to %p in pid=%04zx [%d], allow %#x => %#x; %s\n",
+ pOpInfo->KernelHandle ? "k" : "", PsGetProcessId(PsGetCurrentProcess()), PsGetCurrentProcess(),
+ pOpInfo->Parameters->DuplicateHandleInformation.TargetProcess,
+ PsGetProcessId((PEPROCESS)pOpInfo->Parameters->DuplicateHandleInformation.SourceProcess),
+ pOpInfo->Parameters->DuplicateHandleInformation.SourceProcess,
+ pOpInfo->Parameters->DuplicateHandleInformation.DesiredAccess,
+ pOpInfo->Object, pNtProtect->AvlCore.Key, pNtProtect->enmProcessKind, fAllowedRights,
+ pOpInfo->Parameters->DuplicateHandleInformation.DesiredAccess & fAllowedRights,
+ PsGetProcessImageFileName(PsGetCurrentProcess()) ));
+
+ pOpInfo->Parameters->DuplicateHandleInformation.DesiredAccess &= fAllowedRights;
+ }
+ }
+
+ supdrvNtProtectRelease(pNtProtect);
+ }
+
+ return OB_PREOP_SUCCESS;
+}
+
+
+static VOID __stdcall
+supdrvNtProtectCallback_ThreadHandlePost(PVOID pvUser, POB_POST_OPERATION_INFORMATION pOpInfo)
+{
+ Assert(pvUser == NULL); RT_NOREF1(pvUser);
+ Assert(pOpInfo->Operation == OB_OPERATION_HANDLE_CREATE || pOpInfo->Operation == OB_OPERATION_HANDLE_DUPLICATE);
+ Assert(pOpInfo->ObjectType == *PsThreadType);
+
+ if ( pOpInfo->CallContext
+ && NT_SUCCESS(pOpInfo->ReturnStatus))
+ {
+ ACCESS_MASK const fGrantedAccess = pOpInfo->Parameters->CreateHandleInformation.GrantedAccess;
+ AssertReleaseMsg( !(fGrantedAccess & ~( SUPDRV_NT_ALLOWED_THREAD_RIGHTS
+ | WRITE_OWNER | WRITE_DAC /* these two might be forced upon us */
+ | THREAD_RESUME /* This seems to be force upon us too with 8.1. */
+ ) )
+ || pOpInfo->KernelHandle,
+ ("GrantedAccess=%#x - we allow %#x - we did not allow %#x\n",
+ fGrantedAccess, SUPDRV_NT_ALLOWED_THREAD_RIGHTS, fGrantedAccess & ~SUPDRV_NT_ALLOWED_THREAD_RIGHTS));
+ }
+}
+
+# undef SUPDRV_NT_ALLOWED_THREAD_RIGHTS
+
+/** @} */
+
+
+/**
+ * Creates a new process protection structure.
+ *
+ * @returns VBox status code.
+ * @param ppNtProtect Where to return the pointer to the structure
+ * on success.
+ * @param hPid The process ID of the process to protect.
+ * @param enmProcessKind The kind of process we're protecting.
+ * @param fLink Whether to link the structure into the tree.
+ */
+static int supdrvNtProtectCreate(PSUPDRVNTPROTECT *ppNtProtect, HANDLE hPid, SUPDRVNTPROTECTKIND enmProcessKind, bool fLink)
+{
+ AssertReturn(g_hNtProtectLock != NIL_RTSPINLOCK, VERR_WRONG_ORDER);
+
+ PSUPDRVNTPROTECT pNtProtect = (PSUPDRVNTPROTECT)RTMemAllocZ(sizeof(*pNtProtect));
+ if (!pNtProtect)
+ return VERR_NO_MEMORY;
+
+ pNtProtect->AvlCore.Key = hPid;
+ pNtProtect->u32Magic = SUPDRVNTPROTECT_MAGIC;
+ pNtProtect->cRefs = 1;
+ pNtProtect->enmProcessKind = enmProcessKind;
+ pNtProtect->hParentPid = NULL;
+ pNtProtect->hOpenTid = NULL;
+ pNtProtect->hCsrssPid = NULL;
+ pNtProtect->pCsrssProcess = NULL;
+
+ if (fLink)
+ {
+ RTSpinlockAcquire(g_hNtProtectLock);
+ bool fSuccess = RTAvlPVInsert(&g_NtProtectTree, &pNtProtect->AvlCore);
+ pNtProtect->fInTree = fSuccess;
+ RTSpinlockRelease(g_hNtProtectLock);
+
+ if (!fSuccess)
+ {
+ /* Duplicate entry, fail. */
+ pNtProtect->u32Magic = SUPDRVNTPROTECT_MAGIC_DEAD;
+ LogRel(("supdrvNtProtectCreate: Duplicate (%#x).\n", pNtProtect->AvlCore.Key));
+ RTMemFree(pNtProtect);
+ return VERR_DUPLICATE;
+ }
+ }
+
+ *ppNtProtect = pNtProtect;
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * Releases a reference to a NT protection structure.
+ *
+ * @param pNtProtect The NT protection structure.
+ */
+static void supdrvNtProtectRelease(PSUPDRVNTPROTECT pNtProtect)
+{
+ if (!pNtProtect)
+ return;
+ AssertReturnVoid(pNtProtect->u32Magic == SUPDRVNTPROTECT_MAGIC);
+
+ RTSpinlockAcquire(g_hNtProtectLock);
+ uint32_t cRefs = ASMAtomicDecU32(&pNtProtect->cRefs);
+ if (cRefs != 0)
+ RTSpinlockRelease(g_hNtProtectLock);
+ else
+ {
+ /*
+ * That was the last reference. Remove it from the tree, invalidate it
+ * and free the resources associated with it. Also, release any
+ * child/parent references related to this protection structure.
+ */
+ ASMAtomicWriteU32(&pNtProtect->u32Magic, SUPDRVNTPROTECT_MAGIC_DEAD);
+ if (pNtProtect->fInTree)
+ {
+ PSUPDRVNTPROTECT pRemoved = (PSUPDRVNTPROTECT)RTAvlPVRemove(&g_NtProtectTree, pNtProtect->AvlCore.Key);
+ Assert(pRemoved == pNtProtect); RT_NOREF_PV(pRemoved);
+ pNtProtect->fInTree = false;
+ }
+
+ PSUPDRVNTPROTECT pChild = NULL;
+ if (pNtProtect->enmProcessKind == kSupDrvNtProtectKind_StubParent)
+ {
+ pChild = pNtProtect->u.pChild;
+ if (pChild)
+ {
+ pNtProtect->u.pChild = NULL;
+ pChild->u.pParent = NULL;
+ pChild->enmProcessKind = kSupDrvNtProtectKind_VmProcessDead;
+ uint32_t cChildRefs = ASMAtomicDecU32(&pChild->cRefs);
+ if (!cChildRefs)
+ {
+ Assert(pChild->fInTree);
+ if (pChild->fInTree)
+ {
+ PSUPDRVNTPROTECT pRemovedChild = (PSUPDRVNTPROTECT)RTAvlPVRemove(&g_NtProtectTree, pChild->AvlCore.Key);
+ Assert(pRemovedChild == pChild); RT_NOREF_PV(pRemovedChild);
+ pChild->fInTree = false;
+ }
+ }
+ else
+ pChild = NULL;
+ }
+ }
+ else
+ AssertRelease(pNtProtect->enmProcessKind != kSupDrvNtProtectKind_VmProcessUnconfirmed);
+
+ RTSpinlockRelease(g_hNtProtectLock);
+
+ if (pNtProtect->pCsrssProcess)
+ {
+ ObDereferenceObject(pNtProtect->pCsrssProcess);
+ pNtProtect->pCsrssProcess = NULL;
+ }
+
+ RTMemFree(pNtProtect);
+ if (pChild)
+ RTMemFree(pChild);
+ }
+}
+
+
+/**
+ * Looks up a PID in the NT protect tree.
+ *
+ * @returns Pointer to a NT protection structure (with a referenced) on success,
+ * NULL if not found.
+ * @param hPid The process ID.
+ */
+static PSUPDRVNTPROTECT supdrvNtProtectLookup(HANDLE hPid)
+{
+ RTSpinlockAcquire(g_hNtProtectLock);
+ PSUPDRVNTPROTECT pFound = (PSUPDRVNTPROTECT)RTAvlPVGet(&g_NtProtectTree, hPid);
+ if (pFound)
+ ASMAtomicIncU32(&pFound->cRefs);
+ RTSpinlockRelease(g_hNtProtectLock);
+ return pFound;
+}
+
+
+/**
+ * Validates a few facts about the stub process when the VM process opens
+ * vboxdrv.
+ *
+ * This makes sure the stub process is still around and that it has neither
+ * debugger nor extra threads in it.
+ *
+ * @returns VBox status code.
+ * @param pNtProtect The unconfirmed VM process currently trying to
+ * open vboxdrv.
+ * @param pErrInfo Additional error information.
+ */
+static int supdrvNtProtectVerifyStubForVmProcess(PSUPDRVNTPROTECT pNtProtect, PRTERRINFO pErrInfo)
+{
+ /*
+ * Grab a reference to the parent stub process.
+ */
+ SUPDRVNTPROTECTKIND enmStub = kSupDrvNtProtectKind_Invalid;
+ PSUPDRVNTPROTECT pNtStub = NULL;
+ RTSpinlockAcquire(g_hNtProtectLock);
+ if (pNtProtect->enmProcessKind == kSupDrvNtProtectKind_VmProcessUnconfirmed)
+ {
+ pNtStub = pNtProtect->u.pParent; /* weak reference. */
+ if (pNtStub)
+ {
+ enmStub = pNtStub->enmProcessKind;
+ if (enmStub == kSupDrvNtProtectKind_StubParent)
+ {
+ uint32_t cRefs = ASMAtomicIncU32(&pNtStub->cRefs);
+ Assert(cRefs > 0 && cRefs < 1024); RT_NOREF_PV(cRefs);
+ }
+ else
+ pNtStub = NULL;
+ }
+ }
+ RTSpinlockRelease(g_hNtProtectLock);
+
+ /*
+ * We require the stub process to be present.
+ */
+ if (!pNtStub)
+ return RTErrInfoSetF(pErrInfo, VERR_SUP_VP_STUB_NOT_FOUND, "Missing stub process (enmStub=%d).", enmStub);
+
+ /*
+ * Open the parent process and thread so we can check for debuggers and unwanted threads.
+ */
+ int rc;
+ PEPROCESS pStubProcess;
+ NTSTATUS rcNt = PsLookupProcessByProcessId(pNtStub->AvlCore.Key, &pStubProcess);
+ if (NT_SUCCESS(rcNt))
+ {
+ HANDLE hStubProcess;
+ rcNt = ObOpenObjectByPointer(pStubProcess, OBJ_KERNEL_HANDLE, NULL /*PassedAccessState*/,
+ 0 /*DesiredAccess*/, *PsProcessType, KernelMode, &hStubProcess);
+ if (NT_SUCCESS(rcNt))
+ {
+ PETHREAD pStubThread;
+ rcNt = PsLookupThreadByThreadId(pNtStub->hOpenTid, &pStubThread);
+ if (NT_SUCCESS(rcNt))
+ {
+ HANDLE hStubThread;
+ rcNt = ObOpenObjectByPointer(pStubThread, OBJ_KERNEL_HANDLE, NULL /*PassedAccessState*/,
+ 0 /*DesiredAccess*/, *PsThreadType, KernelMode, &hStubThread);
+ if (NT_SUCCESS(rcNt))
+ {
+ /*
+ * Do some simple sanity checking.
+ */
+ rc = supHardNtVpDebugger(hStubProcess, pErrInfo);
+ if (RT_SUCCESS(rc))
+ rc = supHardNtVpThread(hStubProcess, hStubThread, pErrInfo);
+
+ /* Clean up. */
+ rcNt = NtClose(hStubThread); AssertMsg(NT_SUCCESS(rcNt), ("%#x\n", rcNt));
+ }
+ else
+ rc = RTErrInfoSetF(pErrInfo, VERR_SUP_VP_STUB_THREAD_OPEN_ERROR,
+ "Error opening stub thread %p (tid %p, pid %p): %#x",
+ pStubThread, pNtStub->hOpenTid, pNtStub->AvlCore.Key, rcNt);
+ }
+ else
+ rc = RTErrInfoSetF(pErrInfo, VERR_SUP_VP_STUB_THREAD_NOT_FOUND,
+ "Failed to locate thread %p in %p: %#x", pNtStub->hOpenTid, pNtStub->AvlCore.Key, rcNt);
+ rcNt = NtClose(hStubProcess); AssertMsg(NT_SUCCESS(rcNt), ("%#x\n", rcNt));
+ }
+ else
+ rc = RTErrInfoSetF(pErrInfo, VERR_SUP_VP_STUB_OPEN_ERROR,
+ "Error opening stub process %p (pid %p): %#x", pStubProcess, pNtStub->AvlCore.Key, rcNt);
+ ObDereferenceObject(pStubProcess);
+ }
+ else
+ rc = RTErrInfoSetF(pErrInfo, VERR_SUP_VP_STUB_NOT_FOUND,
+ "Failed to locate stub process %p: %#x", pNtStub->AvlCore.Key, rcNt);
+
+ supdrvNtProtectRelease(pNtStub);
+ return rc;
+}
+
+
+static const char *supdrvNtProtectHandleTypeIndexToName(ULONG idxType, char *pszName, size_t cbName)
+{
+ /*
+ * Query the object types.
+ */
+ uint32_t cbBuf = _8K;
+ uint8_t *pbBuf = (uint8_t *)RTMemAllocZ(_8K);
+ ULONG cbNeeded = cbBuf;
+ NTSTATUS rcNt = NtQueryObject(NULL, ObjectTypesInformation, pbBuf, cbBuf, &cbNeeded);
+ while (rcNt == STATUS_INFO_LENGTH_MISMATCH)
+ {
+ cbBuf = RT_ALIGN_32(cbNeeded + 256, _64K);
+ RTMemFree(pbBuf);
+ pbBuf = (uint8_t *)RTMemAllocZ(cbBuf);
+ if (pbBuf)
+ rcNt = NtQueryObject(NULL, ObjectTypesInformation, pbBuf, cbBuf, &cbNeeded);
+ else
+ break;
+ }
+ if (NT_SUCCESS(rcNt))
+ {
+ Assert(cbNeeded <= cbBuf);
+
+ POBJECT_TYPES_INFORMATION pObjTypes = (OBJECT_TYPES_INFORMATION *)pbBuf;
+ POBJECT_TYPE_INFORMATION pCurType = &pObjTypes->FirstType;
+ ULONG cLeft = pObjTypes->NumberOfTypes;
+ while (cLeft-- > 0 && (uintptr_t)&pCurType[1] - (uintptr_t)pbBuf < cbNeeded)
+ {
+ if (pCurType->TypeIndex == idxType)
+ {
+ PCRTUTF16 const pwszSrc = pCurType->TypeName.Buffer;
+ AssertBreak(pwszSrc);
+ size_t idxName = pCurType->TypeName.Length / sizeof(RTUTF16);
+ AssertBreak(idxName > 0);
+ AssertBreak(idxName < 128);
+ if (idxName >= cbName)
+ idxName = cbName - 1;
+ pszName[idxName] = '\0';
+ while (idxName-- > 0)
+ pszName[idxName] = (char )pwszSrc[idxName];
+ RTMemFree(pbBuf);
+ return pszName;
+ }
+
+ /* next */
+ pCurType = (POBJECT_TYPE_INFORMATION)( (uintptr_t)pCurType->TypeName.Buffer
+ + RT_ALIGN_32(pCurType->TypeName.MaximumLength, sizeof(uintptr_t)));
+ }
+ }
+
+ RTMemFree(pbBuf);
+ return "unknown";
+}
+
+
+/**
+ * Worker for supdrvNtProtectVerifyProcess that verifies the handles to a VM
+ * process and its thread.
+ *
+ * @returns VBox status code.
+ * @param pNtProtect The NT protect structure for getting information
+ * about special processes.
+ * @param pErrInfo Where to return additional error details.
+ */
+static int supdrvNtProtectRestrictHandlesToProcessAndThread(PSUPDRVNTPROTECT pNtProtect, PRTERRINFO pErrInfo)
+{
+ /*
+ * What to protect.
+ */
+ PEPROCESS pProtectedProcess = PsGetCurrentProcess();
+ HANDLE hProtectedPid = PsGetProcessId(pProtectedProcess);
+ PETHREAD pProtectedThread = PsGetCurrentThread();
+ AssertReturn(pNtProtect->AvlCore.Key == hProtectedPid, VERR_INTERNAL_ERROR_5);
+
+ /*
+ * Take a snapshot of all the handles in the system.
+ * Note! The 32 bytes on the size of to counteract the allocation header
+ * that rtR0MemAllocEx slaps on everything.
+ */
+ uint32_t cbBuf = _256K - 32;
+ uint8_t *pbBuf = (uint8_t *)RTMemAlloc(cbBuf);
+ ULONG cbNeeded = cbBuf;
+ NTSTATUS rcNt = NtQuerySystemInformation(SystemExtendedHandleInformation, pbBuf, cbBuf, &cbNeeded);
+ if (!NT_SUCCESS(rcNt))
+ {
+ while ( rcNt == STATUS_INFO_LENGTH_MISMATCH
+ && cbNeeded > cbBuf
+ && cbBuf <= 32U*_1M)
+ {
+ cbBuf = RT_ALIGN_32(cbNeeded + _4K, _64K) - 32;
+ RTMemFree(pbBuf);
+ pbBuf = (uint8_t *)RTMemAlloc(cbBuf);
+ if (!pbBuf)
+ return RTErrInfoSetF(pErrInfo, VERR_NO_MEMORY, "Error allocating %zu bytes for querying handles.", cbBuf);
+ rcNt = NtQuerySystemInformation(SystemExtendedHandleInformation, pbBuf, cbBuf, &cbNeeded);
+ }
+ if (!NT_SUCCESS(rcNt))
+ {
+ RTMemFree(pbBuf);
+ return RTErrInfoSetF(pErrInfo, RTErrConvertFromNtStatus(rcNt),
+ "NtQuerySystemInformation/SystemExtendedHandleInformation failed: %#x\n", rcNt);
+ }
+ }
+
+ /*
+ * Walk the information and look for handles to the two objects we're protecting.
+ */
+ int rc = VINF_SUCCESS;
+# ifdef VBOX_WITHOUT_DEBUGGER_CHECKS
+ HANDLE idLastDebugger = (HANDLE)~(uintptr_t)0;
+# endif
+
+ uint32_t cCsrssProcessHandles = 0;
+ uint32_t cSystemProcessHandles = 0;
+ uint32_t cEvilProcessHandles = 0;
+ uint32_t cBenignProcessHandles = 0;
+
+ uint32_t cCsrssThreadHandles = 0;
+ uint32_t cEvilThreadHandles = 0;
+ uint32_t cBenignThreadHandles = 0;
+
+ uint32_t cEvilInheritableHandles = 0;
+ uint32_t cBenignInheritableHandles = 0;
+ char szTmpName[32];
+
+ SYSTEM_HANDLE_INFORMATION_EX const *pInfo = (SYSTEM_HANDLE_INFORMATION_EX const *)pbBuf;
+ ULONG_PTR i = pInfo->NumberOfHandles;
+ AssertRelease(RT_UOFFSETOF_DYN(SYSTEM_HANDLE_INFORMATION_EX, Handles[i]) == cbNeeded);
+ while (i-- > 0)
+ {
+ const char *pszType;
+ SYSTEM_HANDLE_ENTRY_INFO_EX const *pHandleInfo = &pInfo->Handles[i];
+ if (pHandleInfo->Object == pProtectedProcess)
+ {
+ /* Handles within the protected process are fine. */
+ if ( !(pHandleInfo->GrantedAccess & SUPDRV_NT_EVIL_PROCESS_RIGHTS)
+ || pHandleInfo->UniqueProcessId == hProtectedPid)
+ {
+ cBenignProcessHandles++;
+ continue;
+ }
+
+ /* CSRSS is allowed to have one evil process handle.
+ See the special cases in the hook code. */
+ if ( cCsrssProcessHandles < 1
+ && pHandleInfo->UniqueProcessId == pNtProtect->hCsrssPid)
+ {
+ cCsrssProcessHandles++;
+ continue;
+ }
+
+ /* The system process is allowed having two open process handle in
+ Windows 8.1 and later, and one in earlier. This is probably a
+ little overly paranoid as I think we can safely trust the
+ system process... */
+ if ( cSystemProcessHandles < (g_uNtVerCombined >= SUP_MAKE_NT_VER_SIMPLE(6, 3) ? UINT32_C(2) : UINT32_C(1))
+ && pHandleInfo->UniqueProcessId == PsGetProcessId(PsInitialSystemProcess))
+ {
+ cSystemProcessHandles++;
+ continue;
+ }
+
+ cEvilProcessHandles++;
+ pszType = "process";
+ }
+ else if (pHandleInfo->Object == pProtectedThread)
+ {
+ /* Handles within the protected process is fine. */
+ if ( !(pHandleInfo->GrantedAccess & SUPDRV_NT_EVIL_THREAD_RIGHTS)
+ || pHandleInfo->UniqueProcessId == hProtectedPid)
+ {
+ cBenignThreadHandles++;
+ continue;
+ }
+
+ /* CSRSS is allowed to have one evil handle to the primary thread
+ for LPC purposes. See the hook for special case. */
+ if ( cCsrssThreadHandles < 1
+ && pHandleInfo->UniqueProcessId == pNtProtect->hCsrssPid)
+ {
+ cCsrssThreadHandles++;
+ continue;
+ }
+
+ cEvilThreadHandles++;
+ pszType = "thread";
+ }
+ else if ( (pHandleInfo->HandleAttributes & OBJ_INHERIT)
+ && pHandleInfo->UniqueProcessId == hProtectedPid)
+ {
+ /* No handles should be marked inheritable, except files and two events.
+ Handles to NT 'directory' objects are especially evil, because of
+ KnownDlls faking. See bugref{10294} for details.
+
+ Correlating the ObjectTypeIndex to a type is complicated, so instead
+ we try referecing the handle and check the type that way. So, only
+ file and events objects are allowed to be marked inheritable at the
+ moment. Add more in whitelist fashion if needed. */
+ void *pvObject = NULL;
+ rcNt = ObReferenceObjectByHandle(pHandleInfo->HandleValue, 0, *IoFileObjectType, KernelMode, &pvObject, NULL);
+ if (rcNt == STATUS_OBJECT_TYPE_MISMATCH)
+ rcNt = ObReferenceObjectByHandle(pHandleInfo->HandleValue, 0, *ExEventObjectType, KernelMode, &pvObject, NULL);
+ if (NT_SUCCESS(rcNt))
+ {
+ ObDereferenceObject(pvObject);
+ cBenignInheritableHandles++;
+ continue;
+ }
+
+ if (rcNt != STATUS_OBJECT_TYPE_MISMATCH)
+ {
+ cBenignInheritableHandles++;
+ continue;
+ }
+
+ cEvilInheritableHandles++;
+ pszType = supdrvNtProtectHandleTypeIndexToName(pHandleInfo->ObjectTypeIndex, szTmpName, sizeof(szTmpName));
+ }
+ else
+ continue;
+
+# ifdef VBOX_WITHOUT_DEBUGGER_CHECKS
+ /* Ignore whitelisted debuggers. */
+ if (pHandleInfo->UniqueProcessId == idLastDebugger)
+ continue;
+ PEPROCESS pDbgProc;
+ rcNt = PsLookupProcessByProcessId(pHandleInfo->UniqueProcessId, &pDbgProc);
+ if (NT_SUCCESS(rcNt))
+ {
+ bool fIsDebugger = supdrvNtProtectIsWhitelistedDebugger(pDbgProc);
+ ObDereferenceObject(pDbgProc);
+ if (fIsDebugger)
+ {
+ idLastDebugger = pHandleInfo->UniqueProcessId;
+ continue;
+ }
+ }
+# endif
+
+ /* Found evil handle. Currently ignoring on pre-Vista. */
+# ifndef VBOX_WITH_VISTA_NO_SP
+ if ( g_uNtVerCombined >= SUP_NT_VER_VISTA
+# else
+ if ( g_uNtVerCombined >= SUP_MAKE_NT_VER_COMBINED(6, 0, 6001, 0, 0)
+# endif
+ || g_pfnObRegisterCallbacks)
+ {
+ LogRel(("vboxdrv: Found evil handle to budding VM process: pid=%p h=%p acc=%#x attr=%#x type=%s (%u)\n",
+ pHandleInfo->UniqueProcessId, pHandleInfo->HandleValue,
+ pHandleInfo->GrantedAccess, pHandleInfo->HandleAttributes, pszType, pHandleInfo->ObjectTypeIndex));
+ rc = RTErrInfoAddF(pErrInfo, VERR_SUPDRV_HARDENING_EVIL_HANDLE,
+ *pErrInfo->pszMsg
+ ? "\nFound evil handle to budding VM process: pid=%p h=%p acc=%#x attr=%#x type=%s (%u)"
+ : "Found evil handle to budding VM process: pid=%p h=%p acc=%#x attr=%#x type=%s (%u)",
+ pHandleInfo->UniqueProcessId, pHandleInfo->HandleValue,
+ pHandleInfo->GrantedAccess, pHandleInfo->HandleAttributes, pszType, pHandleInfo->ObjectTypeIndex);
+
+ /* Try add the process name. */
+ PEPROCESS pOffendingProcess;
+ rcNt = PsLookupProcessByProcessId(pHandleInfo->UniqueProcessId, &pOffendingProcess);
+ if (NT_SUCCESS(rcNt))
+ {
+ const char *pszName = (const char *)PsGetProcessImageFileName(pOffendingProcess);
+ if (pszName && *pszName)
+ rc = RTErrInfoAddF(pErrInfo, rc, " [%s]", pszName);
+
+ ObDereferenceObject(pOffendingProcess);
+ }
+ }
+ }
+
+ RTMemFree(pbBuf);
+ return rc;
+}
+
+
+/**
+ * Checks if the current process checks out as a VM process stub.
+ *
+ * @returns VBox status code.
+ * @param pNtProtect The NT protect structure. This is upgraded to a
+ * final protection kind (state) on success.
+ */
+static int supdrvNtProtectVerifyProcess(PSUPDRVNTPROTECT pNtProtect)
+{
+ AssertReturn(PsGetProcessId(PsGetCurrentProcess()) == pNtProtect->AvlCore.Key, VERR_INTERNAL_ERROR_3);
+
+ /*
+ * Do the verification. The handle restriction checks are only preformed
+ * on VM processes.
+ */
+ int rc = VINF_SUCCESS;
+ PSUPDRVNTERRORINFO pErrorInfo = (PSUPDRVNTERRORINFO)RTMemAllocZ(sizeof(*pErrorInfo));
+ if (RT_SUCCESS(rc))
+ {
+ pErrorInfo->hProcessId = PsGetCurrentProcessId();
+ pErrorInfo->hThreadId = PsGetCurrentThreadId();
+ RTERRINFO ErrInfo;
+ RTErrInfoInit(&ErrInfo, pErrorInfo->szErrorInfo, sizeof(pErrorInfo->szErrorInfo));
+
+ if (pNtProtect->enmProcessKind >= kSupDrvNtProtectKind_VmProcessUnconfirmed)
+ rc = supdrvNtProtectRestrictHandlesToProcessAndThread(pNtProtect, &ErrInfo);
+ if (RT_SUCCESS(rc))
+ {
+ rc = supHardenedWinVerifyProcess(NtCurrentProcess(), NtCurrentThread(), SUPHARDNTVPKIND_VERIFY_ONLY, 0 /*fFlags*/,
+ NULL /*pcFixes*/, &ErrInfo);
+ if (RT_SUCCESS(rc) && pNtProtect->enmProcessKind >= kSupDrvNtProtectKind_VmProcessUnconfirmed)
+ rc = supdrvNtProtectVerifyStubForVmProcess(pNtProtect, &ErrInfo);
+ }
+ }
+ else
+ rc = VERR_NO_MEMORY;
+
+ /*
+ * Upgrade and return.
+ */
+ HANDLE hOpenTid = PsGetCurrentThreadId();
+ RTSpinlockAcquire(g_hNtProtectLock);
+
+ /* Stub process verficiation is pretty much straight forward. */
+ if (pNtProtect->enmProcessKind == kSupDrvNtProtectKind_StubUnverified)
+ {
+ pNtProtect->enmProcessKind = RT_SUCCESS(rc) ? kSupDrvNtProtectKind_StubSpawning : kSupDrvNtProtectKind_StubDead;
+ pNtProtect->hOpenTid = hOpenTid;
+ }
+ /* The VM process verification is a little bit more complicated
+ because we need to drop the parent process reference as well. */
+ else if (pNtProtect->enmProcessKind == kSupDrvNtProtectKind_VmProcessUnconfirmed)
+ {
+ AssertRelease(pNtProtect->cRefs >= 2); /* Parent + Caller */
+ PSUPDRVNTPROTECT pParent = pNtProtect->u.pParent;
+ AssertRelease(pParent);
+ AssertRelease(pParent->u.pParent == pNtProtect);
+ AssertRelease(pParent->enmProcessKind == kSupDrvNtProtectKind_StubParent);
+ pParent->u.pParent = NULL;
+
+ pNtProtect->u.pParent = NULL;
+ ASMAtomicDecU32(&pNtProtect->cRefs);
+
+ if (RT_SUCCESS(rc))
+ {
+ pNtProtect->enmProcessKind = kSupDrvNtProtectKind_VmProcessConfirmed;
+ pNtProtect->hOpenTid = hOpenTid;
+ }
+ else
+ pNtProtect->enmProcessKind = kSupDrvNtProtectKind_VmProcessDead;
+ }
+
+ /* Since the stub and VM processes are only supposed to have one thread,
+ we're not supposed to be subject to any races from within the processes.
+
+ There is a race between VM process verification and the stub process
+ exiting, though. We require the stub process to be alive until the new
+ VM process has made it thru the validation. So, when the stub
+ terminates the notification handler will change the state of both stub
+ and VM process to dead.
+
+ Also, I'm not entirely certain where the process
+ termination notification is triggered from, so that can theorically
+ create a race in both cases. */
+ else
+ {
+ AssertReleaseMsg( pNtProtect->enmProcessKind == kSupDrvNtProtectKind_StubDead
+ || pNtProtect->enmProcessKind == kSupDrvNtProtectKind_VmProcessDead,
+ ("enmProcessKind=%d rc=%Rrc\n", pNtProtect->enmProcessKind, rc));
+ if (RT_SUCCESS(rc))
+ rc = VERR_INVALID_STATE; /* There should be no races here. */
+ }
+
+ RTSpinlockRelease(g_hNtProtectLock);
+
+ /*
+ * Free error info on success, keep it on failure.
+ */
+ if (RT_SUCCESS(rc))
+ RTMemFree(pErrorInfo);
+ else if (pErrorInfo)
+ {
+ pErrorInfo->cchErrorInfo = (uint32_t)strlen(pErrorInfo->szErrorInfo);
+ if (!pErrorInfo->cchErrorInfo)
+ pErrorInfo->cchErrorInfo = (uint32_t)RTStrPrintf(pErrorInfo->szErrorInfo, sizeof(pErrorInfo->szErrorInfo),
+ "supdrvNtProtectVerifyProcess: rc=%d", rc);
+ RTLogWriteDebugger(pErrorInfo->szErrorInfo, pErrorInfo->cchErrorInfo);
+
+ int rc2 = RTSemMutexRequest(g_hErrorInfoLock, RT_INDEFINITE_WAIT);
+ if (RT_SUCCESS(rc2))
+ {
+ pErrorInfo->uCreatedMsTs = RTTimeMilliTS();
+
+ /* Free old entries. */
+ PSUPDRVNTERRORINFO pCur;
+ while ( (pCur = RTListGetFirst(&g_ErrorInfoHead, SUPDRVNTERRORINFO, ListEntry)) != NULL
+ && (int64_t)(pErrorInfo->uCreatedMsTs - pCur->uCreatedMsTs) > 60000 /*60sec*/)
+ {
+ RTListNodeRemove(&pCur->ListEntry);
+ RTMemFree(pCur);
+ }
+
+ /* Insert our new entry. */
+ RTListAppend(&g_ErrorInfoHead, &pErrorInfo->ListEntry);
+
+ RTSemMutexRelease(g_hErrorInfoLock);
+ }
+ else
+ RTMemFree(pErrorInfo);
+ }
+
+ return rc;
+}
+
+
+# ifndef VBOX_WITHOUT_DEBUGGER_CHECKS
+
+/**
+ * Checks if the current process is being debugged.
+ * @return @c true if debugged, @c false if not.
+ */
+static bool supdrvNtIsDebuggerAttached(void)
+{
+ return PsIsProcessBeingDebugged(PsGetCurrentProcess()) != FALSE;
+}
+
+# endif /* !VBOX_WITHOUT_DEBUGGER_CHECKS */
+
+
+/**
+ * Terminates the hardening bits.
+ */
+static void supdrvNtProtectTerm(void)
+{
+ /*
+ * Stop intercepting process and thread handle creation calls.
+ */
+ if (g_pvObCallbacksCookie)
+ {
+ g_pfnObUnRegisterCallbacks(g_pvObCallbacksCookie);
+ g_pvObCallbacksCookie = NULL;
+ }
+
+ /*
+ * Stop intercepting process creation and termination notifications.
+ */
+ NTSTATUS rcNt;
+ if (g_pfnPsSetCreateProcessNotifyRoutineEx)
+ rcNt = g_pfnPsSetCreateProcessNotifyRoutineEx(supdrvNtProtectCallback_ProcessCreateNotifyEx, TRUE /*fRemove*/);
+ else
+ rcNt = PsSetCreateProcessNotifyRoutine(supdrvNtProtectCallback_ProcessCreateNotify, TRUE /*fRemove*/);
+ AssertMsg(NT_SUCCESS(rcNt), ("rcNt=%#x\n", rcNt));
+
+ Assert(g_NtProtectTree == NULL);
+
+ /*
+ * Clean up globals.
+ */
+ RTSpinlockDestroy(g_hNtProtectLock);
+ g_NtProtectTree = NIL_RTSPINLOCK;
+
+ RTSemMutexDestroy(g_hErrorInfoLock);
+ g_hErrorInfoLock = NIL_RTSEMMUTEX;
+
+ PSUPDRVNTERRORINFO pCur;
+ while ((pCur = RTListGetFirst(&g_ErrorInfoHead, SUPDRVNTERRORINFO, ListEntry)) != NULL)
+ {
+ RTListNodeRemove(&pCur->ListEntry);
+ RTMemFree(pCur);
+ }
+
+ supHardenedWinTermImageVerifier();
+}
+
+# ifdef RT_ARCH_X86
+DECLASM(void) supdrvNtQueryVirtualMemory_0xAF(void);
+DECLASM(void) supdrvNtQueryVirtualMemory_0xB0(void);
+DECLASM(void) supdrvNtQueryVirtualMemory_0xB1(void);
+DECLASM(void) supdrvNtQueryVirtualMemory_0xB2(void);
+DECLASM(void) supdrvNtQueryVirtualMemory_0xB3(void);
+DECLASM(void) supdrvNtQueryVirtualMemory_0xB4(void);
+DECLASM(void) supdrvNtQueryVirtualMemory_0xB5(void);
+DECLASM(void) supdrvNtQueryVirtualMemory_0xB6(void);
+DECLASM(void) supdrvNtQueryVirtualMemory_0xB7(void);
+DECLASM(void) supdrvNtQueryVirtualMemory_0xB8(void);
+DECLASM(void) supdrvNtQueryVirtualMemory_0xB9(void);
+DECLASM(void) supdrvNtQueryVirtualMemory_0xBA(void);
+DECLASM(void) supdrvNtQueryVirtualMemory_0xBB(void);
+DECLASM(void) supdrvNtQueryVirtualMemory_0xBC(void);
+DECLASM(void) supdrvNtQueryVirtualMemory_0xBD(void);
+DECLASM(void) supdrvNtQueryVirtualMemory_0xBE(void);
+# elif defined(RT_ARCH_AMD64)
+DECLASM(void) supdrvNtQueryVirtualMemory_0x1F(void);
+DECLASM(void) supdrvNtQueryVirtualMemory_0x20(void);
+DECLASM(void) supdrvNtQueryVirtualMemory_0x21(void);
+DECLASM(void) supdrvNtQueryVirtualMemory_0x22(void);
+DECLASM(void) supdrvNtQueryVirtualMemory_0x23(void);
+extern "C" NTSYSAPI NTSTATUS NTAPI ZwRequestWaitReplyPort(HANDLE, PVOID, PVOID);
+# endif
+
+
+/**
+ * Initalizes the hardening bits.
+ *
+ * @returns NT status code.
+ */
+static NTSTATUS supdrvNtProtectInit(void)
+{
+ /*
+ * Initialize the globals.
+ */
+
+ /* The NT version. */
+ ULONG uMajor, uMinor, uBuild;
+ PsGetVersion(&uMajor, &uMinor, &uBuild, NULL);
+ g_uNtVerCombined = SUP_MAKE_NT_VER_COMBINED(uMajor, uMinor, uBuild, 0, 0);
+
+ /* Resolve methods we want but isn't available everywhere. */
+ UNICODE_STRING RoutineName;
+
+ RtlInitUnicodeString(&RoutineName, L"ObGetObjectType");
+ g_pfnObGetObjectType = (PFNOBGETOBJECTTYPE)MmGetSystemRoutineAddress(&RoutineName);
+
+ RtlInitUnicodeString(&RoutineName, L"ObRegisterCallbacks");
+ g_pfnObRegisterCallbacks = (PFNOBREGISTERCALLBACKS)MmGetSystemRoutineAddress(&RoutineName);
+
+ RtlInitUnicodeString(&RoutineName, L"ObUnRegisterCallbacks");
+ g_pfnObUnRegisterCallbacks = (PFNOBUNREGISTERCALLBACKS)MmGetSystemRoutineAddress(&RoutineName);
+
+ RtlInitUnicodeString(&RoutineName, L"PsSetCreateProcessNotifyRoutineEx");
+ g_pfnPsSetCreateProcessNotifyRoutineEx = (PFNPSSETCREATEPROCESSNOTIFYROUTINEEX)MmGetSystemRoutineAddress(&RoutineName);
+
+ RtlInitUnicodeString(&RoutineName, L"PsReferenceProcessFilePointer");
+ g_pfnPsReferenceProcessFilePointer = (PFNPSREFERENCEPROCESSFILEPOINTER)MmGetSystemRoutineAddress(&RoutineName);
+
+ RtlInitUnicodeString(&RoutineName, L"PsIsProtectedProcessLight");
+ g_pfnPsIsProtectedProcessLight = (PFNPSISPROTECTEDPROCESSLIGHT)MmGetSystemRoutineAddress(&RoutineName);
+
+ RtlInitUnicodeString(&RoutineName, L"ZwAlpcCreatePort");
+ g_pfnZwAlpcCreatePort = (PFNZWALPCCREATEPORT)MmGetSystemRoutineAddress(&RoutineName);
+
+ RtlInitUnicodeString(&RoutineName, L"ZwQueryVirtualMemory"); /* Yes, using Zw version here. */
+ g_pfnNtQueryVirtualMemory = (PFNNTQUERYVIRTUALMEMORY)MmGetSystemRoutineAddress(&RoutineName);
+ if (!g_pfnNtQueryVirtualMemory && g_uNtVerCombined < SUP_NT_VER_VISTA)
+ {
+ /* XP & W2K3 doesn't have this function exported, so we've cooked up a
+ few alternative in the assembly helper file that uses the code in
+ ZwReadFile with a different eax value. We figure the syscall number
+ by inspecting ZwQueryVolumeInformationFile as it's the next number. */
+# ifdef RT_ARCH_X86
+ uint8_t const *pbCode = (uint8_t const *)(uintptr_t)ZwQueryVolumeInformationFile;
+ if (*pbCode == 0xb8) /* mov eax, dword */
+ switch (*(uint32_t const *)&pbCode[1])
+ {
+ case 0xb0: g_pfnNtQueryVirtualMemory = (PFNNTQUERYVIRTUALMEMORY)supdrvNtQueryVirtualMemory_0xAF; break; /* just in case */
+ case 0xb1: g_pfnNtQueryVirtualMemory = (PFNNTQUERYVIRTUALMEMORY)supdrvNtQueryVirtualMemory_0xB0; break; /* just in case */
+ case 0xb2: g_pfnNtQueryVirtualMemory = (PFNNTQUERYVIRTUALMEMORY)supdrvNtQueryVirtualMemory_0xB1; break; /* just in case */
+ case 0xb3: g_pfnNtQueryVirtualMemory = (PFNNTQUERYVIRTUALMEMORY)supdrvNtQueryVirtualMemory_0xB2; break; /* XP SP3 */
+ case 0xb4: g_pfnNtQueryVirtualMemory = (PFNNTQUERYVIRTUALMEMORY)supdrvNtQueryVirtualMemory_0xB2; break; /* just in case */
+ case 0xb5: g_pfnNtQueryVirtualMemory = (PFNNTQUERYVIRTUALMEMORY)supdrvNtQueryVirtualMemory_0xB3; break; /* just in case */
+ case 0xb6: g_pfnNtQueryVirtualMemory = (PFNNTQUERYVIRTUALMEMORY)supdrvNtQueryVirtualMemory_0xB4; break; /* just in case */
+ case 0xb7: g_pfnNtQueryVirtualMemory = (PFNNTQUERYVIRTUALMEMORY)supdrvNtQueryVirtualMemory_0xB5; break; /* just in case */
+ case 0xb8: g_pfnNtQueryVirtualMemory = (PFNNTQUERYVIRTUALMEMORY)supdrvNtQueryVirtualMemory_0xB6; break; /* just in case */
+ case 0xb9: g_pfnNtQueryVirtualMemory = (PFNNTQUERYVIRTUALMEMORY)supdrvNtQueryVirtualMemory_0xB7; break; /* just in case */
+ case 0xba: g_pfnNtQueryVirtualMemory = (PFNNTQUERYVIRTUALMEMORY)supdrvNtQueryVirtualMemory_0xB8; break; /* just in case */
+ case 0xbb: g_pfnNtQueryVirtualMemory = (PFNNTQUERYVIRTUALMEMORY)supdrvNtQueryVirtualMemory_0xBA; break; /* W2K3 R2 SP2 */
+ case 0xbc: g_pfnNtQueryVirtualMemory = (PFNNTQUERYVIRTUALMEMORY)supdrvNtQueryVirtualMemory_0xBB; break; /* just in case */
+ case 0xbd: g_pfnNtQueryVirtualMemory = (PFNNTQUERYVIRTUALMEMORY)supdrvNtQueryVirtualMemory_0xBC; break; /* just in case */
+ case 0xbe: g_pfnNtQueryVirtualMemory = (PFNNTQUERYVIRTUALMEMORY)supdrvNtQueryVirtualMemory_0xBD; break; /* just in case */
+ case 0xbf: g_pfnNtQueryVirtualMemory = (PFNNTQUERYVIRTUALMEMORY)supdrvNtQueryVirtualMemory_0xBE; break; /* just in case */
+ }
+# elif defined(RT_ARCH_AMD64)
+ uint8_t const *pbCode = (uint8_t const *)(uintptr_t)ZwRequestWaitReplyPort;
+ if ( pbCode[ 0] == 0x48 /* mov rax, rsp */
+ && pbCode[ 1] == 0x8b
+ && pbCode[ 2] == 0xc4
+ && pbCode[ 3] == 0xfa /* cli */
+ && pbCode[ 4] == 0x48 /* sub rsp, 10h */
+ && pbCode[ 5] == 0x83
+ && pbCode[ 6] == 0xec
+ && pbCode[ 7] == 0x10
+ && pbCode[ 8] == 0x50 /* push rax */
+ && pbCode[ 9] == 0x9c /* pushfq */
+ && pbCode[10] == 0x6a /* push 10 */
+ && pbCode[11] == 0x10
+ && pbCode[12] == 0x48 /* lea rax, [nt!KiServiceLinkage] */
+ && pbCode[13] == 0x8d
+ && pbCode[14] == 0x05
+ && pbCode[19] == 0x50 /* push rax */
+ && pbCode[20] == 0xb8 /* mov eax,1fh <- the syscall no. */
+ /*&& pbCode[21] == 0x1f*/
+ && pbCode[22] == 0x00
+ && pbCode[23] == 0x00
+ && pbCode[24] == 0x00
+ && pbCode[25] == 0xe9 /* jmp KiServiceInternal */
+ )
+ {
+ uint8_t const *pbKiServiceInternal = &pbCode[30] + *(int32_t const *)&pbCode[26];
+ uint8_t const *pbKiServiceLinkage = &pbCode[19] + *(int32_t const *)&pbCode[15];
+ if (*pbKiServiceLinkage == 0xc3)
+ {
+ g_pfnKiServiceInternal = (PFNRT)pbKiServiceInternal;
+ g_pfnKiServiceLinkage = (PFNRT)pbKiServiceLinkage;
+ switch (pbCode[21])
+ {
+ case 0x1e: g_pfnNtQueryVirtualMemory = (PFNNTQUERYVIRTUALMEMORY)supdrvNtQueryVirtualMemory_0x1F; break;
+ case 0x1f: g_pfnNtQueryVirtualMemory = (PFNNTQUERYVIRTUALMEMORY)supdrvNtQueryVirtualMemory_0x20; break;
+ case 0x20: g_pfnNtQueryVirtualMemory = (PFNNTQUERYVIRTUALMEMORY)supdrvNtQueryVirtualMemory_0x21; break;
+ case 0x21: g_pfnNtQueryVirtualMemory = (PFNNTQUERYVIRTUALMEMORY)supdrvNtQueryVirtualMemory_0x22; break;
+ case 0x22: g_pfnNtQueryVirtualMemory = (PFNNTQUERYVIRTUALMEMORY)supdrvNtQueryVirtualMemory_0x23; break;
+ }
+ }
+ }
+# endif
+ }
+ if (!g_pfnNtQueryVirtualMemory)
+ {
+ LogRel(("vboxdrv: Cannot locate ZwQueryVirtualMemory in ntoskrnl, nor were we able to cook up a replacement.\n"));
+ return STATUS_PROCEDURE_NOT_FOUND;
+ }
+
+# ifdef VBOX_STRICT
+ if ( g_uNtVerCombined >= SUP_NT_VER_W70
+ && ( g_pfnObGetObjectType == NULL
+ || g_pfnZwAlpcCreatePort == NULL) )
+ {
+ LogRel(("vboxdrv: g_pfnObGetObjectType=%p g_pfnZwAlpcCreatePort=%p.\n", g_pfnObGetObjectType, g_pfnZwAlpcCreatePort));
+ return STATUS_PROCEDURE_NOT_FOUND;
+ }
+# endif
+
+ /* LPC object type. */
+ g_pAlpcPortObjectType1 = *LpcPortObjectType;
+
+ /* The spinlock protecting our structures. */
+ int rc = RTSpinlockCreate(&g_hNtProtectLock, RTSPINLOCK_FLAGS_INTERRUPT_UNSAFE, "NtProtectLock");
+ if (RT_FAILURE(rc))
+ return VBoxDrvNtErr2NtStatus(rc);
+ g_NtProtectTree = NULL;
+
+ NTSTATUS rcNt;
+
+ /* The mutex protecting the error information. */
+ RTListInit(&g_ErrorInfoHead);
+ rc = RTSemMutexCreate(&g_hErrorInfoLock);
+ if (RT_SUCCESS(rc))
+ {
+ /* Image stuff + certificates. */
+ rc = supHardenedWinInitImageVerifier(NULL);
+ if (RT_SUCCESS(rc))
+ {
+ /*
+ * Intercept process creation and termination.
+ */
+ if (g_pfnPsSetCreateProcessNotifyRoutineEx)
+ rcNt = g_pfnPsSetCreateProcessNotifyRoutineEx(supdrvNtProtectCallback_ProcessCreateNotifyEx, FALSE /*fRemove*/);
+ else
+ rcNt = PsSetCreateProcessNotifyRoutine(supdrvNtProtectCallback_ProcessCreateNotify, FALSE /*fRemove*/);
+ if (NT_SUCCESS(rcNt))
+ {
+ /*
+ * Intercept process and thread handle creation calls.
+ * The preferred method is only available on Vista SP1+.
+ */
+ if (g_pfnObRegisterCallbacks && g_pfnObUnRegisterCallbacks)
+ {
+ static OB_OPERATION_REGISTRATION s_aObOperations[] =
+ {
+ {
+ 0, /* PsProcessType - imported, need runtime init, better do it explicitly. */
+ OB_OPERATION_HANDLE_CREATE | OB_OPERATION_HANDLE_DUPLICATE,
+ supdrvNtProtectCallback_ProcessHandlePre,
+ supdrvNtProtectCallback_ProcessHandlePost,
+ },
+ {
+ 0, /* PsThreadType - imported, need runtime init, better do it explicitly. */
+ OB_OPERATION_HANDLE_CREATE | OB_OPERATION_HANDLE_DUPLICATE,
+ supdrvNtProtectCallback_ThreadHandlePre,
+ supdrvNtProtectCallback_ThreadHandlePost,
+ },
+ };
+ s_aObOperations[0].ObjectType = PsProcessType;
+ s_aObOperations[1].ObjectType = PsThreadType;
+
+ static OB_CALLBACK_REGISTRATION s_ObCallbackReg =
+ {
+ /* .Version = */ OB_FLT_REGISTRATION_VERSION,
+ /* .OperationRegistrationCount = */ RT_ELEMENTS(s_aObOperations),
+ /* .Altitude.Length = */ { 0,
+ /* .Altitude.MaximumLength = */ 0,
+ /* .Altitude.Buffer = */ NULL },
+ /* .RegistrationContext = */ NULL,
+ /* .OperationRegistration = */ &s_aObOperations[0]
+ };
+ static WCHAR const *s_apwszAltitudes[] = /** @todo get a valid number */
+ {
+ L"48596.98940", L"46935.19485", L"49739.39704", L"40334.74976",
+ L"66667.98940", L"69888.19485", L"69889.39704", L"60364.74976",
+ L"85780.98940", L"88978.19485", L"89939.39704", L"80320.74976",
+ L"329879.98940", L"326787.19485", L"328915.39704", L"320314.74976",
+ };
+
+ rcNt = STATUS_FLT_INSTANCE_ALTITUDE_COLLISION;
+ for (uint32_t i = 0; i < RT_ELEMENTS(s_apwszAltitudes) && rcNt == STATUS_FLT_INSTANCE_ALTITUDE_COLLISION; i++)
+ {
+ s_ObCallbackReg.Altitude.Buffer = (WCHAR *)s_apwszAltitudes[i];
+ s_ObCallbackReg.Altitude.Length = (uint16_t)RTUtf16Len(s_apwszAltitudes[i]) * sizeof(WCHAR);
+ s_ObCallbackReg.Altitude.MaximumLength = s_ObCallbackReg.Altitude.Length + sizeof(WCHAR);
+
+ rcNt = g_pfnObRegisterCallbacks(&s_ObCallbackReg, &g_pvObCallbacksCookie);
+ if (NT_SUCCESS(rcNt))
+ {
+ /*
+ * Happy ending.
+ */
+ return STATUS_SUCCESS;
+ }
+ }
+ LogRel(("vboxdrv: ObRegisterCallbacks failed with rcNt=%#x\n", rcNt));
+ g_pvObCallbacksCookie = NULL;
+ }
+ else
+ {
+ /*
+ * For the time being, we do not implement extra process
+ * protection on pre-Vista-SP1 systems as they are lacking
+ * necessary KPIs. XP is end of life, we do not wish to
+ * spend more time on it, so we don't put up a fuss there.
+ * Vista users without SP1 can install SP1 (or later), darn it,
+ * so refuse to load.
+ */
+ /** @todo Hack up an XP solution - will require hooking kernel APIs or doing bad
+ * stuff to a couple of object types. */
+# ifndef VBOX_WITH_VISTA_NO_SP
+ if (g_uNtVerCombined >= SUP_NT_VER_VISTA)
+# else
+ if (g_uNtVerCombined >= SUP_MAKE_NT_VER_COMBINED(6, 0, 6001, 0, 0))
+# endif
+ {
+ DbgPrint("vboxdrv: ObRegisterCallbacks was not found. Please make sure you got the latest updates and service packs installed\n");
+ rcNt = STATUS_SXS_VERSION_CONFLICT;
+ }
+ else
+ {
+ Log(("vboxdrv: ObRegisterCallbacks was not found; ignored pre-Vista\n"));
+ return rcNt = STATUS_SUCCESS;
+ }
+ g_pvObCallbacksCookie = NULL;
+ }
+
+ /*
+ * Drop process create/term notifications.
+ */
+ if (g_pfnPsSetCreateProcessNotifyRoutineEx)
+ g_pfnPsSetCreateProcessNotifyRoutineEx(supdrvNtProtectCallback_ProcessCreateNotifyEx, TRUE /*fRemove*/);
+ else
+ PsSetCreateProcessNotifyRoutine(supdrvNtProtectCallback_ProcessCreateNotify, TRUE /*fRemove*/);
+ }
+ else
+ LogRel(("vboxdrv: PsSetCreateProcessNotifyRoutine%s failed with rcNt=%#x\n",
+ g_pfnPsSetCreateProcessNotifyRoutineEx ? "Ex" : "", rcNt));
+ supHardenedWinTermImageVerifier();
+ }
+ else
+ rcNt = VBoxDrvNtErr2NtStatus(rc);
+
+ RTSemMutexDestroy(g_hErrorInfoLock);
+ g_hErrorInfoLock = NIL_RTSEMMUTEX;
+ }
+ else
+ rcNt = VBoxDrvNtErr2NtStatus(rc);
+
+ RTSpinlockDestroy(g_hNtProtectLock);
+ g_NtProtectTree = NIL_RTSPINLOCK;
+ return rcNt;
+}
+
+#endif /* VBOX_WITH_HARDENING */
+
diff --git a/src/VBox/HostDrivers/Support/win/SUPDrvA-win.asm b/src/VBox/HostDrivers/Support/win/SUPDrvA-win.asm
new file mode 100644
index 00000000..f899c815
--- /dev/null
+++ b/src/VBox/HostDrivers/Support/win/SUPDrvA-win.asm
@@ -0,0 +1,119 @@
+; $Id: SUPDrvA-win.asm $
+;; @file
+; VirtualBox Support Driver - Windows NT specific assembly parts.
+;
+
+;
+; Copyright (C) 2006-2023 Oracle and/or its affiliates.
+;
+; This file is part of VirtualBox base platform packages, as
+; available from https://www.virtualbox.org.
+;
+; This program is free software; you can redistribute it and/or
+; modify it under the terms of the GNU General Public License
+; as published by the Free Software Foundation, in version 3 of the
+; License.
+;
+; This program is distributed in the hope that it will be useful, but
+; WITHOUT ANY WARRANTY; without even the implied warranty of
+; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+; General Public License for more details.
+;
+; You should have received a copy of the GNU General Public License
+; along with this program; if not, see <https://www.gnu.org/licenses>.
+;
+; The contents of this file may alternatively be used under the terms
+; of the Common Development and Distribution License Version 1.0
+; (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+; in the VirtualBox distribution, in which case the provisions of the
+; CDDL are applicable instead of those of the GPL.
+;
+; You may elect to license modified versions of this file under the
+; terms and conditions of either the GPL or the CDDL or both.
+;
+; SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+;
+
+;*******************************************************************************
+;* Header Files *
+;*******************************************************************************
+%include "iprt/asmdefs.mac"
+
+BEGINCODE
+
+%ifdef VBOX_WITH_HARDENING
+
+ %ifdef RT_ARCH_X86
+;
+; Faking up ZwQueryVirtualMemory on XP and W2K3 where it's not exported.
+; Using ZwOpenFile as a helper as it has the name number of parameters.
+;
+extern IMPNAME(ZwOpenFile@24)
+
+BEGINPROC supdrvNtQueryVirtualMemory_Xxx
+ %macro NtQueryVirtualMemorySyscall 1
+ GLOBALNAME supdrvNtQueryVirtualMemory_ %+ %1
+ mov eax, %1
+ jmp supdrvNtQueryVirtualMemory_Jump
+ %endm
+ NtQueryVirtualMemorySyscall 0xAF
+ NtQueryVirtualMemorySyscall 0xB0
+ NtQueryVirtualMemorySyscall 0xB1
+ NtQueryVirtualMemorySyscall 0xB2
+ NtQueryVirtualMemorySyscall 0xB3
+ NtQueryVirtualMemorySyscall 0xB4
+ NtQueryVirtualMemorySyscall 0xB5
+ NtQueryVirtualMemorySyscall 0xB6
+ NtQueryVirtualMemorySyscall 0xB7
+ NtQueryVirtualMemorySyscall 0xB8
+ NtQueryVirtualMemorySyscall 0xB9
+ NtQueryVirtualMemorySyscall 0xBA
+ NtQueryVirtualMemorySyscall 0xBB
+ NtQueryVirtualMemorySyscall 0xBC
+ NtQueryVirtualMemorySyscall 0xBD
+ NtQueryVirtualMemorySyscall 0xBE
+
+supdrvNtQueryVirtualMemory_Jump:
+ mov edx, IMP2(ZwOpenFile@24)
+ lea edx, [edx + 5]
+ jmp edx
+ENDPROC supdrvNtQueryVirtualMemory_Xxx
+
+ %endif
+
+ %ifdef RT_ARCH_AMD64
+;
+; Faking up ZwQueryVirtualMemory on XP64 and W2K3-64 where it's not exported.
+; The C code locates and verifies the essentials in ZwRequestWaitReplyPort.
+;
+extern NAME(g_pfnKiServiceLinkage)
+extern NAME(g_pfnKiServiceInternal)
+BEGINPROC supdrvNtQueryVirtualMemory_Xxx
+ %macro NtQueryVirtualMemorySyscall 1
+ GLOBALNAME supdrvNtQueryVirtualMemory_ %+ %1
+ mov eax, %1
+ jmp supdrvNtQueryVirtualMemory_Jump
+ %endm
+
+ NtQueryVirtualMemorySyscall 0x1F
+ NtQueryVirtualMemorySyscall 0x20
+ NtQueryVirtualMemorySyscall 0x21
+ NtQueryVirtualMemorySyscall 0x22
+ NtQueryVirtualMemorySyscall 0x23
+
+supdrvNtQueryVirtualMemory_Jump:
+ cli
+ mov r10, rsp ; save call frame pointer.
+ mov r11, [NAME(g_pfnKiServiceLinkage) wrt rip]
+ push 0
+ push 0
+ push r10 ; call frame pointer (incoming rsp).
+ pushfq
+ push 10h
+ push r11 ; r11 = KiServiceLinkage (ret w/ unwind info)
+ jmp qword [NAME(g_pfnKiServiceInternal) wrt rip]
+ENDPROC supdrvNtQueryVirtualMemory_Xxx
+ %endif
+
+%endif ; VBOX_WITH_HARDENING
+
diff --git a/src/VBox/HostDrivers/Support/win/SUPHardenedVerify-win.h b/src/VBox/HostDrivers/Support/win/SUPHardenedVerify-win.h
new file mode 100644
index 00000000..0f953e38
--- /dev/null
+++ b/src/VBox/HostDrivers/Support/win/SUPHardenedVerify-win.h
@@ -0,0 +1,303 @@
+/* $Id: SUPHardenedVerify-win.h $ */
+/** @file
+ * VirtualBox Support Library/Driver - Hardened Verification, Windows.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+#ifndef VBOX_INCLUDED_SRC_Support_win_SUPHardenedVerify_win_h
+#define VBOX_INCLUDED_SRC_Support_win_SUPHardenedVerify_win_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+#include <iprt/types.h>
+#include <iprt/crypto/x509.h>
+#ifndef SUP_CERTIFICATES_ONLY
+# ifdef RT_OS_WINDOWS
+# include <iprt/ldr.h>
+# endif
+#endif
+
+
+RT_C_DECLS_BEGIN
+
+#ifndef SUP_CERTIFICATES_ONLY
+# ifdef RT_OS_WINDOWS
+DECLHIDDEN(int) supHardenedWinInitImageVerifier(PRTERRINFO pErrInfo);
+DECLHIDDEN(void) supHardenedWinTermImageVerifier(void);
+DECLHIDDEN(void) supR3HardenedWinVerifyCacheScheduleImports(RTLDRMOD hLdrMod, PCRTUTF16 pwszName);
+DECLHIDDEN(void) supR3HardenedWinVerifyCachePreload(PCRTUTF16 pwszName);
+
+
+typedef enum SUPHARDNTVPKIND
+{
+ SUPHARDNTVPKIND_VERIFY_ONLY = 1,
+ SUPHARDNTVPKIND_CHILD_PURIFICATION,
+ SUPHARDNTVPKIND_SELF_PURIFICATION,
+ SUPHARDNTVPKIND_SELF_PURIFICATION_LIMITED,
+ SUPHARDNTVPKIND_32BIT_HACK = 0x7fffffff
+} SUPHARDNTVPKIND;
+/** @name SUPHARDNTVP_F_XXX - Flags for supHardenedWinVerifyProcess
+ * @{ */
+/** Replace unwanted executable memory allocations with a new one that's filled
+ * with a safe read-write copy (default is just to free it).
+ *
+ * This is one way we attempt to work around buggy protection software that
+ * either result in host BSOD or VBox application malfunction. Here the current
+ * shit list:
+ * - Trend Micro's data protection software includes a buggy driver called
+ * sakfile.sys that has been observed crashing accessing user memory that we
+ * probably freed. I'd love to report this to Trend Micro, but unfortunately
+ * they doesn't advertise (or have?) an email address for reporting security
+ * vulnerabilities in the their software. Having wasted time looking and not
+ * very sorry for having to disclosing the bug here.
+ * - Maybe one more.
+ */
+#define SUPHARDNTVP_F_EXEC_ALLOC_REPLACE_WITH_RW RT_BIT_32(0)
+/** @} */
+DECLHIDDEN(int) supHardenedWinVerifyProcess(HANDLE hProcess, HANDLE hThread, SUPHARDNTVPKIND enmKind,
+ uint32_t fFlags, uint32_t *pcFixes, PRTERRINFO pErrInfo);
+DECLHIDDEN(int) supHardNtVpThread(HANDLE hProcess, HANDLE hThread, PRTERRINFO pErrInfo);
+DECLHIDDEN(int) supHardNtVpDebugger(HANDLE hProcess, PRTERRINFO pErrInfo);
+
+DECLHIDDEN(bool) supHardViUtf16PathIsEqualEx(PCRTUTF16 pawcLeft, size_t cwcLeft, const char *pszRight);
+DECLHIDDEN(bool) supHardViUniStrPathStartsWithUniStr(UNICODE_STRING const *pUniStrLeft,
+ UNICODE_STRING const *pUniStrRight, bool fCheckSlash);
+DECLHIDDEN(bool) supHardViUtf16PathStartsWithEx(PCRTUTF16 pwszLeft, uint32_t cwcLeft,
+ PCRTUTF16 pwszRight, uint32_t cwcRight, bool fCheckSlash);
+DECLHIDDEN(bool) supHardViIsAppPatchDir(PCRTUTF16 pwszPath, uint32_t cwcName);
+
+
+/**
+ * SUP image verifier loader reader instance.
+ */
+typedef struct SUPHNTVIRDR
+{
+ /** The core reader structure. */
+ RTLDRREADER Core;
+ /** The file handle. */
+ HANDLE hFile;
+ /** Handle to event sempahore in case we're force to deal with asynchronous I/O. */
+ HANDLE hEvent;
+ /** Current file offset. */
+ RTFOFF off;
+ /** The file size. */
+ uint64_t cbFile;
+ /** Flags for the verification callback, SUPHNTVI_F_XXX. */
+ uint32_t fFlags;
+ /** Number of signatures that verified okay. */
+ uint16_t cOkaySignatures;
+ /** Number of signatures that couldn't be successfully verified (time stamp
+ * issues, no certificate path, etc) but weren't fatal. */
+ uint16_t cNokSignatures;
+ /** Total number of signatures. */
+ uint16_t cTotalSignatures;
+ /** The current signature (for passing to supHardNtViCertVerifyCallback). */
+ uint16_t iCurSignature;
+ /** The last non-fatal signature failure. */
+ int rcLastSignatureFailure;
+ /** Log name. */
+ char szFilename[1];
+} SUPHNTVIRDR;
+/** Pointer to an SUP image verifier loader reader instance. */
+typedef SUPHNTVIRDR *PSUPHNTVIRDR;
+DECLHIDDEN(int) supHardNtViRdrCreate(HANDLE hFile, PCRTUTF16 pwszName, uint32_t fFlags, PSUPHNTVIRDR *ppNtViRdr);
+DECLHIDDEN(bool) supHardenedWinIsWinVerifyTrustCallable(void);
+DECLHIDDEN(int) supHardenedWinVerifyImageTrust(HANDLE hFile, PCRTUTF16 pwszName, uint32_t fFlags, int rc,
+ bool *pfWinVerifyTrust, PRTERRINFO pErrInfo);
+DECLHIDDEN(int) supHardenedWinVerifyImageByHandle(HANDLE hFile, PCRTUTF16 pwszName, uint32_t fFlags,
+ bool fAvoidWinVerifyTrust, bool *pfWinVerifyTrust, PRTERRINFO pErrInfo);
+DECLHIDDEN(int) supHardenedWinVerifyImageByHandleNoName(HANDLE hFile, uint32_t fFlags, PRTERRINFO pErrInfo);
+DECLHIDDEN(int) supHardenedWinVerifyImageByLdrMod(RTLDRMOD hLdrMod, PCRTUTF16 pwszName, PSUPHNTVIRDR pNtViRdr,
+ bool fAvoidWinVerifyTrust, bool *pfWinVerifyTrust, PRTERRINFO pErrInfo);
+/** @name SUPHNTVI_F_XXX - Flags for supHardenedWinVerifyImageByHandle.
+ * @{ */
+/** The signing certificate must be the same as the one the VirtualBox build
+ * was signed with. */
+# define SUPHNTVI_F_REQUIRE_BUILD_CERT RT_BIT(0)
+/** Require kernel code signing level. */
+# define SUPHNTVI_F_REQUIRE_KERNEL_CODE_SIGNING RT_BIT(1)
+/** Require the image to force the memory mapper to do signature checking. */
+# define SUPHNTVI_F_REQUIRE_SIGNATURE_ENFORCEMENT RT_BIT(2)
+/** Whether to allow image verification by catalog file. */
+# define SUPHNTVI_F_ALLOW_CAT_FILE_VERIFICATION RT_BIT(3)
+/** The file owner must be TrustedInstaller on Vista+. */
+# define SUPHNTVI_F_TRUSTED_INSTALLER_OWNER RT_BIT(4)
+/** Ignore the image architecture (otherwise it must match the verification
+ * code). Used with resource images and such. */
+# define SUPHNTVI_F_IGNORE_ARCHITECTURE RT_BIT(30)
+/** Raw-mode context image, always 32-bit. */
+# define SUPHNTVI_F_RC_IMAGE RT_BIT(31)
+/** @} */
+
+/* Array in SUPHardenedVerifyImage-win.cpp */
+extern const RTSTRTUPLE g_aSupNtViBlacklistedDlls[];
+
+/**
+ * Loader cache entry.
+ *
+ * This is for avoiding loading and signature checking a file multiple times,
+ * due to multiple passes thru the process validation code (and syscall import
+ * code of NTDLL).
+ */
+typedef struct SUPHNTLDRCACHEENTRY
+{
+ /** The file name (from g_apszSupNtVpAllowedDlls or
+ * g_apszSupNtVpAllowedVmExes). */
+ const char *pszName;
+ /** Load module associated with the image during content verfication. */
+ RTLDRMOD hLdrMod;
+ /** The file reader. */
+ PSUPHNTVIRDR pNtViRdr;
+ /** The module file handle, if we've opened it.
+ * (pNtviRdr does not close the file handle on destruction.) */
+ HANDLE hFile;
+ /** Bits buffer. */
+ uint8_t *pbBits;
+ /** Set if verified. */
+ bool fVerified;
+ /** Whether we've got valid cacheable image bits. */
+ bool fValidBits;
+ /** The image base address. */
+ uintptr_t uImageBase;
+} SUPHNTLDRCACHEENTRY;
+/** Pointer to a loader cache entry. */
+typedef SUPHNTLDRCACHEENTRY *PSUPHNTLDRCACHEENTRY;
+DECLHIDDEN(int) supHardNtLdrCacheOpen(const char *pszName, PSUPHNTLDRCACHEENTRY *ppEntry, PRTERRINFO pErrInfo);
+DECLHIDDEN(int) supHardNtLdrCacheEntryVerify(PSUPHNTLDRCACHEENTRY pEntry, PCRTUTF16 pwszName, PRTERRINFO pErrInfo);
+DECLHIDDEN(int) supHardNtLdrCacheEntryGetBits(PSUPHNTLDRCACHEENTRY pEntry, uint8_t **ppbBits, RTLDRADDR uBaseAddress,
+ PFNRTLDRIMPORT pfnGetImport, void *pvUser, PRTERRINFO pErrInfo);
+
+
+/** Which directory under the system root to get. */
+typedef enum SUPHARDNTSYSROOTDIR
+{
+ kSupHardNtSysRootDir_System32 = 0,
+ kSupHardNtSysRootDir_WinSxS,
+} SUPHARDNTSYSROOTDIR;
+
+DECLHIDDEN(int) supHardNtGetSystemRootDir(void *pvBuf, uint32_t cbBuf, SUPHARDNTSYSROOTDIR enmDir, PRTERRINFO pErrInfo);
+
+# ifndef SUPHNTVI_NO_NT_STUFF
+
+/** Typical system root directory buffer. */
+typedef struct SUPSYSROOTDIRBUF
+{
+ UNICODE_STRING UniStr;
+ WCHAR awcBuffer[260];
+} SUPSYSROOTDIRBUF;
+extern SUPSYSROOTDIRBUF g_System32NtPath;
+extern SUPSYSROOTDIRBUF g_WinSxSNtPath;
+#if defined(IN_RING3) && !defined(VBOX_PERMIT_EVEN_MORE)
+extern SUPSYSROOTDIRBUF g_ProgramFilesNtPath;
+extern SUPSYSROOTDIRBUF g_CommonFilesNtPath;
+# if ARCH_BITS == 64
+extern SUPSYSROOTDIRBUF g_ProgramFilesX86NtPath;
+extern SUPSYSROOTDIRBUF g_CommonFilesX86NtPath;
+# endif
+#endif /* IN_RING3 && !VBOX_PERMIT_EVEN_MORE */
+extern SUPSYSROOTDIRBUF g_SupLibHardenedExeNtPath;
+extern SUPSYSROOTDIRBUF g_SupLibHardenedAppBinNtPath;
+
+# ifdef IN_RING0
+/** Pointer to NtQueryVirtualMemory. */
+typedef DECLCALLBACKPTR_EX(NTSTATUS, NTAPI, PFNNTQUERYVIRTUALMEMORY,(HANDLE, void const *, MEMORY_INFORMATION_CLASS,
+ PVOID, SIZE_T, PSIZE_T));
+extern PFNNTQUERYVIRTUALMEMORY g_pfnNtQueryVirtualMemory;
+# endif
+
+# endif /* SUPHNTVI_NO_NT_STUFF */
+
+/** Creates a combined NT version number for simple comparisons. */
+#define SUP_MAKE_NT_VER_COMBINED(a_uMajor, a_uMinor, a_uBuild, a_uSpMajor, a_uSpMinor) \
+ ( ((uint32_t)((a_uMajor) & UINT32_C(0xf)) << 28) \
+ | ((uint32_t)((a_uMinor) & UINT32_C(0xf)) << 24) \
+ | ((uint32_t)((a_uBuild) & UINT32_C(0xffff)) << 8) \
+ | ((uint32_t)((a_uSpMajor) & UINT32_C(0xf)) << 4) \
+ | (uint32_t)((a_uSpMinor) & UINT32_C(0xf)) )
+/** Simple version of SUP_MAKE_NT_VER_COMBINED. */
+#define SUP_MAKE_NT_VER_SIMPLE(a_uMajor, a_uMinor) SUP_MAKE_NT_VER_COMBINED(a_uMajor, a_uMinor, 0, 0, 0)
+extern uint32_t g_uNtVerCombined;
+
+/** @name NT version constants for less-than checks.
+ * @{ */
+/** Combined NT version number for XP. */
+#define SUP_NT_VER_XP SUP_MAKE_NT_VER_SIMPLE(5,1)
+/** Combined NT version number for Windows server 2003 & XP64. */
+#define SUP_NT_VER_W2K3 SUP_MAKE_NT_VER_SIMPLE(5,2)
+/** Combined NT version number for Vista. */
+#define SUP_NT_VER_VISTA SUP_MAKE_NT_VER_SIMPLE(6,0)
+/** Combined NT version number for Vista with SP1. */
+#define SUP_NT_VER_VISTA_SP1 SUP_MAKE_NT_VER_COMBINED(6,0,6001,1,0)
+/** Combined NT version number for Windows 7. */
+#define SUP_NT_VER_W70 SUP_MAKE_NT_VER_SIMPLE(6,1)
+/** Combined NT version number for Windows 8.0. */
+#define SUP_NT_VER_W80 SUP_MAKE_NT_VER_SIMPLE(6,2)
+/** Combined NT version number for Windows 8.1. */
+#define SUP_NT_VER_W81 SUP_MAKE_NT_VER_SIMPLE(6,3)
+/** @} */
+
+# endif
+
+# ifndef IN_SUP_HARDENED_R3
+# include <iprt/mem.h>
+# include <iprt/string.h>
+
+# define suplibHardenedMemComp memcmp
+# define suplibHardenedMemCopy memcpy
+# define suplibHardenedMemSet memset
+# define suplibHardenedStrCopy strcpy
+# define suplibHardenedStrLen strlen
+# define suplibHardenedStrCat strcat
+# define suplibHardenedStrCmp strcmp
+# define suplibHardenedStrNCmp strncmp
+# else /* IN_SUP_HARDENED_R3 */
+# include <iprt/mem.h>
+# if 0
+# define memcmp suplibHardenedMemComp
+# define memcpy suplibHardenedMemCopy
+# define memset suplibHardenedMemSet
+# define strcpy suplibHardenedStrCopy
+# define strlen suplibHardenedStrLen
+# define strcat suplibHardenedStrCat
+# define strcmp suplibHardenedStrCmp
+# define strncmp suplibHardenedStrNCmp
+# endif
+# endif /* IN_SUP_HARDENED_R3 */
+
+#endif /* SUP_CERTIFICATES_ONLY */
+
+RT_C_DECLS_END
+
+#endif /* !VBOX_INCLUDED_SRC_Support_win_SUPHardenedVerify_win_h */
+
diff --git a/src/VBox/HostDrivers/Support/win/SUPHardenedVerifyImage-win.cpp b/src/VBox/HostDrivers/Support/win/SUPHardenedVerifyImage-win.cpp
new file mode 100644
index 00000000..747d975e
--- /dev/null
+++ b/src/VBox/HostDrivers/Support/win/SUPHardenedVerifyImage-win.cpp
@@ -0,0 +1,3090 @@
+/* $Id: SUPHardenedVerifyImage-win.cpp $ */
+/** @file
+ * VirtualBox Support Library/Driver - Hardened Image Verification, Windows.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#ifdef IN_RING0
+# ifndef IPRT_NT_MAP_TO_ZW
+# define IPRT_NT_MAP_TO_ZW
+# endif
+# include <iprt/nt/nt.h>
+# include <ntimage.h>
+#else
+# include <iprt/nt/nt-and-windows.h>
+# include "Wintrust.h"
+# include "Softpub.h"
+# include "mscat.h"
+# ifndef LOAD_LIBRARY_SEARCH_APPLICATION_DIR
+# define LOAD_LIBRARY_SEARCH_SYSTEM32 0x800
+# endif
+#endif
+
+#include <VBox/sup.h>
+#include <VBox/err.h>
+#include <iprt/ctype.h>
+#include <iprt/ldr.h>
+#include <iprt/log.h>
+#include <iprt/path.h>
+#include <iprt/string.h>
+#include <iprt/utf16.h>
+#include <iprt/crypto/pkcs7.h>
+#include <iprt/crypto/store.h>
+
+#ifdef IN_RING0
+# include "SUPDrvInternal.h"
+#else
+# include "SUPLibInternal.h"
+#endif
+#include "win/SUPHardenedVerify-win.h"
+
+
+/*********************************************************************************************************************************
+* Defined Constants And Macros *
+*********************************************************************************************************************************/
+/** The size of static hash (output) buffers.
+ * Avoids dynamic allocations and cleanups for of small buffers as well as extra
+ * calls for getting the appropriate buffer size. The largest digest in regular
+ * use by current windows version is SHA-512, we double this and hope it's
+ * enough a good while. */
+#define SUPHARDNTVI_MAX_CAT_HASH_SIZE 128
+
+
+#if defined(VBOX_PERMIT_EVEN_MORE) && !defined(VBOX_PERMIT_MORE)
+# error "VBOX_PERMIT_EVEN_MORE without VBOX_PERMIT_MORE!"
+#endif
+
+
+/*********************************************************************************************************************************
+* Structures and Typedefs *
+*********************************************************************************************************************************/
+
+#ifdef IN_RING3
+typedef DECLCALLBACKPTR_EX(LONG, WINAPI, PFNWINVERIFYTRUST,(HWND hwnd, GUID const *pgActionID, PVOID pWVTData));
+typedef DECLCALLBACKPTR_EX(BOOL, WINAPI, PFNCRYPTCATADMINACQUIRECONTEXT,(HCATADMIN *phCatAdmin, const GUID *pGuidSubsystem,
+ DWORD dwFlags));
+typedef DECLCALLBACKPTR_EX(BOOL, WINAPI, PFNCRYPTCATADMINACQUIRECONTEXT2,(HCATADMIN *phCatAdmin, const GUID *pGuidSubsystem,
+ PCWSTR pwszHashAlgorithm,
+ struct _CERT_STRONG_SIGN_PARA const *pStrongHashPolicy,
+ DWORD dwFlags));
+typedef DECLCALLBACKPTR_EX(BOOL, WINAPI, PFNCRYPTCATADMINCALCHASHFROMFILEHANDLE,(HANDLE hFile, DWORD *pcbHash, BYTE *pbHash,
+ DWORD dwFlags));
+typedef DECLCALLBACKPTR_EX(BOOL, WINAPI, PFNCRYPTCATADMINCALCHASHFROMFILEHANDLE2,(HCATADMIN hCatAdmin, HANDLE hFile,
+ DWORD *pcbHash, BYTE *pbHash, DWORD dwFlags));
+typedef DECLCALLBACKPTR_EX(HCATINFO, WINAPI, PFNCRYPTCATADMINENUMCATALOGFROMHASH,(HCATADMIN hCatAdmin, BYTE *pbHash, DWORD cbHash,
+ DWORD dwFlags, HCATINFO *phPrevCatInfo));
+typedef DECLCALLBACKPTR_EX(BOOL, WINAPI, PFNCRYPTCATADMINRELEASECATALOGCONTEXT,(HCATADMIN hCatAdmin, HCATINFO hCatInfo,
+ DWORD dwFlags));
+typedef DECLCALLBACKPTR_EX(BOOL, WINAPI, PFNCRYPTCATDADMINRELEASECONTEXT,(HCATADMIN hCatAdmin, DWORD dwFlags));
+typedef DECLCALLBACKPTR_EX(BOOL, WINAPI, PFNCRYPTCATCATALOGINFOFROMCONTEXT,(HCATINFO hCatInfo, CATALOG_INFO *psCatInfo,
+ DWORD dwFlags));
+
+typedef DECLCALLBACKPTR_EX(HCERTSTORE, WINAPI, PFNCERTOPENSTORE,(PCSTR pszStoreProvider, DWORD dwEncodingType,
+ HCRYPTPROV_LEGACY hCryptProv, DWORD dwFlags, const void *pvParam));
+typedef DECLCALLBACKPTR_EX(BOOL, WINAPI, PFNCERTCLOSESTORE,(HCERTSTORE hCertStore, DWORD dwFlags));
+typedef DECLCALLBACKPTR_EX(PCCERT_CONTEXT, WINAPI, PFNCERTENUMCERTIFICATESINSTORE,(HCERTSTORE hCertStore,
+ PCCERT_CONTEXT pPrevCertContext));
+
+typedef DECLCALLBACKPTR_EX(NTSTATUS, WINAPI, PFNBCRYPTOPENALGORTIHMPROVIDER,(BCRYPT_ALG_HANDLE *phAlgo, PCWSTR pwszAlgoId,
+ PCWSTR pwszImpl, DWORD dwFlags));
+#endif
+
+
+/*********************************************************************************************************************************
+* Global Variables *
+*********************************************************************************************************************************/
+/** The build certificate. */
+static RTCRX509CERTIFICATE g_BuildX509Cert;
+
+/** Store for root software publisher certificates. */
+static RTCRSTORE g_hSpcRootStore = NIL_RTCRSTORE;
+/** Store for root NT kernel certificates. */
+static RTCRSTORE g_hNtKernelRootStore = NIL_RTCRSTORE;
+
+/** Store containing SPC, NT kernel signing, and timestamp root certificates. */
+static RTCRSTORE g_hSpcAndNtKernelRootStore = NIL_RTCRSTORE;
+/** Store for supplemental certificates for use with
+ * g_hSpcAndNtKernelRootStore. */
+static RTCRSTORE g_hSpcAndNtKernelSuppStore = NIL_RTCRSTORE;
+
+/** The full \\SystemRoot\\System32 path. */
+SUPSYSROOTDIRBUF g_System32NtPath;
+/** The full \\SystemRoot\\WinSxS path. */
+SUPSYSROOTDIRBUF g_WinSxSNtPath;
+#if defined(IN_RING3) && !defined(VBOX_PERMIT_EVEN_MORE)
+/** The full 'Program Files' path. */
+SUPSYSROOTDIRBUF g_ProgramFilesNtPath;
+# ifdef RT_ARCH_AMD64
+/** The full 'Program Files (x86)' path. */
+SUPSYSROOTDIRBUF g_ProgramFilesX86NtPath;
+# endif
+/** The full 'Common Files' path. */
+SUPSYSROOTDIRBUF g_CommonFilesNtPath;
+# ifdef RT_ARCH_AMD64
+/** The full 'Common Files (x86)' path. */
+SUPSYSROOTDIRBUF g_CommonFilesX86NtPath;
+# endif
+#endif /* IN_RING3 && !VBOX_PERMIT_MORE*/
+
+/**
+ * Blacklisted DLL names.
+ */
+const RTSTRTUPLE g_aSupNtViBlacklistedDlls[] =
+{
+ { RT_STR_TUPLE("SCROBJ.dll") },
+ { NULL, 0 } /* terminator entry */
+};
+
+
+static union
+{
+ SID Sid;
+ uint8_t abPadding[SECURITY_MAX_SID_SIZE];
+}
+/** The TrustedInstaller SID (Vista+). */
+ g_TrustedInstallerSid,
+/** Local system ID (S-1-5-21). */
+ g_LocalSystemSid,
+/** Builtin Administrators group alias (S-1-5-32-544). */
+ g_AdminsGroupSid;
+
+
+/** Set after we've retrived other SPC root certificates from the system. */
+static bool g_fHaveOtherRoots = false;
+
+#if defined(IN_RING3) && !defined(IN_SUP_HARDENED_R3)
+/** Combined windows NT version number. See SUP_MAKE_NT_VER_COMBINED and
+ * SUP_MAKE_NT_VER_SIMPLE. */
+uint32_t g_uNtVerCombined;
+#endif
+
+#ifdef IN_RING3
+/** Timestamp hack working around issues with old DLLs that we ship.
+ * See supHardenedWinVerifyImageByHandle() for details. */
+static uint64_t g_uBuildTimestampHack = 0;
+#endif
+
+#ifdef IN_RING3
+/** Pointer to WinVerifyTrust. */
+PFNWINVERIFYTRUST g_pfnWinVerifyTrust;
+/** Pointer to CryptCATAdminAcquireContext. */
+PFNCRYPTCATADMINACQUIRECONTEXT g_pfnCryptCATAdminAcquireContext;
+/** Pointer to CryptCATAdminAcquireContext2 if available. */
+PFNCRYPTCATADMINACQUIRECONTEXT2 g_pfnCryptCATAdminAcquireContext2;
+/** Pointer to CryptCATAdminCalcHashFromFileHandle. */
+PFNCRYPTCATADMINCALCHASHFROMFILEHANDLE g_pfnCryptCATAdminCalcHashFromFileHandle;
+/** Pointer to CryptCATAdminCalcHashFromFileHandle2. */
+PFNCRYPTCATADMINCALCHASHFROMFILEHANDLE2 g_pfnCryptCATAdminCalcHashFromFileHandle2;
+/** Pointer to CryptCATAdminEnumCatalogFromHash. */
+PFNCRYPTCATADMINENUMCATALOGFROMHASH g_pfnCryptCATAdminEnumCatalogFromHash;
+/** Pointer to CryptCATAdminReleaseCatalogContext. */
+PFNCRYPTCATADMINRELEASECATALOGCONTEXT g_pfnCryptCATAdminReleaseCatalogContext;
+/** Pointer to CryptCATAdminReleaseContext. */
+PFNCRYPTCATDADMINRELEASECONTEXT g_pfnCryptCATAdminReleaseContext;
+/** Pointer to CryptCATCatalogInfoFromContext. */
+PFNCRYPTCATCATALOGINFOFROMCONTEXT g_pfnCryptCATCatalogInfoFromContext;
+
+/** Where we store the TLS entry for detecting WinVerifyTrustRecursion. */
+static uint32_t g_iTlsWinVerifyTrustRecursion = UINT32_MAX;
+/** Fallback WinVerifyTrust recursion protection. */
+static uint32_t volatile g_idActiveThread = UINT32_MAX;
+
+#endif
+
+
+/*********************************************************************************************************************************
+* Internal Functions *
+*********************************************************************************************************************************/
+#ifdef IN_RING3
+static int supR3HardNtViCallWinVerifyTrust(HANDLE hFile, PCRTUTF16 pwszName, uint32_t fFlags, PRTERRINFO pErrInfo,
+ PFNWINVERIFYTRUST pfnWinVerifyTrust, HRESULT *phrcWinVerifyTrust);
+static int supR3HardNtViCallWinVerifyTrustCatFile(HANDLE hFile, PCRTUTF16 pwszName, uint32_t fFlags, PRTERRINFO pErrInfo,
+ PFNWINVERIFYTRUST pfnWinVerifyTrust);
+#endif
+
+
+
+
+/** @copydoc RTLDRREADER::pfnRead */
+static DECLCALLBACK(int) supHardNtViRdrRead(PRTLDRREADER pReader, void *pvBuf, size_t cb, RTFOFF off)
+{
+ PSUPHNTVIRDR pNtViRdr = (PSUPHNTVIRDR)pReader;
+ Assert(pNtViRdr->Core.uMagic == RTLDRREADER_MAGIC);
+ NTSTATUS rcNt;
+
+ /* Check for type overflow (paranoia). */
+ if ((ULONG)cb != cb)
+ return VERR_OUT_OF_RANGE;
+
+#ifdef IN_RING3
+ /* Make sure the event semaphore is reset (normally we don't use one). */
+ if (pNtViRdr->hEvent)
+ {
+ rcNt = NtClearEvent(pNtViRdr->hEvent);
+ if (!NT_SUCCESS(rcNt))
+ return RTErrConvertFromNtStatus(rcNt);
+ }
+#endif
+
+ /* Perform the read. */
+ LARGE_INTEGER offNt;
+ offNt.QuadPart = off;
+
+ IO_STATUS_BLOCK Ios = RTNT_IO_STATUS_BLOCK_INITIALIZER;
+ rcNt = NtReadFile(pNtViRdr->hFile,
+ pNtViRdr->hEvent,
+ NULL /*ApcRoutine*/,
+ NULL /*ApcContext*/,
+ &Ios,
+ pvBuf,
+ (ULONG)cb,
+ &offNt,
+ NULL);
+
+#ifdef IN_RING0
+ /* In ring-0 the handles shall be synchronized and not alertable. */
+ AssertMsg(rcNt == STATUS_SUCCESS || !NT_SUCCESS(rcNt), ("%#x\n", rcNt));
+#else
+ /* In ring-3 we like our handles synchronized and non-alertable, but we
+ sometimes have to take what we can get. So, deal with pending I/O as
+ best we can. */
+ if (rcNt == STATUS_PENDING)
+ rcNt = NtWaitForSingleObject(pNtViRdr->hEvent ? pNtViRdr->hEvent : pNtViRdr->hFile, FALSE /*Alertable*/, NULL);
+#endif
+ if (NT_SUCCESS(rcNt))
+ rcNt = Ios.Status;
+ if (NT_SUCCESS(rcNt))
+ {
+ /* We require the caller to not read beyond the end of the file since
+ we don't have any way to communicate that we've read less that
+ requested. */
+ if (Ios.Information == cb)
+ {
+ pNtViRdr->off = off + cb; /* (just for show) */
+ return VINF_SUCCESS;
+ }
+#ifdef IN_RING3
+ supR3HardenedError(VERR_READ_ERROR, false,
+ "supHardNtViRdrRead: Only got %#zx bytes when requesting %#zx bytes at %#llx in '%s'.\n",
+ Ios.Information, off, cb, pNtViRdr->szFilename);
+#endif
+ }
+ pNtViRdr->off = -1;
+ return VERR_READ_ERROR;
+}
+
+
+/** @copydoc RTLDRREADER::pfnTell */
+static DECLCALLBACK(RTFOFF) supHardNtViRdrTell(PRTLDRREADER pReader)
+{
+ PSUPHNTVIRDR pNtViRdr = (PSUPHNTVIRDR)pReader;
+ Assert(pNtViRdr->Core.uMagic == RTLDRREADER_MAGIC);
+ return pNtViRdr->off;
+}
+
+
+/** @copydoc RTLDRREADER::pfnSize */
+static DECLCALLBACK(uint64_t) supHardNtViRdrSize(PRTLDRREADER pReader)
+{
+ PSUPHNTVIRDR pNtViRdr = (PSUPHNTVIRDR)pReader;
+ Assert(pNtViRdr->Core.uMagic == RTLDRREADER_MAGIC);
+ return pNtViRdr->cbFile;
+}
+
+
+/** @copydoc RTLDRREADER::pfnLogName */
+static DECLCALLBACK(const char *) supHardNtViRdrLogName(PRTLDRREADER pReader)
+{
+ PSUPHNTVIRDR pNtViRdr = (PSUPHNTVIRDR)pReader;
+ return pNtViRdr->szFilename;
+}
+
+
+/** @copydoc RTLDRREADER::pfnMap */
+static DECLCALLBACK(int) supHardNtViRdrMap(PRTLDRREADER pReader, const void **ppvBits)
+{
+ RT_NOREF2(pReader, ppvBits);
+ return VERR_NOT_SUPPORTED;
+}
+
+
+/** @copydoc RTLDRREADER::pfnUnmap */
+static DECLCALLBACK(int) supHardNtViRdrUnmap(PRTLDRREADER pReader, const void *pvBits)
+{
+ RT_NOREF2(pReader, pvBits);
+ return VERR_NOT_SUPPORTED;
+}
+
+
+/** @copydoc RTLDRREADER::pfnDestroy */
+static DECLCALLBACK(int) supHardNtViRdrDestroy(PRTLDRREADER pReader)
+{
+ PSUPHNTVIRDR pNtViRdr = (PSUPHNTVIRDR)pReader;
+ Assert(pNtViRdr->Core.uMagic == RTLDRREADER_MAGIC);
+
+ pNtViRdr->Core.uMagic = ~RTLDRREADER_MAGIC;
+ pNtViRdr->hFile = NULL;
+#ifdef IN_RING3
+ if (pNtViRdr->hEvent)
+ {
+ NtClose(pNtViRdr->hEvent);
+ pNtViRdr->hEvent = NULL;
+ }
+#endif
+ RTMemFree(pNtViRdr);
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * Creates a loader reader instance for the given NT file handle.
+ *
+ * @returns iprt status code.
+ * @param hFile Native NT file handle.
+ * @param pwszName Optional file name.
+ * @param fFlags Flags, SUPHNTVI_F_XXX.
+ * @param ppNtViRdr Where to store the reader instance on success.
+ */
+DECLHIDDEN(int) supHardNtViRdrCreate(HANDLE hFile, PCRTUTF16 pwszName, uint32_t fFlags, PSUPHNTVIRDR *ppNtViRdr)
+{
+ /*
+ * Try determine the size of the file.
+ */
+ IO_STATUS_BLOCK Ios = RTNT_IO_STATUS_BLOCK_INITIALIZER;
+ FILE_STANDARD_INFORMATION StdInfo;
+ NTSTATUS rcNt = NtQueryInformationFile(hFile, &Ios, &StdInfo, sizeof(StdInfo), FileStandardInformation);
+ if (!NT_SUCCESS(rcNt) || !NT_SUCCESS(Ios.Status))
+ return VERR_LDRVI_FILE_LENGTH_ERROR;
+
+ /*
+ * Figure the file mode so we can see whether we'll be needing an event
+ * semaphore for waiting on reads. This may happen in very unlikely
+ * NtCreateSection scenarios.
+ */
+#if defined(IN_RING3) || defined(VBOX_STRICT)
+ Ios.Status = STATUS_UNSUCCESSFUL;
+ ULONG fMode;
+ rcNt = NtQueryInformationFile(hFile, &Ios, &fMode, sizeof(fMode), FileModeInformation);
+ if (!NT_SUCCESS(rcNt) || !NT_SUCCESS(Ios.Status))
+ return VERR_SUP_VP_FILE_MODE_ERROR;
+#endif
+
+ HANDLE hEvent = NULL;
+#ifdef IN_RING3
+ if (!(fMode & (FILE_SYNCHRONOUS_IO_NONALERT | FILE_SYNCHRONOUS_IO_ALERT)))
+ {
+ rcNt = NtCreateEvent(&hEvent, EVENT_ALL_ACCESS, NULL, NotificationEvent, FALSE);
+ if (!NT_SUCCESS(rcNt))
+ return VERR_SUP_VP_CREATE_READ_EVT_SEM_FAILED;
+ }
+#else
+ Assert(fMode & FILE_SYNCHRONOUS_IO_NONALERT);
+#endif
+
+ /*
+ * Calc the file name length and allocate memory for the reader instance.
+ */
+ size_t cchFilename = 0;
+ if (pwszName)
+ cchFilename = RTUtf16CalcUtf8Len(pwszName);
+
+ int rc = VERR_NO_MEMORY;
+ PSUPHNTVIRDR pNtViRdr = (PSUPHNTVIRDR)RTMemAllocZ(sizeof(*pNtViRdr) + cchFilename);
+ if (!pNtViRdr)
+ {
+#ifdef IN_RING3
+ if (hEvent != NULL)
+ NtClose(hEvent);
+#endif
+ return VERR_NO_MEMORY;
+ }
+
+ /*
+ * Initialize the structure.
+ */
+ if (cchFilename)
+ {
+ char *pszName = &pNtViRdr->szFilename[0];
+ rc = RTUtf16ToUtf8Ex(pwszName, RTSTR_MAX, &pszName, cchFilename + 1, NULL);
+ AssertStmt(RT_SUCCESS(rc), pNtViRdr->szFilename[0] = '\0');
+ }
+ else
+ pNtViRdr->szFilename[0] = '\0';
+
+ pNtViRdr->Core.uMagic = RTLDRREADER_MAGIC;
+ pNtViRdr->Core.pfnRead = supHardNtViRdrRead;
+ pNtViRdr->Core.pfnTell = supHardNtViRdrTell;
+ pNtViRdr->Core.pfnSize = supHardNtViRdrSize;
+ pNtViRdr->Core.pfnLogName = supHardNtViRdrLogName;
+ pNtViRdr->Core.pfnMap = supHardNtViRdrMap;
+ pNtViRdr->Core.pfnUnmap = supHardNtViRdrUnmap;
+ pNtViRdr->Core.pfnDestroy = supHardNtViRdrDestroy;
+ pNtViRdr->hFile = hFile;
+ pNtViRdr->hEvent = hEvent;
+ pNtViRdr->off = 0;
+ pNtViRdr->cbFile = (uint64_t)StdInfo.EndOfFile.QuadPart;
+ pNtViRdr->fFlags = fFlags;
+ *ppNtViRdr = pNtViRdr;
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * Checks if the file is owned by TrustedInstaller (Vista+) or similar.
+ *
+ * @returns true if owned by TrustedInstaller of pre-Vista, false if not.
+ *
+ * @param hFile The handle to the file.
+ * @param pwszName The name of the file.
+ */
+static bool supHardNtViCheckIsOwnedByTrustedInstallerOrSimilar(HANDLE hFile, PCRTUTF16 pwszName)
+{
+ if (g_uNtVerCombined < SUP_NT_VER_VISTA)
+ return true;
+
+ /*
+ * Get the ownership information.
+ */
+ union
+ {
+ SECURITY_DESCRIPTOR_RELATIVE Rel;
+ SECURITY_DESCRIPTOR Abs;
+ uint8_t abView[256];
+ } uBuf;
+ ULONG cbActual;
+ NTSTATUS rcNt = NtQuerySecurityObject(hFile, OWNER_SECURITY_INFORMATION, &uBuf.Abs, sizeof(uBuf), &cbActual);
+ if (!NT_SUCCESS(rcNt))
+ {
+ SUP_DPRINTF(("NtQuerySecurityObject failed with rcNt=%#x on '%ls'\n", rcNt, pwszName));
+ return false;
+ }
+
+ /*
+ * Check the owner.
+ *
+ * Initially we wished to only allow TrustedInstaller. But a Windows CAPI
+ * plugin "Program Files\Tumbleweed\Desktop Validator\tmwdcapiclient.dll"
+ * turned up owned by the local system user, and we cannot operate without
+ * the plugin loaded once it's installed (WinVerityTrust fails).
+ *
+ * We'd like to avoid allowing Builtin\Administrators here since it's the
+ * default owner of anything an admin user creates (at least when elevated).
+ * Seems windows update or someone ends up installing or modifying system
+ * DLL ownership to this group, so for system32 and winsxs it's unavoidable.
+ * And, not surprise, a bunch of products, including AV, firewalls and similar
+ * ends up with their files installed with this group as owner. For instance
+ * if we wish to have NAT continue working, we need to allow this.
+ *
+ * Hopefully, we can limit the allowed files to these owners though, so
+ * we won't be subject to ordinary (non-admin, or not elevated) users
+ * downloading or be tricked into putting evil DLLs around the place...
+ */
+ PSID pOwner = uBuf.Rel.Control & SE_SELF_RELATIVE ? &uBuf.abView[uBuf.Rel.Owner] : uBuf.Abs.Owner;
+ Assert((uintptr_t)pOwner - (uintptr_t)&uBuf < sizeof(uBuf) - sizeof(SID));
+ if (RtlEqualSid(pOwner, &g_TrustedInstallerSid))
+ return true;
+ if (RtlEqualSid(pOwner, &g_LocalSystemSid))
+ return true;
+ if (RtlEqualSid(pOwner, &g_AdminsGroupSid))
+ {
+ SUP_DPRINTF(("%ls: Owner is administrators group.\n", pwszName));
+ return true;
+ }
+
+ SUP_DPRINTF(("%ls: Owner is not trusted installer (%.*Rhxs)\n",
+ pwszName, ((uint8_t *)pOwner)[1] /*SubAuthorityCount*/ * sizeof(ULONG) + 8, pOwner));
+ RT_NOREF1(pwszName);
+ return false;
+}
+
+
+/**
+ * Simple case insensitive UTF-16 / ASCII path compare.
+ *
+ * @returns true if equal, false if not.
+ * @param pawcLeft The UTF-16 path string, not necessarily null
+ * terminated.
+ * @param cwcLeft The number of chars in the left string,
+ * RTSTR_MAX if unknown but terminated.
+ * @param pszRight The ascii string.
+ */
+DECLHIDDEN(bool) supHardViUtf16PathIsEqualEx(PCRTUTF16 pawcLeft, size_t cwcLeft, const char *pszRight)
+{
+ for (;;)
+ {
+ RTUTF16 wc;
+ if (cwcLeft-- > 0)
+ wc =*pawcLeft++;
+ else
+ wc = 0;
+ uint8_t b = *pszRight++;
+ if (b != wc)
+ {
+ if (wc >= 0x80)
+ return false;
+ wc = RT_C_TO_LOWER(wc);
+ if (wc != b)
+ {
+ b = RT_C_TO_LOWER(b);
+ if (wc != b)
+ {
+ if (wc == '/')
+ wc = '\\';
+ if (b == '/')
+ b = '\\';
+ if (wc != b)
+ return false;
+ }
+ }
+ }
+ if (!b)
+ return true;
+ }
+}
+
+
+/**
+ * Simple case insensitive UTF-16 / ASCII path compare.
+ *
+ * @returns true if equal, false if not.
+ * @param pwszLeft The UTF-16 path string.
+ * @param pszRight The ascii string.
+ */
+static bool supHardViUtf16PathIsEqual(PCRTUTF16 pwszLeft, const char *pszRight)
+{
+ return supHardViUtf16PathIsEqualEx(pwszLeft, RTSTR_MAX, pszRight);
+}
+
+
+#if 0 /* unused */
+/**
+ * Simple case insensitive UTF-16 / ASCII ends-with path predicate.
+ *
+ * @returns true if equal, false if not.
+ * @param pwsz The UTF-16 path string.
+ * @param pszSuffix The ascii suffix string.
+ */
+static bool supHardViUtf16PathEndsWith(PCRTUTF16 pwsz, const char *pszSuffix)
+{
+ size_t cwc = RTUtf16Len(pwsz);
+ size_t cchSuffix = strlen(pszSuffix);
+ if (cwc >= cchSuffix)
+ return supHardViUtf16PathIsEqual(pwsz + cwc - cchSuffix, pszSuffix);
+ return false;
+}
+#endif
+
+
+/**
+ * Simple case insensitive UTF-16 / ASCII starts-with path predicate.
+ *
+ * @returns true if starts with given string, false if not.
+ * @param pwszLeft The UTF-16 path string.
+ * @param pszRight The ascii prefix string.
+ */
+static bool supHardViUtf16PathStartsWithAscii(PCRTUTF16 pwszLeft, const char *pszRight)
+{
+ for (;;)
+ {
+ RTUTF16 wc = *pwszLeft++;
+ uint8_t b = *pszRight++;
+ if (b != wc)
+ {
+ if (!b)
+ return true;
+ if (wc >= 0x80 || wc == 0)
+ return false;
+ wc = RT_C_TO_LOWER(wc);
+ if (wc != b)
+ {
+ b = RT_C_TO_LOWER(b);
+ if (wc != b)
+ {
+ if (wc == '/')
+ wc = '\\';
+ if (b == '/')
+ b = '\\';
+ if (wc != b)
+ return false;
+ }
+ }
+ }
+ }
+}
+
+
+/**
+ * Simple case insensitive UNICODE_STRING starts-with path predicate.
+ *
+ * @returns true if starts with given string, false if not.
+ * @param pwszLeft The path to check.
+ * @param cwcLeft The length of @a pwszLeft
+ * @param pwszRight The starts-with path.
+ * @param cwcRight The length of @a pwszRight.
+ * @param fCheckSlash Check for a slash following the prefix.
+ */
+DECLHIDDEN(bool) supHardViUtf16PathStartsWithEx(PCRTUTF16 pwszLeft, uint32_t cwcLeft,
+ PCRTUTF16 pwszRight, uint32_t cwcRight, bool fCheckSlash)
+{
+ if (cwcLeft < cwcRight || !cwcRight || !pwszRight)
+ return false;
+
+ /* See if we can get away with a case sensitive compare first. */
+ if (memcmp(pwszLeft, pwszRight, cwcRight * sizeof(RTUTF16)) == 0)
+ pwszLeft += cwcRight;
+ else
+ {
+ /* No luck, do a slow case insensitive comapre. */
+ uint32_t cLeft = cwcRight;
+ while (cLeft-- > 0)
+ {
+ RTUTF16 wcLeft = *pwszLeft++;
+ RTUTF16 wcRight = *pwszRight++;
+ if (wcLeft != wcRight)
+ {
+ wcLeft = wcLeft < 0x80 ? wcLeft == '/' ? '\\' : RT_C_TO_LOWER(wcLeft) : wcLeft;
+ wcRight = wcRight < 0x80 ? wcRight == '/' ? '\\' : RT_C_TO_LOWER(wcRight) : wcRight;
+ if (wcLeft != wcRight)
+ return false;
+ }
+ }
+ }
+
+ /* Check for slash following the prefix, if request. */
+ if ( !fCheckSlash
+ || *pwszLeft == '\\'
+ || *pwszLeft == '/')
+ return true;
+ return false;
+}
+
+
+/**
+ * Simple case insensitive UNICODE_STRING starts-with path predicate.
+ *
+ * @returns true if starts with given string, false if not.
+ * @param pUniStrLeft The path to check.
+ * @param pUniStrRight The starts-with path.
+ * @param fCheckSlash Check for a slash following the prefix.
+ */
+DECLHIDDEN(bool) supHardViUniStrPathStartsWithUniStr(UNICODE_STRING const *pUniStrLeft,
+ UNICODE_STRING const *pUniStrRight, bool fCheckSlash)
+{
+ return supHardViUtf16PathStartsWithEx(pUniStrLeft->Buffer, pUniStrLeft->Length / sizeof(WCHAR),
+ pUniStrRight->Buffer, pUniStrRight->Length / sizeof(WCHAR), fCheckSlash);
+}
+
+
+#ifndef IN_RING0
+/**
+ * Counts slashes in the given UTF-8 path string.
+ *
+ * @returns Number of slashes.
+ * @param pwsz The UTF-16 path string.
+ */
+static uint32_t supHardViUtf16PathCountSlashes(PCRTUTF16 pwsz)
+{
+ uint32_t cSlashes = 0;
+ RTUTF16 wc;
+ while ((wc = *pwsz++) != '\0')
+ if (wc == '/' || wc == '\\')
+ cSlashes++;
+ return cSlashes;
+}
+#endif
+
+
+#ifdef VBOX_PERMIT_MORE
+/**
+ * Checks if the path goes into %windir%\apppatch\.
+ *
+ * @returns true if apppatch, false if not.
+ * @param pwszPath The path to examine.
+ */
+DECLHIDDEN(bool) supHardViIsAppPatchDir(PCRTUTF16 pwszPath, uint32_t cwcName)
+{
+ uint32_t cwcWinDir = (g_System32NtPath.UniStr.Length - sizeof(L"System32")) / sizeof(WCHAR);
+
+ if (cwcName <= cwcWinDir + sizeof("AppPatch"))
+ return false;
+
+ if (memcmp(pwszPath, g_System32NtPath.UniStr.Buffer, cwcWinDir * sizeof(WCHAR)))
+ return false;
+
+ if (!supHardViUtf16PathStartsWithAscii(&pwszPath[cwcWinDir], "\\AppPatch\\"))
+ return false;
+
+ return g_uNtVerCombined >= SUP_NT_VER_VISTA;
+}
+#else
+# error should not get here..
+#endif
+
+
+
+/**
+ * Checks if the unsigned DLL is fine or not.
+ *
+ * @returns VINF_LDRVI_NOT_SIGNED or @a rc.
+ * @param hLdrMod The loader module handle.
+ * @param pwszName The NT name of the DLL/EXE.
+ * @param fFlags Flags.
+ * @param hFile The file handle.
+ * @param rc The status code..
+ */
+static int supHardNtViCheckIfNotSignedOk(RTLDRMOD hLdrMod, PCRTUTF16 pwszName, uint32_t fFlags, HANDLE hFile, int rc)
+{
+ RT_NOREF1(hLdrMod);
+
+ if (fFlags & (SUPHNTVI_F_REQUIRE_BUILD_CERT | SUPHNTVI_F_REQUIRE_KERNEL_CODE_SIGNING))
+ return rc;
+
+ /*
+ * Version macros.
+ */
+ uint32_t const uNtVer = g_uNtVerCombined;
+#define IS_XP() ( uNtVer >= SUP_MAKE_NT_VER_SIMPLE(5, 1) && uNtVer < SUP_MAKE_NT_VER_SIMPLE(5, 2) )
+#define IS_W2K3() ( uNtVer >= SUP_MAKE_NT_VER_SIMPLE(5, 2) && uNtVer < SUP_MAKE_NT_VER_SIMPLE(5, 3) )
+#define IS_VISTA() ( uNtVer >= SUP_MAKE_NT_VER_SIMPLE(6, 0) && uNtVer < SUP_MAKE_NT_VER_SIMPLE(6, 1) )
+#define IS_W70() ( uNtVer >= SUP_MAKE_NT_VER_SIMPLE(6, 1) && uNtVer < SUP_MAKE_NT_VER_SIMPLE(6, 2) )
+#define IS_W80() ( uNtVer >= SUP_MAKE_NT_VER_SIMPLE(6, 2) && uNtVer < SUP_MAKE_NT_VER_SIMPLE(6, 3) )
+#define IS_W81() ( uNtVer >= SUP_MAKE_NT_VER_SIMPLE(6, 3) && uNtVer < SUP_MAKE_NT_VER_SIMPLE(6, 4) )
+
+ /*
+ * The System32 directory.
+ *
+ * System32 is full of unsigned DLLs shipped by microsoft, graphics
+ * hardware vendors, input device/method vendors and whatnot else that
+ * actually needs to be loaded into a process for it to work correctly.
+ * We have to ASSUME that anything our process attempts to load from
+ * System32 is trustworthy and that the Windows system with the help of
+ * anti-virus software make sure there is nothing evil lurking in System32
+ * or being loaded from it.
+ *
+ * A small measure of protection is to list DLLs we know should be signed
+ * and decline loading unsigned versions of them, assuming they have been
+ * replaced by an adversary with evil intentions.
+ */
+ PCRTUTF16 pwsz;
+ uint32_t cwcName = (uint32_t)RTUtf16Len(pwszName);
+ uint32_t cwcOther = g_System32NtPath.UniStr.Length / sizeof(WCHAR);
+ if (supHardViUtf16PathStartsWithEx(pwszName, cwcName, g_System32NtPath.UniStr.Buffer, cwcOther, true /*fCheckSlash*/))
+ {
+ pwsz = pwszName + cwcOther + 1;
+
+ /* Must be owned by trusted installer. (This test is superfuous, thus no relaxation here.) */
+ if ( !(fFlags & SUPHNTVI_F_TRUSTED_INSTALLER_OWNER)
+ && !supHardNtViCheckIsOwnedByTrustedInstallerOrSimilar(hFile, pwszName))
+ return rc;
+
+ /* Core DLLs. */
+ if (supHardViUtf16PathIsEqual(pwsz, "ntdll.dll"))
+ return uNtVer < SUP_NT_VER_VISTA ? VINF_LDRVI_NOT_SIGNED : rc;
+ if (supHardViUtf16PathIsEqual(pwsz, "kernel32.dll"))
+ return uNtVer < SUP_NT_VER_W81 ? VINF_LDRVI_NOT_SIGNED : rc;
+ if (supHardViUtf16PathIsEqual(pwsz, "kernelbase.dll"))
+ return IS_W80() || IS_W70() ? VINF_LDRVI_NOT_SIGNED : rc;
+ if (supHardViUtf16PathIsEqual(pwsz, "apisetschema.dll"))
+ return IS_W70() ? VINF_LDRVI_NOT_SIGNED : rc;
+ if (supHardViUtf16PathIsEqual(pwsz, "apphelp.dll"))
+ return VINF_LDRVI_NOT_SIGNED; /* So far, never signed... */
+#ifdef VBOX_PERMIT_VERIFIER_DLL
+ if (supHardViUtf16PathIsEqual(pwsz, "verifier.dll"))
+ return uNtVer < SUP_NT_VER_W81 ? VINF_LDRVI_NOT_SIGNED : rc;
+#endif
+#ifdef VBOX_PERMIT_MORE
+ if (uNtVer >= SUP_NT_VER_W70) /* hard limit: user32.dll is unwanted prior to w7. */
+ {
+ if (supHardViUtf16PathIsEqual(pwsz, "sfc.dll"))
+ return uNtVer < SUP_MAKE_NT_VER_SIMPLE(6, 4) ? VINF_LDRVI_NOT_SIGNED : rc;
+ if (supHardViUtf16PathIsEqual(pwsz, "sfc_os.dll"))
+ return uNtVer < SUP_MAKE_NT_VER_SIMPLE(6, 4) ? VINF_LDRVI_NOT_SIGNED : rc;
+ if (supHardViUtf16PathIsEqual(pwsz, "user32.dll"))
+ return uNtVer < SUP_NT_VER_W81 ? VINF_LDRVI_NOT_SIGNED : rc;
+ }
+#endif
+
+#ifndef IN_RING0
+ /* Check that this DLL isn't supposed to be signed on this windows
+ version. If it should, it's likely to be a fake. */
+ /** @todo list of signed dlls for various windows versions. */
+ return VINF_LDRVI_NOT_SIGNED;
+#else
+ return rc;
+#endif /* IN_RING0 */
+ }
+
+
+#ifndef IN_RING0
+ /*
+ * The WinSxS white list.
+ *
+ * Just like with System32 there are potentially a number of DLLs that
+ * could be required from WinSxS.
+ */
+ cwcOther = g_WinSxSNtPath.UniStr.Length / sizeof(WCHAR);
+ if (supHardViUtf16PathStartsWithEx(pwszName, cwcName, g_WinSxSNtPath.UniStr.Buffer, cwcOther, true /*fCheckSlash*/))
+ {
+ pwsz = pwszName + cwcOther + 1;
+ cwcName -= cwcOther + 1;
+
+ /* The WinSxS layout means everything worth loading is exactly one level down. */
+ uint32_t cSlashes = supHardViUtf16PathCountSlashes(pwsz);
+ if (cSlashes != 1)
+ return rc;
+
+ /* Must be owned by trusted installer. */
+ if ( !(fFlags & SUPHNTVI_F_TRUSTED_INSTALLER_OWNER)
+ && !supHardNtViCheckIsOwnedByTrustedInstallerOrSimilar(hFile, pwszName))
+ return rc;
+ return VINF_LDRVI_NOT_SIGNED;
+ }
+#endif /* !IN_RING0 */
+
+
+#ifdef VBOX_PERMIT_MORE
+ /*
+ * AppPatch whitelist.
+ */
+ if (supHardViIsAppPatchDir(pwszName, cwcName))
+ {
+ cwcOther = g_System32NtPath.UniStr.Length / sizeof(WCHAR); /* ASSUMES System32 is called System32. */
+ pwsz = pwszName + cwcOther + 1;
+
+ if ( !(fFlags & SUPHNTVI_F_TRUSTED_INSTALLER_OWNER)
+ && !supHardNtViCheckIsOwnedByTrustedInstallerOrSimilar(hFile, pwszName))
+ return rc;
+
+# ifndef VBOX_PERMIT_EVEN_MORE
+ if (supHardViUtf16PathIsEqual(pwsz, "acres.dll"))
+ return VINF_LDRVI_NOT_SIGNED;
+
+# ifdef RT_ARCH_AMD64
+ if (supHardViUtf16PathIsEqual(pwsz, "AppPatch64\\AcGenral.dll"))
+ return VINF_LDRVI_NOT_SIGNED;
+# elif defined(RT_ARCH_X86)
+ if (supHardViUtf16PathIsEqual(pwsz, "AcGenral.dll"))
+ return VINF_LDRVI_NOT_SIGNED;
+# endif
+# endif /* !VBOX_PERMIT_EVEN_MORE */
+
+# ifdef IN_RING0
+ return rc;
+# else
+ return VINF_LDRVI_NOT_SIGNED;
+# endif
+ }
+#endif /* VBOX_PERMIT_MORE */
+
+
+#ifndef IN_RING0
+# if defined(VBOX_PERMIT_MORE) && !defined(VBOX_PERMIT_EVEN_MORE)
+ /*
+ * Program files and common files.
+ * Permit anything that's signed and correctly installed.
+ */
+ if ( supHardViUtf16PathStartsWithEx(pwszName, cwcName,
+ g_ProgramFilesNtPath.UniStr.Buffer, g_ProgramFilesNtPath.UniStr.Length,
+ true /*fCheckSlash*/)
+ || supHardViUtf16PathStartsWithEx(pwszName, cwcName,
+ g_CommonFilesNtPath.UniStr.Buffer, g_CommonFilesNtPath.UniStr.Length,
+ true /*fCheckSlash*/)
+# ifdef RT_ARCH_AMD64
+ || supHardViUtf16PathStartsWithEx(pwszName, cwcName,
+ g_ProgramFilesX86NtPath.UniStr.Buffer, g_ProgramFilesX86NtPath.UniStr.Length,
+ true /*fCheckSlash*/)
+ || supHardViUtf16PathStartsWithEx(pwszName, cwcName,
+ g_CommonFilesX86NtPath.UniStr.Buffer, g_CommonFilesX86NtPath.UniStr.Length,
+ true /*fCheckSlash*/)
+# endif
+ )
+ {
+ if ( !(fFlags & SUPHNTVI_F_TRUSTED_INSTALLER_OWNER)
+ && !supHardNtViCheckIsOwnedByTrustedInstallerOrSimilar(hFile, pwszName))
+ return rc;
+ return VINF_LDRVI_NOT_SIGNED;
+ }
+
+# elif defined(VBOX_PERMIT_MORE) && defined(VBOX_PERMIT_EVEN_MORE)
+ /*
+ * Anything that's owned by the trusted installer.
+ */
+ if ( (fFlags & SUPHNTVI_F_TRUSTED_INSTALLER_OWNER)
+ || supHardNtViCheckIsOwnedByTrustedInstallerOrSimilar(hFile, pwszName))
+ return VINF_LDRVI_NOT_SIGNED;
+
+# endif
+#endif /* !IN_RING0 */
+
+ /*
+ * Not permitted.
+ */
+ return rc;
+}
+
+
+/**
+ * @callback_method_impl{FNRTDUMPPRINTFV, Formats into RTERRINFO. }
+ */
+static DECLCALLBACK(void) supHardNtViAsn1DumpToErrInfo(void *pvUser, const char *pszFormat, va_list va)
+{
+ PRTERRINFO pErrInfo = (PRTERRINFO)pvUser;
+ RTErrInfoAddV(pErrInfo, pErrInfo->rc, pszFormat, va);
+}
+
+
+/**
+ * Attempts to locate a root certificate in the specified store.
+ *
+ * @returns IPRT status code.
+ * @retval VINF_SUCCESS if found.
+ * @retval VWRN_NOT_FOUND if not found.
+ *
+ * @param hRootStore The root certificate store to search.
+ * @param pSubject The root certificate subject.
+ * @param pPublicKeyInfo The public key of the root certificate to find.
+ */
+static int supHardNtViCertVerifyFindRootCert(RTCRSTORE hRootStore, PCRTCRX509NAME pSubject,
+ PCRTCRX509SUBJECTPUBLICKEYINFO pPublicKeyInfo)
+{
+ RTCRSTORECERTSEARCH Search;
+ int rc = RTCrStoreCertFindBySubjectOrAltSubjectByRfc5280(hRootStore, pSubject, &Search);
+ AssertRCReturn(rc, rc);
+
+ rc = VWRN_NOT_FOUND;
+ PCRTCRCERTCTX pCertCtx;
+ while ((pCertCtx = RTCrStoreCertSearchNext(hRootStore, &Search)) != NULL)
+ {
+ PCRTCRX509SUBJECTPUBLICKEYINFO pCertPubKeyInfo = NULL;
+ if (pCertCtx->pCert)
+ pCertPubKeyInfo = &pCertCtx->pCert->TbsCertificate.SubjectPublicKeyInfo;
+ else if (pCertCtx->pTaInfo)
+ pCertPubKeyInfo = &pCertCtx->pTaInfo->PubKey;
+ else
+ pCertPubKeyInfo = NULL;
+ if ( pCertPubKeyInfo
+ && RTCrX509SubjectPublicKeyInfo_Compare(pCertPubKeyInfo, pPublicKeyInfo) == 0)
+ {
+ RTCrCertCtxRelease(pCertCtx);
+ rc = VINF_SUCCESS;
+ break;
+ }
+ RTCrCertCtxRelease(pCertCtx);
+ }
+
+ int rc2 = RTCrStoreCertSearchDestroy(hRootStore, &Search);
+ AssertRC(rc2);
+ return rc;
+}
+
+
+/**
+ * @callback_method_impl{FNRTCRPKCS7VERIFYCERTCALLBACK,
+ * Standard code signing. Use this for Microsoft SPC.}
+ */
+static DECLCALLBACK(int) supHardNtViCertVerifyCallback(PCRTCRX509CERTIFICATE pCert, RTCRX509CERTPATHS hCertPaths,
+ uint32_t fFlags, void *pvUser, PRTERRINFO pErrInfo)
+{
+ PSUPHNTVIRDR pNtViRdr = (PSUPHNTVIRDR)pvUser;
+ Assert(pNtViRdr->Core.uMagic == RTLDRREADER_MAGIC);
+
+ /*
+ * If there is no certificate path build & validator associated with this
+ * callback, it must be because of the build certificate. We trust the
+ * build certificate without any second thoughts.
+ */
+ if (RTCrX509Certificate_Compare(pCert, &g_BuildX509Cert) == 0)
+ {
+#ifdef VBOX_STRICT
+ Assert(RTCrX509CertPathsGetPathCount(hCertPaths) == 1);
+ bool fTrusted = false;
+ uint32_t cNodes = UINT32_MAX;
+ int rcVerify = -1;
+ int rc = RTCrX509CertPathsQueryPathInfo(hCertPaths, 0, &fTrusted, &cNodes, NULL, NULL, NULL, NULL, &rcVerify);
+ AssertRC(rc); AssertRC(rcVerify); Assert(fTrusted); Assert(cNodes == 1);
+#endif
+ return VINF_SUCCESS;
+ }
+
+ /*
+ * Standard code signing capabilites required.
+ */
+ int rc = RTCrPkcs7VerifyCertCallbackCodeSigning(pCert, hCertPaths, fFlags, NULL, pErrInfo);
+ if ( RT_SUCCESS(rc)
+ && (fFlags & RTCRPKCS7VCC_F_SIGNED_DATA))
+ {
+ /*
+ * For kernel code signing there are two options for a valid certificate path:
+ * 1. Anchored by the microsoft kernel signing root certificate (g_hNtKernelRootStore).
+ * 2. Anchored by an SPC root and signing entity including a 1.3.6.1.4.1.311.10.3.5 (WHQL)
+ * or 1.3.6.1.4.1.311.10.3.5.1 (WHQL attestation) extended usage key.
+ */
+ if (pNtViRdr->fFlags & SUPHNTVI_F_REQUIRE_KERNEL_CODE_SIGNING)
+ {
+ uint32_t cPaths = RTCrX509CertPathsGetPathCount(hCertPaths);
+ uint32_t cFound = 0;
+ uint32_t cValid = 0;
+ for (uint32_t iPath = 0; iPath < cPaths; iPath++)
+ {
+ bool fTrusted;
+ PCRTCRX509NAME pSubject;
+ PCRTCRX509SUBJECTPUBLICKEYINFO pPublicKeyInfo;
+ int rcVerify;
+ rc = RTCrX509CertPathsQueryPathInfo(hCertPaths, iPath, &fTrusted, NULL /*pcNodes*/, &pSubject, &pPublicKeyInfo,
+ NULL, NULL /*pCertCtx*/, &rcVerify);
+ AssertRCBreak(rc);
+
+ if (RT_SUCCESS(rcVerify))
+ {
+ Assert(fTrusted);
+ cValid++;
+
+ /*
+ * 1. Search the kernel signing root store for a matching anchor.
+ */
+ rc = supHardNtViCertVerifyFindRootCert(g_hNtKernelRootStore, pSubject, pPublicKeyInfo);
+ if (rc == VINF_SUCCESS)
+ cFound++;
+ /*
+ * 2. Check for WHQL EKU and make sure it has a SPC root.
+ */
+ else if ( rc == VWRN_NOT_FOUND
+ && ( pCert->TbsCertificate.T3.fExtKeyUsage
+ & (RTCRX509CERT_EKU_F_MS_ATTEST_WHQL_CRYPTO | RTCRX509CERT_EKU_F_MS_WHQL_CRYPTO)))
+ {
+ rc = supHardNtViCertVerifyFindRootCert(g_hSpcRootStore, pSubject, pPublicKeyInfo);
+ if (rc == VINF_SUCCESS)
+ cFound++;
+ }
+ AssertRCBreak(rc);
+ }
+ }
+ if (RT_SUCCESS(rc) && cFound == 0)
+ rc = RTErrInfoSetF(pErrInfo, VERR_SUP_VP_NOT_VALID_KERNEL_CODE_SIGNATURE,
+ "Signature #%u/%u: Not valid kernel code signature.",
+ pNtViRdr->iCurSignature + 1, pNtViRdr->cTotalSignatures);
+
+
+ if (RT_SUCCESS(rc) && cValid < 2 && g_fHaveOtherRoots)
+ rc = RTErrInfoSetF(pErrInfo, VERR_SUP_VP_UNEXPECTED_VALID_PATH_COUNT,
+ "Signature #%u/%u: Expected at least %u valid paths, not %u.",
+ pNtViRdr->iCurSignature + 1, pNtViRdr->cTotalSignatures, 2, cValid);
+ if (rc == VWRN_NOT_FOUND)
+ rc = VINF_SUCCESS;
+ }
+ }
+
+ /*
+ * More requirements? NT5 build lab?
+ */
+
+ return rc;
+}
+
+
+/**
+ * RTTimeNow equivaltent that handles ring-3 where we cannot use it.
+ *
+ * @returns pNow
+ * @param pNow Where to return the current time.
+ */
+static PRTTIMESPEC supHardNtTimeNow(PRTTIMESPEC pNow)
+{
+#ifdef IN_RING3
+ /*
+ * Just read system time.
+ */
+ KUSER_SHARED_DATA volatile *pUserSharedData = (KUSER_SHARED_DATA volatile *)MM_SHARED_USER_DATA_VA;
+# ifdef RT_ARCH_AMD64
+ uint64_t uRet = *(uint64_t volatile *)&pUserSharedData->SystemTime; /* This is what KeQuerySystemTime does (missaligned). */
+ return RTTimeSpecSetNtTime(pNow, uRet);
+# else
+
+ LARGE_INTEGER NtTime;
+ do
+ {
+ NtTime.HighPart = pUserSharedData->SystemTime.High1Time;
+ NtTime.LowPart = pUserSharedData->SystemTime.LowPart;
+ } while (pUserSharedData->SystemTime.High2Time != NtTime.HighPart);
+ return RTTimeSpecSetNtTime(pNow, NtTime.QuadPart);
+# endif
+#else /* IN_RING0 */
+ return RTTimeNow(pNow);
+#endif /* IN_RING0 */
+}
+
+
+/**
+ * @callback_method_impl{FNRTLDRVALIDATESIGNEDDATA}
+ */
+static DECLCALLBACK(int) supHardNtViCallback(RTLDRMOD hLdrMod, PCRTLDRSIGNATUREINFO pInfo, PRTERRINFO pErrInfo, void *pvUser)
+{
+ RT_NOREF(hLdrMod);
+
+ /*
+ * Check out the input.
+ */
+ PSUPHNTVIRDR pNtViRdr = (PSUPHNTVIRDR)pvUser;
+ Assert(pNtViRdr->Core.uMagic == RTLDRREADER_MAGIC);
+ pNtViRdr->cTotalSignatures = pInfo->cSignatures;
+ pNtViRdr->iCurSignature = pInfo->iSignature;
+
+ AssertReturn(pInfo->enmType == RTLDRSIGNATURETYPE_PKCS7_SIGNED_DATA, VERR_INTERNAL_ERROR_5);
+ AssertReturn(!pInfo->pvExternalData, VERR_INTERNAL_ERROR_5);
+ AssertReturn(pInfo->cbSignature == sizeof(RTCRPKCS7CONTENTINFO), VERR_INTERNAL_ERROR_5);
+ PCRTCRPKCS7CONTENTINFO pContentInfo = (PCRTCRPKCS7CONTENTINFO)pInfo->pvSignature;
+ AssertReturn(RTCrPkcs7ContentInfo_IsSignedData(pContentInfo), VERR_INTERNAL_ERROR_5);
+ AssertReturn(pContentInfo->u.pSignedData->SignerInfos.cItems == 1, VERR_INTERNAL_ERROR_5);
+ PCRTCRPKCS7SIGNERINFO pSignerInfo = pContentInfo->u.pSignedData->SignerInfos.papItems[0];
+
+
+ /*
+ * If special certificate requirements, check them out before validating
+ * the signature. These only apply to the first signature (for now).
+ */
+ if ( (pNtViRdr->fFlags & SUPHNTVI_F_REQUIRE_BUILD_CERT)
+ && pInfo->iSignature == 0)
+ {
+ if (!RTCrX509Certificate_MatchIssuerAndSerialNumber(&g_BuildX509Cert,
+ &pSignerInfo->IssuerAndSerialNumber.Name,
+ &pSignerInfo->IssuerAndSerialNumber.SerialNumber))
+ return RTErrInfoSetF(pErrInfo, VERR_SUP_VP_NOT_SIGNED_WITH_BUILD_CERT,
+ "Signature #%u/%u: Not signed with the build certificate (serial %.*Rhxs, expected %.*Rhxs)",
+ pInfo->iSignature + 1, pInfo->cSignatures,
+ pSignerInfo->IssuerAndSerialNumber.SerialNumber.Asn1Core.cb,
+ pSignerInfo->IssuerAndSerialNumber.SerialNumber.Asn1Core.uData.pv,
+ g_BuildX509Cert.TbsCertificate.SerialNumber.Asn1Core.cb,
+ g_BuildX509Cert.TbsCertificate.SerialNumber.Asn1Core.uData.pv);
+ }
+
+ /*
+ * We instruction the verifier to use the signing time counter signature
+ * when present, but provides the linker time then the current time as
+ * fallbacks should the timestamp be missing or unusable.
+ *
+ * Update: Save the first timestamp we validate with build cert and
+ * use this as a minimum timestamp for further build cert
+ * validations. This works around issues with old DLLs that
+ * we sign against with our certificate (crt, sdl, qt).
+ *
+ * Update: If the validation fails, retry with the current timestamp. This
+ * is a workaround for NTDLL.DLL in build 14971 having a weird
+ * timestamp: 0xDF1E957E (Sat Aug 14 14:05:18 2088).
+ */
+ uint32_t fFlags = RTCRPKCS7VERIFY_SD_F_ALWAYS_USE_SIGNING_TIME_IF_PRESENT
+ | RTCRPKCS7VERIFY_SD_F_ALWAYS_USE_MS_TIMESTAMP_IF_PRESENT
+ | RTCRPKCS7VERIFY_SD_F_COUNTER_SIGNATURE_SIGNING_TIME_ONLY;
+
+ /* In ring-0 we don't have all the necessary timestamp server root certificate
+ * info, so we have to allow using counter signatures unverified there.
+ * Ditto for the early period of ring-3 hardened stub execution. */
+#ifndef IN_RING0
+ if (!g_fHaveOtherRoots)
+#endif
+ fFlags |= RTCRPKCS7VERIFY_SD_F_USE_SIGNING_TIME_UNVERIFIED | RTCRPKCS7VERIFY_SD_F_USE_MS_TIMESTAMP_UNVERIFIED;
+
+ /* Fallback timestamps to try: */
+ struct { RTTIMESPEC TimeSpec; const char *pszDesc; } aTimes[2];
+ unsigned cTimes = 0;
+
+ /* 1. The linking timestamp: */
+ uint64_t uTimestamp = 0;
+ int rc = RTLdrQueryProp(hLdrMod, RTLDRPROP_TIMESTAMP_SECONDS, &uTimestamp, sizeof(uTimestamp));
+ if (RT_SUCCESS(rc))
+ {
+#ifdef IN_RING3 /* Hack alert! (see above) */
+ if ( (pNtViRdr->fFlags & SUPHNTVI_F_REQUIRE_KERNEL_CODE_SIGNING)
+ && (pNtViRdr->fFlags & SUPHNTVI_F_REQUIRE_SIGNATURE_ENFORCEMENT)
+ && uTimestamp < g_uBuildTimestampHack)
+ uTimestamp = g_uBuildTimestampHack;
+#endif
+ RTTimeSpecSetSeconds(&aTimes[0].TimeSpec, uTimestamp);
+ aTimes[0].pszDesc = "link";
+ cTimes++;
+ }
+ else
+ SUP_DPRINTF(("RTLdrQueryProp/RTLDRPROP_TIMESTAMP_SECONDS failed on %s: %Rrc", pNtViRdr->szFilename, rc));
+
+ /* 2. Current time. */
+ supHardNtTimeNow(&aTimes[cTimes].TimeSpec);
+ aTimes[cTimes].pszDesc = "now";
+ cTimes++;
+
+ /* Make the verfication attempts. */
+ for (unsigned i = 0; ; i++)
+ {
+ Assert(i < cTimes);
+ rc = RTCrPkcs7VerifySignedData(pContentInfo, fFlags, g_hSpcAndNtKernelSuppStore, g_hSpcAndNtKernelRootStore,
+ &aTimes[i].TimeSpec, supHardNtViCertVerifyCallback, pNtViRdr, pErrInfo);
+ if (RT_SUCCESS(rc))
+ {
+ if (rc != VINF_SUCCESS)
+ {
+ SUP_DPRINTF(("%s: Signature #%u/%u: info status: %d\n", pNtViRdr->szFilename, pInfo->iSignature + 1, pInfo->cSignatures, rc));
+ if (pNtViRdr->rcLastSignatureFailure == VINF_SUCCESS)
+ pNtViRdr->rcLastSignatureFailure = rc;
+ }
+ pNtViRdr->cOkaySignatures++;
+
+#ifdef IN_RING3 /* Hack alert! (see above) */
+ if ((pNtViRdr->fFlags & SUPHNTVI_F_REQUIRE_BUILD_CERT) && g_uBuildTimestampHack == 0 && cTimes > 1)
+ g_uBuildTimestampHack = uTimestamp;
+#endif
+ return VINF_SUCCESS;
+ }
+
+ if (rc == VERR_CR_X509_CPV_NOT_VALID_AT_TIME && i + 1 < cTimes)
+ SUP_DPRINTF(("%s: Signature #%u/%u: VERR_CR_X509_CPV_NOT_VALID_AT_TIME for %#RX64; retrying against current time: %#RX64.\n",
+ pNtViRdr->szFilename, pInfo->iSignature + 1, pInfo->cSignatures,
+ RTTimeSpecGetSeconds(&aTimes[0].TimeSpec), RTTimeSpecGetSeconds(&aTimes[1].TimeSpec)));
+ else
+ {
+ /* There are a couple of failures we can tollerate if there are more than
+ one signature and one of them works out fine. The RTLdrVerifySignature
+ caller will have to check the failure counts though to make sure
+ something succeeded.
+
+ VERR_CR_PKCS7_KEY_USAGE_MISMATCH: Nvidia 391.35 nvldumpx.dll has an misconfigured
+ certificate "CN=NVIDIA Corporation PE Sign v2016" without valid Key Usage. It is
+ rooted by "CN=NVIDIA Subordinate CA 2016 v2,DC=nvidia,DC=com", so homebrewn.
+ Sysinternals' sigcheck util ignores it, while MS sigtool doesn't trust the root.
+ It's possible we're being too strict, but well, it's the only case so far, so no
+ need to relax the Key Usage restrictions just for a certificate w/o a trusted root.
+
+ VERR_CR_X509_CPV_UNKNOWN_CRITICAL_EXTENSION: Intel 27.20.100.9126 igdumdim64.dll
+ has three signatures, the first is signed with a certificate (C=US,ST=CA,
+ L=Santa Clara,O=Intel Corporation,CN=IntelGraphicsPE2021) that has a critical
+ subject key identifier. This used to trip up the path validator. However, the
+ other two signatures are from microsoft and checks out fine. So, in future
+ situations like this it would be nice to simply continue with the next signature.
+ See bugref{10130} for details.
+
+ VERR_SUP_VP_NOT_VALID_KERNEL_CODE_SIGNATURE: Is related to the above intel problem,
+ but this is what we get if suppressing the unknown critical subjectKeyIdentifier
+ in IPRT. We don't need all signatures to be valid kernel signatures, we should be
+ happy with just one and ignore any additional signatures as long as they don't look
+ like they've been compromised. Thus continue with this status too. */
+ pNtViRdr->rcLastSignatureFailure = rc;
+ if ( rc == VERR_CR_X509_CPV_NOT_VALID_AT_TIME
+ || rc == VERR_CR_X509_CPV_NO_TRUSTED_PATHS
+ || rc == VERR_CR_PKCS7_KEY_USAGE_MISMATCH
+ || rc == VERR_CR_X509_CPV_UNKNOWN_CRITICAL_EXTENSION
+ || rc == VERR_SUP_VP_NOT_VALID_KERNEL_CODE_SIGNATURE)
+ {
+ SUP_DPRINTF(("%s: Signature #%u/%u: %s (%d) w/ timestamp=%#RX64/%s.\n", pNtViRdr->szFilename, pInfo->iSignature + 1, pInfo->cSignatures,
+ rc == VERR_CR_X509_CPV_NOT_VALID_AT_TIME ? "VERR_CR_X509_CPV_NOT_VALID_AT_TIME"
+ : rc == VERR_CR_X509_CPV_NO_TRUSTED_PATHS ? "VERR_CR_X509_CPV_NO_TRUSTED_PATHS"
+ : rc == VERR_CR_PKCS7_KEY_USAGE_MISMATCH ? "VERR_CR_PKCS7_KEY_USAGE_MISMATCH"
+ : rc == VERR_CR_X509_CPV_UNKNOWN_CRITICAL_EXTENSION ? "VERR_CR_X509_CPV_UNKNOWN_CRITICAL_EXTENSION"
+ : "VERR_SUP_VP_NOT_VALID_KERNEL_CODE_SIGNATURE",
+ rc, RTTimeSpecGetSeconds(&aTimes[i].TimeSpec), aTimes[i].pszDesc));
+
+ /* This leniency is not applicable to build certificate requirements (signature #1 only). */
+ if ( !(pNtViRdr->fFlags & SUPHNTVI_F_REQUIRE_BUILD_CERT)
+ || pInfo->iSignature != 0)
+ {
+ pNtViRdr->cNokSignatures++;
+ rc = VINF_SUCCESS;
+ }
+ }
+ else
+ SUP_DPRINTF(("%s: Signature #%u/%u: %Rrc w/ timestamp=%#RX64/%s.\n", pNtViRdr->szFilename, pInfo->iSignature + 1, pInfo->cSignatures,
+ rc, RTTimeSpecGetSeconds(&aTimes[i].TimeSpec), aTimes[i].pszDesc));
+ return rc;
+ }
+ }
+}
+
+
+/**
+ * Verifies the given loader image.
+ *
+ * @returns IPRT status code.
+ * @param hLdrMod File handle to the executable file.
+ * @param pwszName Full NT path to the DLL in question, used for
+ * dealing with unsigned system dlls as well as for
+ * error/logging.
+ * @param pNtViRdr The reader instance /w flags.
+ * @param fAvoidWinVerifyTrust Whether to avoid WinVerifyTrust because of
+ * deadlock or other loader related dangers.
+ * @param pfWinVerifyTrust Where to return whether WinVerifyTrust was used.
+ * @param pErrInfo Pointer to error info structure. Optional.
+ */
+DECLHIDDEN(int) supHardenedWinVerifyImageByLdrMod(RTLDRMOD hLdrMod, PCRTUTF16 pwszName, PSUPHNTVIRDR pNtViRdr,
+ bool fAvoidWinVerifyTrust, bool *pfWinVerifyTrust, PRTERRINFO pErrInfo)
+{
+ if (pfWinVerifyTrust)
+ *pfWinVerifyTrust = false;
+
+#ifdef IN_RING3
+ /* Check that the caller has performed the necessary library initialization. */
+ if (!RTCrX509Certificate_IsPresent(&g_BuildX509Cert))
+ return RTErrInfoSet(pErrInfo, VERR_WRONG_ORDER,
+ "supHardenedWinVerifyImageByHandle: supHardenedWinInitImageVerifier was not called.");
+#endif
+
+ /*
+ * Check the trusted installer bit first, if requested as it's somewhat
+ * cheaper than the rest.
+ *
+ * We relax this for system32 and a little for WinSxS, like we used to, as
+ * there are apparently some systems out there where the user, admin, or
+ * someone has changed the ownership of core windows DLLs like user32.dll
+ * and comctl32.dll. Since we need user32.dll and will be checking it's
+ * digital signature, it's reasonably safe to let this thru. (The report
+ * was of SECURITY_BUILTIN_DOMAIN_RID + DOMAIN_ALIAS_RID_ADMINS
+ * owning user32.dll, see public ticket 13187, VBoxStartup.3.log.)
+ *
+ * We've also had problems with graphics driver components like ig75icd64.dll
+ * and atig6pxx.dll not being owned by TrustedInstaller, with the result
+ * that 3D got broken (mod by zero issue in test build 5). These were also
+ * SECURITY_BUILTIN_DOMAIN_RID + DOMAIN_ALIAS_RID_ADMINS.
+ *
+ * In one report by 'thor' the WinSxS resident comctl32.dll was owned by
+ * SECURITY_BUILTIN_DOMAIN_RID + DOMAIN_ALIAS_RID_ADMINS (with 4.3.16).
+ */
+ /** @todo Since we're now allowing Builtin\\Administrators after all, perhaps we
+ * could drop these system32 + winsxs hacks?? */
+ if ( (pNtViRdr->fFlags & SUPHNTVI_F_TRUSTED_INSTALLER_OWNER)
+ && !supHardNtViCheckIsOwnedByTrustedInstallerOrSimilar(pNtViRdr->hFile, pwszName))
+ {
+ if (supHardViUtf16PathStartsWithEx(pwszName, (uint32_t)RTUtf16Len(pwszName),
+ g_System32NtPath.UniStr.Buffer, g_System32NtPath.UniStr.Length / sizeof(WCHAR),
+ true /*fCheckSlash*/))
+ SUP_DPRINTF(("%ls: Relaxing the TrustedInstaller requirement for this DLL (it's in system32).\n", pwszName));
+ else if (supHardViUtf16PathStartsWithEx(pwszName, (uint32_t)RTUtf16Len(pwszName),
+ g_WinSxSNtPath.UniStr.Buffer, g_WinSxSNtPath.UniStr.Length / sizeof(WCHAR),
+ true /*fCheckSlash*/))
+ SUP_DPRINTF(("%ls: Relaxing the TrustedInstaller requirement for this DLL (it's in WinSxS).\n", pwszName));
+ else
+ return RTErrInfoSetF(pErrInfo, VERR_SUP_VP_NOT_OWNED_BY_TRUSTED_INSTALLER,
+ "supHardenedWinVerifyImageByHandle: TrustedInstaller is not the owner of '%ls'.", pwszName);
+ }
+
+ /*
+ * Verify it.
+ *
+ * The PKCS #7 SignedData signature is checked in the callback. Any
+ * signing certificate restrictions are also enforced there.
+ */
+ pNtViRdr->cOkaySignatures = 0;
+ pNtViRdr->cNokSignatures = 0;
+ pNtViRdr->cTotalSignatures = 0;
+ pNtViRdr->rcLastSignatureFailure = VINF_SUCCESS;
+ int rc = RTLdrVerifySignature(hLdrMod, supHardNtViCallback, pNtViRdr, pErrInfo);
+ if (RT_SUCCESS(rc))
+ {
+ Assert(pNtViRdr->cOkaySignatures + pNtViRdr->cNokSignatures == pNtViRdr->cTotalSignatures);
+ if ( !pNtViRdr->cOkaySignatures
+ || pNtViRdr->cOkaySignatures + pNtViRdr->cNokSignatures < pNtViRdr->cTotalSignatures /* paranoia */)
+ {
+ rc = pNtViRdr->rcLastSignatureFailure;
+ AssertStmt(RT_FAILURE_NP(rc), rc = VERR_INTERNAL_ERROR_3);
+ }
+ else if (rc == VINF_SUCCESS && RT_SUCCESS(pNtViRdr->rcLastSignatureFailure))
+ rc = pNtViRdr->rcLastSignatureFailure;
+ }
+
+ /*
+ * Microsoft doesn't sign a whole bunch of DLLs, so we have to
+ * ASSUME that a bunch of system DLLs are fine.
+ */
+ if (rc == VERR_LDRVI_NOT_SIGNED)
+ rc = supHardNtViCheckIfNotSignedOk(hLdrMod, pwszName, pNtViRdr->fFlags, pNtViRdr->hFile, rc);
+ if (RT_FAILURE(rc))
+ RTErrInfoAddF(pErrInfo, rc, ": %ls", pwszName);
+
+ /*
+ * Check for the signature checking enforcement, if requested to do so.
+ */
+ if (RT_SUCCESS(rc) && (pNtViRdr->fFlags & SUPHNTVI_F_REQUIRE_SIGNATURE_ENFORCEMENT))
+ {
+ bool fEnforced = false;
+ int rc2 = RTLdrQueryProp(hLdrMod, RTLDRPROP_SIGNATURE_CHECKS_ENFORCED, &fEnforced, sizeof(fEnforced));
+ if (RT_FAILURE(rc2))
+ rc = RTErrInfoSetF(pErrInfo, rc2, "Querying RTLDRPROP_SIGNATURE_CHECKS_ENFORCED failed on %ls: %Rrc.",
+ pwszName, rc2);
+ else if (!fEnforced)
+ rc = RTErrInfoSetF(pErrInfo, VERR_SUP_VP_SIGNATURE_CHECKS_NOT_ENFORCED,
+ "The image '%ls' was not linked with /IntegrityCheck.", pwszName);
+ }
+
+#ifdef IN_RING3
+ /*
+ * Pass it thru WinVerifyTrust when possible.
+ */
+ if (!fAvoidWinVerifyTrust)
+ rc = supHardenedWinVerifyImageTrust(pNtViRdr->hFile, pwszName, pNtViRdr->fFlags, rc, pfWinVerifyTrust, pErrInfo);
+#else
+ RT_NOREF1(fAvoidWinVerifyTrust);
+#endif
+
+ /*
+ * Check for blacklisted DLLs, both internal name and filename.
+ */
+ if (RT_SUCCESS(rc))
+ {
+ size_t const cwcName = RTUtf16Len(pwszName);
+ char szIntName[64];
+ int rc2 = RTLdrQueryProp(hLdrMod, RTLDRPROP_INTERNAL_NAME, szIntName, sizeof(szIntName));
+ if (RT_SUCCESS(rc2))
+ {
+ size_t const cchIntName = strlen(szIntName);
+ for (unsigned i = 0; g_aSupNtViBlacklistedDlls[i].psz != NULL; i++)
+ if ( cchIntName == g_aSupNtViBlacklistedDlls[i].cch
+ && RTStrICmpAscii(szIntName, g_aSupNtViBlacklistedDlls[i].psz) == 0)
+ {
+ rc = RTErrInfoSetF(pErrInfo, VERR_SUP_VP_UNDESIRABLE_MODULE,
+ "The image '%ls' is listed as undesirable.", pwszName);
+ break;
+ }
+ }
+ if (RT_SUCCESS(rc))
+ {
+ for (unsigned i = 0; g_aSupNtViBlacklistedDlls[i].psz != NULL; i++)
+ if (cwcName >= g_aSupNtViBlacklistedDlls[i].cch)
+ {
+ PCRTUTF16 pwszTmp = &pwszName[cwcName - g_aSupNtViBlacklistedDlls[i].cch];
+ if ( ( cwcName == g_aSupNtViBlacklistedDlls[i].cch
+ || pwszTmp[-1] == '\\'
+ || pwszTmp[-1] == '/')
+ && RTUtf16ICmpAscii(pwszTmp, g_aSupNtViBlacklistedDlls[i].psz) == 0)
+ {
+ rc = RTErrInfoSetF(pErrInfo, VERR_SUP_VP_UNDESIRABLE_MODULE,
+ "The image '%ls' is listed as undesirable.", pwszName);
+ break;
+ }
+ }
+ }
+ }
+
+#ifdef IN_SUP_HARDENED_R3
+ /*
+ * Hook for the LdrLoadDll code to schedule scanning of imports.
+ */
+ if (RT_SUCCESS(rc))
+ supR3HardenedWinVerifyCacheScheduleImports(hLdrMod, pwszName);
+#endif
+
+ return rc;
+}
+
+
+/**
+ * Verifies the given executable image.
+ *
+ * @returns IPRT status code.
+ * @param hFile File handle to the executable file.
+ * @param pwszName Full NT path to the DLL in question, used for
+ * dealing with unsigned system dlls as well as for
+ * error/logging.
+ * @param fFlags Flags, SUPHNTVI_F_XXX.
+ * @param fAvoidWinVerifyTrust Whether to avoid WinVerifyTrust because of
+ * deadlock or other loader related dangers.
+ * @param pfWinVerifyTrust Where to return whether WinVerifyTrust was used.
+ * @param pErrInfo Pointer to error info structure. Optional.
+ */
+DECLHIDDEN(int) supHardenedWinVerifyImageByHandle(HANDLE hFile, PCRTUTF16 pwszName, uint32_t fFlags,
+ bool fAvoidWinVerifyTrust, bool *pfWinVerifyTrust, PRTERRINFO pErrInfo)
+{
+ /*
+ * Create a reader instance.
+ */
+ PSUPHNTVIRDR pNtViRdr;
+ int rc = supHardNtViRdrCreate(hFile, pwszName, fFlags, &pNtViRdr);
+ if (RT_SUCCESS(rc))
+ {
+ /*
+ * Open the image.
+ */
+ RTLDRMOD hLdrMod;
+ RTLDRARCH enmArch = fFlags & SUPHNTVI_F_RC_IMAGE ? RTLDRARCH_X86_32 : RTLDRARCH_HOST;
+ uint32_t fLdrFlags = RTLDR_O_FOR_VALIDATION | RTLDR_O_IGNORE_ARCH_IF_NO_CODE;
+ if (fFlags & SUPHNTVI_F_IGNORE_ARCHITECTURE)
+ fLdrFlags |= RTLDR_O_IGNORE_ARCH_IF_NO_CODE;
+ rc = RTLdrOpenWithReader(&pNtViRdr->Core, fLdrFlags, enmArch, &hLdrMod, pErrInfo);
+ if (RT_SUCCESS(rc))
+ {
+ /*
+ * Verify it.
+ */
+ rc = supHardenedWinVerifyImageByLdrMod(hLdrMod, pwszName, pNtViRdr, fAvoidWinVerifyTrust, pfWinVerifyTrust, pErrInfo);
+ int rc2 = RTLdrClose(hLdrMod); AssertRC(rc2);
+ }
+ else
+ supHardNtViRdrDestroy(&pNtViRdr->Core);
+ }
+ SUP_DPRINTF(("supHardenedWinVerifyImageByHandle: -> %d (%ls)%s\n",
+ rc, pwszName, pfWinVerifyTrust && *pfWinVerifyTrust ? " WinVerifyTrust" : ""));
+ return rc;
+}
+
+
+#ifdef IN_RING3
+/**
+ * supHardenedWinVerifyImageByHandle version without the name.
+ *
+ * The name is derived from the handle.
+ *
+ * @returns IPRT status code.
+ * @param hFile File handle to the executable file.
+ * @param fFlags Flags, SUPHNTVI_F_XXX.
+ * @param pErrInfo Pointer to error info structure. Optional.
+ */
+DECLHIDDEN(int) supHardenedWinVerifyImageByHandleNoName(HANDLE hFile, uint32_t fFlags, PRTERRINFO pErrInfo)
+{
+ /*
+ * Determine the NT name and call the verification function.
+ */
+ union
+ {
+ UNICODE_STRING UniStr;
+ uint8_t abBuffer[(MAX_PATH + 8 + 1) * 2];
+ } uBuf;
+
+ ULONG cbIgn;
+ NTSTATUS rcNt = NtQueryObject(hFile,
+ ObjectNameInformation,
+ &uBuf,
+ sizeof(uBuf) - sizeof(WCHAR),
+ &cbIgn);
+ if (NT_SUCCESS(rcNt))
+ uBuf.UniStr.Buffer[uBuf.UniStr.Length / sizeof(WCHAR)] = '\0';
+ else
+ uBuf.UniStr.Buffer = (WCHAR *)L"TODO3";
+
+ return supHardenedWinVerifyImageByHandle(hFile, uBuf.UniStr.Buffer, fFlags, false /*fAvoidWinVerifyTrust*/,
+ NULL /*pfWinVerifyTrust*/, pErrInfo);
+}
+#endif /* IN_RING3 */
+
+
+/**
+ * Retrieves the full official path to the system root or one of it's sub
+ * directories.
+ *
+ * This code is also used by the support driver.
+ *
+ * @returns VBox status code.
+ * @param pvBuf The output buffer. This will contain a
+ * UNICODE_STRING followed (at the kernel's
+ * discretion) the string buffer.
+ * @param cbBuf The size of the buffer @a pvBuf points to.
+ * @param enmDir Which directory under the system root we're
+ * interested in.
+ * @param pErrInfo Pointer to error info structure. Optional.
+ */
+DECLHIDDEN(int) supHardNtGetSystemRootDir(void *pvBuf, uint32_t cbBuf, SUPHARDNTSYSROOTDIR enmDir, PRTERRINFO pErrInfo)
+{
+ HANDLE hFile = RTNT_INVALID_HANDLE_VALUE;
+ IO_STATUS_BLOCK Ios = RTNT_IO_STATUS_BLOCK_INITIALIZER;
+
+ UNICODE_STRING NtName;
+ switch (enmDir)
+ {
+ case kSupHardNtSysRootDir_System32:
+ {
+ static const WCHAR s_wszNameSystem32[] = L"\\SystemRoot\\System32\\";
+ NtName.Buffer = (PWSTR)s_wszNameSystem32;
+ NtName.Length = sizeof(s_wszNameSystem32) - sizeof(WCHAR);
+ NtName.MaximumLength = sizeof(s_wszNameSystem32);
+ break;
+ }
+ case kSupHardNtSysRootDir_WinSxS:
+ {
+ static const WCHAR s_wszNameWinSxS[] = L"\\SystemRoot\\WinSxS\\";
+ NtName.Buffer = (PWSTR)s_wszNameWinSxS;
+ NtName.Length = sizeof(s_wszNameWinSxS) - sizeof(WCHAR);
+ NtName.MaximumLength = sizeof(s_wszNameWinSxS);
+ break;
+ }
+ default:
+ AssertFailed();
+ return VERR_INVALID_PARAMETER;
+ }
+
+ OBJECT_ATTRIBUTES ObjAttr;
+ InitializeObjectAttributes(&ObjAttr, &NtName, OBJ_CASE_INSENSITIVE, NULL /*hRootDir*/, NULL /*pSecDesc*/);
+
+ NTSTATUS rcNt = NtCreateFile(&hFile,
+ FILE_READ_DATA | SYNCHRONIZE,
+ &ObjAttr,
+ &Ios,
+ NULL /* Allocation Size*/,
+ FILE_ATTRIBUTE_NORMAL,
+ FILE_SHARE_READ | FILE_SHARE_WRITE,
+ FILE_OPEN,
+ FILE_DIRECTORY_FILE | FILE_OPEN_FOR_BACKUP_INTENT | FILE_SYNCHRONOUS_IO_NONALERT,
+ NULL /*EaBuffer*/,
+ 0 /*EaLength*/);
+ if (NT_SUCCESS(rcNt))
+ rcNt = Ios.Status;
+ if (NT_SUCCESS(rcNt))
+ {
+ ULONG cbIgn;
+ rcNt = NtQueryObject(hFile,
+ ObjectNameInformation,
+ pvBuf,
+ cbBuf - sizeof(WCHAR),
+ &cbIgn);
+ NtClose(hFile);
+ if (NT_SUCCESS(rcNt))
+ {
+ PUNICODE_STRING pUniStr = (PUNICODE_STRING)pvBuf;
+ if (pUniStr->Length > 0)
+ {
+ /* Make sure it's terminated so it can safely be printed.*/
+ pUniStr->Buffer[pUniStr->Length / sizeof(WCHAR)] = '\0';
+ return VINF_SUCCESS;
+ }
+
+ return RTErrInfoSetF(pErrInfo, VERR_SUP_VP_SYSTEM32_PATH,
+ "NtQueryObject returned an empty path for '%ls'", NtName.Buffer);
+ }
+ return RTErrInfoSetF(pErrInfo, VERR_SUP_VP_SYSTEM32_PATH, "NtQueryObject failed on '%ls' dir: %#x", NtName.Buffer, rcNt);
+ }
+ return RTErrInfoSetF(pErrInfo, VERR_SUP_VP_SYSTEM32_PATH, "Failure to open '%ls': %#x", NtName.Buffer, rcNt);
+}
+
+
+/**
+ * Initialize one certificate entry.
+ *
+ * @returns VBox status code.
+ * @param pCert The X.509 certificate representation to init.
+ * @param pabCert The raw DER encoded certificate.
+ * @param cbCert The size of the raw certificate.
+ * @param pErrInfo Where to return extended error info. Optional.
+ * @param pszErrorTag Error tag.
+ */
+static int supHardNtViCertInit(PRTCRX509CERTIFICATE pCert, unsigned char const *pabCert, unsigned cbCert,
+ PRTERRINFO pErrInfo, const char *pszErrorTag)
+{
+ AssertReturn(cbCert > 16 && cbCert < _128K,
+ RTErrInfoSetF(pErrInfo, VERR_INTERNAL_ERROR_3, "%s: cbCert=%#x out of range", pszErrorTag, cbCert));
+ AssertReturn(!RTCrX509Certificate_IsPresent(pCert),
+ RTErrInfoSetF(pErrInfo, VERR_WRONG_ORDER, "%s: Certificate already decoded?", pszErrorTag));
+
+ RTASN1CURSORPRIMARY PrimaryCursor;
+ RTAsn1CursorInitPrimary(&PrimaryCursor, pabCert, cbCert, pErrInfo, &g_RTAsn1DefaultAllocator, RTASN1CURSOR_FLAGS_DER, NULL);
+ int rc = RTCrX509Certificate_DecodeAsn1(&PrimaryCursor.Cursor, 0, pCert, pszErrorTag);
+ if (RT_SUCCESS(rc))
+ rc = RTCrX509Certificate_CheckSanity(pCert, 0, pErrInfo, pszErrorTag);
+ return rc;
+}
+
+
+static int supHardNtViCertStoreAddArray(RTCRSTORE hStore, PCSUPTAENTRY paCerts, unsigned cCerts, PRTERRINFO pErrInfo)
+{
+ for (uint32_t i = 0; i < cCerts; i++)
+ {
+ int rc = RTCrStoreCertAddEncoded(hStore, RTCRCERTCTX_F_ENC_TAF_DER, paCerts[i].pch, paCerts[i].cb, pErrInfo);
+ if (RT_FAILURE(rc))
+ return rc;
+ }
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * Initialize a certificate table.
+ *
+ * @param phStore Where to return the store pointer.
+ * @param paCerts1 Pointer to the first certificate table.
+ * @param cCerts1 Entries in the first certificate table.
+ * @param paCerts2 Pointer to the second certificate table.
+ * @param cCerts2 Entries in the second certificate table.
+ * @param paCerts3 Pointer to the third certificate table.
+ * @param cCerts3 Entries in the third certificate table.
+ * @param pErrInfo Where to return extended error info. Optional.
+ * @param pszErrorTag Error tag.
+ */
+static int supHardNtViCertStoreInit(PRTCRSTORE phStore,
+ PCSUPTAENTRY paCerts1, unsigned cCerts1,
+ PCSUPTAENTRY paCerts2, unsigned cCerts2,
+ PCSUPTAENTRY paCerts3, unsigned cCerts3,
+ PRTERRINFO pErrInfo, const char *pszErrorTag)
+{
+ AssertReturn(*phStore == NIL_RTCRSTORE, VERR_WRONG_ORDER);
+ RT_NOREF1(pszErrorTag);
+
+ int rc = RTCrStoreCreateInMem(phStore, cCerts1 + cCerts2);
+ if (RT_FAILURE(rc))
+ return RTErrInfoSetF(pErrInfo, rc, "RTCrStoreCreateMemoryStore failed: %Rrc", rc);
+
+ rc = supHardNtViCertStoreAddArray(*phStore, paCerts1, cCerts1, pErrInfo);
+ if (RT_SUCCESS(rc))
+ rc = supHardNtViCertStoreAddArray(*phStore, paCerts2, cCerts2, pErrInfo);
+ if (RT_SUCCESS(rc))
+ rc = supHardNtViCertStoreAddArray(*phStore, paCerts3, cCerts3, pErrInfo);
+ return rc;
+}
+
+
+#if defined(IN_RING3) && !defined(VBOX_PERMIT_EVEN_MORE)
+/**
+ * Initializes the windows paths.
+ */
+static void supHardenedWinInitImageVerifierWinPaths(void)
+{
+ /*
+ * Windows paths that we're interested in.
+ */
+ static const struct
+ {
+ SUPSYSROOTDIRBUF *pNtPath;
+ WCHAR const *pwszRegValue;
+ const char *pszLogName;
+ } s_aPaths[] =
+ {
+ { &g_ProgramFilesNtPath, L"ProgramFilesDir", "ProgDir" },
+ { &g_CommonFilesNtPath, L"CommonFilesDir", "ComDir" },
+# ifdef RT_ARCH_AMD64
+ { &g_ProgramFilesX86NtPath, L"ProgramFilesDir (x86)", "ProgDir32" },
+ { &g_CommonFilesX86NtPath, L"CommonFilesDir (x86)", "ComDir32" },
+# endif
+ };
+
+ /*
+ * Open the registry key containing the paths.
+ */
+ UNICODE_STRING NtName = RTNT_CONSTANT_UNISTR(L"\\Registry\\Machine\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion");
+ OBJECT_ATTRIBUTES ObjAttr;
+ InitializeObjectAttributes(&ObjAttr, &NtName, OBJ_CASE_INSENSITIVE, NULL /*hRootDir*/, NULL /*pSecDesc*/);
+ HANDLE hKey;
+ NTSTATUS rcNt = NtOpenKey(&hKey, KEY_QUERY_VALUE, &ObjAttr);
+ if (NT_SUCCESS(rcNt))
+ {
+ /*
+ * Loop over the paths and resolve their NT paths.
+ */
+ for (uint32_t i = 0; i < RT_ELEMENTS(s_aPaths); i++)
+ {
+ /*
+ * Query the value first.
+ */
+ UNICODE_STRING ValueName;
+ ValueName.Buffer = (WCHAR *)s_aPaths[i].pwszRegValue;
+ ValueName.Length = (USHORT)(RTUtf16Len(s_aPaths[i].pwszRegValue) * sizeof(WCHAR));
+ ValueName.MaximumLength = ValueName.Length + sizeof(WCHAR);
+
+ union
+ {
+ KEY_VALUE_PARTIAL_INFORMATION PartialInfo;
+ uint8_t abPadding[sizeof(KEY_VALUE_PARTIAL_INFORMATION) + sizeof(WCHAR) * 128];
+ uint64_t uAlign;
+ } uBuf;
+
+ ULONG cbActual = 0;
+ rcNt = NtQueryValueKey(hKey, &ValueName, KeyValuePartialInformation, &uBuf, sizeof(uBuf) - sizeof(WCHAR), &cbActual);
+ if (NT_SUCCESS(rcNt))
+ {
+ /*
+ * Must be a simple string value, terminate it.
+ */
+ if ( uBuf.PartialInfo.Type == REG_EXPAND_SZ
+ || uBuf.PartialInfo.Type == REG_SZ)
+ {
+ /*
+ * Expand any environment variable references before opening it.
+ * We use the result buffer as storage for the expaneded path,
+ * reserving space for the windows name space prefix.
+ */
+ UNICODE_STRING Src;
+ Src.Buffer = (WCHAR *)uBuf.PartialInfo.Data;
+ Src.Length = uBuf.PartialInfo.DataLength;
+ if (Src.Length >= sizeof(WCHAR) && Src.Buffer[Src.Length / sizeof(WCHAR) - 1] == '\0')
+ Src.Length -= sizeof(WCHAR);
+ Src.MaximumLength = Src.Length + sizeof(WCHAR);
+ Src.Buffer[uBuf.PartialInfo.DataLength / sizeof(WCHAR)] = '\0';
+
+ s_aPaths[i].pNtPath->awcBuffer[0] = '\\';
+ s_aPaths[i].pNtPath->awcBuffer[1] = '?';
+ s_aPaths[i].pNtPath->awcBuffer[2] = '?';
+ s_aPaths[i].pNtPath->awcBuffer[3] = '\\';
+ UNICODE_STRING Dst;
+ Dst.Buffer = &s_aPaths[i].pNtPath->awcBuffer[4];
+ Dst.MaximumLength = sizeof(s_aPaths[i].pNtPath->awcBuffer) - sizeof(WCHAR) * 5;
+ Dst.Length = Dst.MaximumLength;
+
+ if (uBuf.PartialInfo.Type == REG_EXPAND_SZ)
+ rcNt = RtlExpandEnvironmentStrings_U(NULL, &Src, &Dst, NULL);
+ else
+ {
+ memcpy(Dst.Buffer, Src.Buffer, Src.Length);
+ Dst.Length = Src.Length;
+ }
+ if (NT_SUCCESS(rcNt))
+ {
+ Dst.Buffer[Dst.Length / sizeof(WCHAR)] = '\0';
+
+ /*
+ * Include the \\??\\ prefix in the result and open the path.
+ */
+ Dst.Buffer -= 4;
+ Dst.Length += 4 * sizeof(WCHAR);
+ Dst.MaximumLength += 4 * sizeof(WCHAR);
+ InitializeObjectAttributes(&ObjAttr, &Dst, OBJ_CASE_INSENSITIVE, NULL /*hRootDir*/, NULL /*pSecDesc*/);
+ HANDLE hFile = INVALID_HANDLE_VALUE;
+ IO_STATUS_BLOCK Ios = RTNT_IO_STATUS_BLOCK_INITIALIZER;
+ NTSTATUS rcNt = NtCreateFile(&hFile,
+ FILE_READ_DATA | SYNCHRONIZE,
+ &ObjAttr,
+ &Ios,
+ NULL /* Allocation Size*/,
+ FILE_ATTRIBUTE_NORMAL,
+ FILE_SHARE_READ | FILE_SHARE_WRITE,
+ FILE_OPEN,
+ FILE_DIRECTORY_FILE | FILE_OPEN_FOR_BACKUP_INTENT
+ | FILE_SYNCHRONOUS_IO_NONALERT,
+ NULL /*EaBuffer*/,
+ 0 /*EaLength*/);
+ if (NT_SUCCESS(rcNt))
+ rcNt = Ios.Status;
+ if (NT_SUCCESS(rcNt))
+ {
+ /*
+ * Query the real NT name.
+ */
+ ULONG cbIgn;
+ rcNt = NtQueryObject(hFile,
+ ObjectNameInformation,
+ s_aPaths[i].pNtPath,
+ sizeof(*s_aPaths[i].pNtPath) - sizeof(WCHAR),
+ &cbIgn);
+ if (NT_SUCCESS(rcNt))
+ {
+ if (s_aPaths[i].pNtPath->UniStr.Length > 0)
+ {
+ /* Make sure it's terminated.*/
+ s_aPaths[i].pNtPath->UniStr.Buffer[s_aPaths[i].pNtPath->UniStr.Length / sizeof(WCHAR)] = '\0';
+ SUP_DPRINTF(("%s:%*s %ls\n", s_aPaths[i].pszLogName, 9 - strlen(s_aPaths[i].pszLogName), "",
+ s_aPaths[i].pNtPath->UniStr.Buffer));
+ }
+ else
+ {
+ SUP_DPRINTF(("%s: NtQueryObject returned empty string\n", s_aPaths[i].pszLogName));
+ rcNt = STATUS_INVALID_PARAMETER;
+ }
+ }
+ else
+ SUP_DPRINTF(("%s: NtQueryObject failed: %#x\n", s_aPaths[i].pszLogName, rcNt));
+ NtClose(hFile);
+ }
+ else
+ SUP_DPRINTF(("%s: NtCreateFile failed: %#x (%ls)\n",
+ s_aPaths[i].pszLogName, rcNt, Dst.Buffer));
+ }
+ else
+ SUP_DPRINTF(("%s: RtlExpandEnvironmentStrings_U failed: %#x (%ls)\n",
+ s_aPaths[i].pszLogName, rcNt, Src.Buffer));
+ }
+ else
+ {
+ SUP_DPRINTF(("%s: type mismatch: %#x\n", s_aPaths[i].pszLogName, uBuf.PartialInfo.Type));
+ rcNt = STATUS_INVALID_PARAMETER;
+ }
+ }
+ else
+ SUP_DPRINTF(("%s: NtQueryValueKey failed: %#x\n", s_aPaths[i].pszLogName, rcNt));
+
+ /* Stub the entry on failure. */
+ if (!NT_SUCCESS(rcNt))
+ {
+ s_aPaths[i].pNtPath->UniStr.Length = 0;
+ s_aPaths[i].pNtPath->UniStr.Buffer = NULL;
+ }
+ }
+ NtClose(hKey);
+ }
+ else
+ {
+ SUP_DPRINTF(("NtOpenKey(%ls) failed: %#x\n", NtName.Buffer, rcNt));
+
+ /* Stub all the entries on failure. */
+ for (uint32_t i = 0; i < RT_ELEMENTS(s_aPaths); i++)
+ {
+ s_aPaths[i].pNtPath->UniStr.Length = 0;
+ s_aPaths[i].pNtPath->UniStr.Buffer = NULL;
+ }
+ }
+}
+#endif /* IN_RING3 && !VBOX_PERMIT_EVEN_MORE */
+
+
+/**
+ * This initializes the certificates globals so we don't have to reparse them
+ * every time we need to verify an image.
+ *
+ * @returns IPRT status code.
+ * @param pErrInfo Where to return extended error info. Optional.
+ */
+DECLHIDDEN(int) supHardenedWinInitImageVerifier(PRTERRINFO pErrInfo)
+{
+ AssertReturn(!RTCrX509Certificate_IsPresent(&g_BuildX509Cert), VERR_WRONG_ORDER);
+
+ /*
+ * Get the system root paths.
+ */
+ int rc = supHardNtGetSystemRootDir(&g_System32NtPath, sizeof(g_System32NtPath), kSupHardNtSysRootDir_System32, pErrInfo);
+ if (RT_SUCCESS(rc))
+ rc = supHardNtGetSystemRootDir(&g_WinSxSNtPath, sizeof(g_WinSxSNtPath), kSupHardNtSysRootDir_WinSxS, pErrInfo);
+ if (RT_SUCCESS(rc))
+ {
+ SUP_DPRINTF(("System32: %ls\n", g_System32NtPath.UniStr.Buffer));
+ SUP_DPRINTF(("WinSxS: %ls\n", g_WinSxSNtPath.UniStr.Buffer));
+#if defined(IN_RING3) && !defined(VBOX_PERMIT_EVEN_MORE)
+ supHardenedWinInitImageVerifierWinPaths();
+#endif
+
+ /*
+ * Initialize it, leaving the cleanup to the termination call.
+ */
+ rc = supHardNtViCertInit(&g_BuildX509Cert, g_abSUPBuildCert, g_cbSUPBuildCert, pErrInfo, "BuildCertificate");
+ if (RT_SUCCESS(rc))
+ rc = supHardNtViCertStoreInit(&g_hSpcRootStore, g_aSUPSpcRootTAs, g_cSUPSpcRootTAs,
+ NULL, 0, NULL, 0, pErrInfo, "SpcRoot");
+ if (RT_SUCCESS(rc))
+ rc = supHardNtViCertStoreInit(&g_hNtKernelRootStore, g_aSUPNtKernelRootTAs, g_cSUPNtKernelRootTAs,
+ NULL, 0, NULL, 0, pErrInfo, "NtKernelRoot");
+ if (RT_SUCCESS(rc))
+ rc = supHardNtViCertStoreInit(&g_hSpcAndNtKernelRootStore,
+ g_aSUPSpcRootTAs, g_cSUPSpcRootTAs,
+ g_aSUPNtKernelRootTAs, g_cSUPNtKernelRootTAs,
+ g_aSUPTimestampTAs, g_cSUPTimestampTAs,
+ pErrInfo, "SpcAndNtKernelRoot");
+ if (RT_SUCCESS(rc))
+ rc = supHardNtViCertStoreInit(&g_hSpcAndNtKernelSuppStore,
+ NULL, 0, NULL, 0, NULL, 0,
+ pErrInfo, "SpcAndNtKernelSupplemental");
+
+#if 0 /* For the time being, always trust the build certificate. It bypasses the timestamp issues of CRT and SDL. */
+ /* If the build certificate is a test singing certificate, it must be a
+ trusted root or we'll fail to validate anything. */
+ if ( RT_SUCCESS(rc)
+ && RTCrX509Name_Compare(&g_BuildX509Cert.TbsCertificate.Subject, &g_BuildX509Cert.TbsCertificate.Issuer) == 0)
+#else
+ if (RT_SUCCESS(rc))
+#endif
+ rc = RTCrStoreCertAddEncoded(g_hSpcAndNtKernelRootStore, RTCRCERTCTX_F_ENC_X509_DER,
+ g_abSUPBuildCert, g_cbSUPBuildCert, pErrInfo);
+
+ if (RT_SUCCESS(rc))
+ {
+ /*
+ * Finally initialize known SIDs that we use.
+ */
+ SID_IDENTIFIER_AUTHORITY s_NtAuth = SECURITY_NT_AUTHORITY;
+ NTSTATUS rcNt = RtlInitializeSid(&g_TrustedInstallerSid, &s_NtAuth, SECURITY_SERVICE_ID_RID_COUNT);
+ if (NT_SUCCESS(rcNt))
+ {
+ *RtlSubAuthoritySid(&g_TrustedInstallerSid, 0) = SECURITY_SERVICE_ID_BASE_RID;
+ *RtlSubAuthoritySid(&g_TrustedInstallerSid, 1) = 956008885;
+ *RtlSubAuthoritySid(&g_TrustedInstallerSid, 2) = 3418522649;
+ *RtlSubAuthoritySid(&g_TrustedInstallerSid, 3) = 1831038044;
+ *RtlSubAuthoritySid(&g_TrustedInstallerSid, 4) = 1853292631;
+ *RtlSubAuthoritySid(&g_TrustedInstallerSid, 5) = 2271478464;
+
+ rcNt = RtlInitializeSid(&g_LocalSystemSid, &s_NtAuth, 1);
+ if (NT_SUCCESS(rcNt))
+ {
+ *RtlSubAuthoritySid(&g_LocalSystemSid, 0) = SECURITY_LOCAL_SYSTEM_RID;
+
+ rcNt = RtlInitializeSid(&g_AdminsGroupSid, &s_NtAuth, 2);
+ if (NT_SUCCESS(rcNt))
+ {
+ *RtlSubAuthoritySid(&g_AdminsGroupSid, 0) = SECURITY_BUILTIN_DOMAIN_RID;
+ *RtlSubAuthoritySid(&g_AdminsGroupSid, 1) = DOMAIN_ALIAS_RID_ADMINS;
+ return VINF_SUCCESS;
+ }
+ }
+ }
+ rc = RTErrConvertFromNtStatus(rcNt);
+ }
+ supHardenedWinTermImageVerifier();
+ }
+ return rc;
+}
+
+
+/**
+ * Releases resources allocated by supHardenedWinInitImageVerifier.
+ */
+DECLHIDDEN(void) supHardenedWinTermImageVerifier(void)
+{
+ if (RTCrX509Certificate_IsPresent(&g_BuildX509Cert))
+ RTAsn1VtDelete(&g_BuildX509Cert.SeqCore.Asn1Core);
+
+ RTCrStoreRelease(g_hSpcAndNtKernelSuppStore);
+ g_hSpcAndNtKernelSuppStore = NIL_RTCRSTORE;
+ RTCrStoreRelease(g_hSpcAndNtKernelRootStore);
+ g_hSpcAndNtKernelRootStore = NIL_RTCRSTORE;
+
+ RTCrStoreRelease(g_hNtKernelRootStore);
+ g_hNtKernelRootStore = NIL_RTCRSTORE;
+ RTCrStoreRelease(g_hSpcRootStore);
+ g_hSpcRootStore = NIL_RTCRSTORE;
+}
+
+#ifdef IN_RING3
+
+/**
+ * This is a hardcoded list of certificates we thing we might need.
+ *
+ * @returns true if wanted, false if not.
+ * @param pCert The certificate.
+ */
+static bool supR3HardenedWinIsDesiredRootCA(PCRTCRX509CERTIFICATE pCert)
+{
+ char szSubject[512];
+ szSubject[sizeof(szSubject) - 1] = '\0';
+ RTCrX509Name_FormatAsString(&pCert->TbsCertificate.Subject, szSubject, sizeof(szSubject) - 1, NULL);
+
+ /*
+ * Check that it's a plausible root certificate.
+ */
+ if (!RTCrX509Certificate_IsSelfSigned(pCert))
+ {
+ SUP_DPRINTF(("supR3HardenedWinIsDesiredRootCA: skipping - not-self-signed: %s\n", szSubject));
+ return false;
+ }
+
+ if (RTAsn1Integer_UnsignedCompareWithU32(&pCert->TbsCertificate.T0.Version, 3) > 0)
+ {
+ if ( !(pCert->TbsCertificate.T3.fExtKeyUsage & RTCRX509CERT_KEY_USAGE_F_KEY_CERT_SIGN)
+ && (pCert->TbsCertificate.T3.fFlags & RTCRX509TBSCERTIFICATE_F_PRESENT_KEY_USAGE) )
+ {
+ SUP_DPRINTF(("supR3HardenedWinIsDesiredRootCA: skipping - non-cert-sign: %s\n", szSubject));
+ return false;
+ }
+ if ( pCert->TbsCertificate.T3.pBasicConstraints
+ && !pCert->TbsCertificate.T3.pBasicConstraints->CA.fValue)
+ {
+ SUP_DPRINTF(("supR3HardenedWinIsDesiredRootCA: skipping - non-CA: %s\n", szSubject));
+ return false;
+ }
+ }
+ if (pCert->TbsCertificate.SubjectPublicKeyInfo.SubjectPublicKey.cBits < 256) /* mostly for u64KeyId reading. */
+ {
+ SUP_DPRINTF(("supR3HardenedWinIsDesiredRootCA: skipping - key too small: %u bits %s\n",
+ pCert->TbsCertificate.SubjectPublicKeyInfo.SubjectPublicKey.cBits, szSubject));
+ return false;
+ }
+ uint64_t const u64KeyId = pCert->TbsCertificate.SubjectPublicKeyInfo.SubjectPublicKey.uBits.pu64[1];
+
+# if 0
+ /*
+ * Whitelist - Array of names and key clues of the certificates we want.
+ */
+ static struct
+ {
+ uint64_t u64KeyId;
+ const char *pszName;
+ } const s_aWanted[] =
+ {
+ /* SPC */
+ { UINT64_C(0xffffffffffffffff), "C=US, O=VeriSign, Inc., OU=Class 3 Public Primary Certification Authority" },
+ { UINT64_C(0xffffffffffffffff), "L=Internet, O=VeriSign, Inc., OU=VeriSign Commercial Software Publishers CA" },
+ { UINT64_C(0x491857ead79dde00), "C=US, O=The Go Daddy Group, Inc., OU=Go Daddy Class 2 Certification Authority" },
+
+ /* TS */
+ { UINT64_C(0xffffffffffffffff), "O=Microsoft Trust Network, OU=Microsoft Corporation, OU=Microsoft Time Stamping Service Root, OU=Copyright (c) 1997 Microsoft Corp." },
+ { UINT64_C(0xffffffffffffffff), "O=VeriSign Trust Network, OU=VeriSign, Inc., OU=VeriSign Time Stamping Service Root, OU=NO LIABILITY ACCEPTED, (c)97 VeriSign, Inc." },
+ { UINT64_C(0xffffffffffffffff), "C=ZA, ST=Western Cape, L=Durbanville, O=Thawte, OU=Thawte Certification, CN=Thawte Timestamping CA" },
+
+ /* Additional Windows 8.1 list: */
+ { UINT64_C(0x5ad46780fa5df300), "DC=com, DC=microsoft, CN=Microsoft Root Certificate Authority" },
+ { UINT64_C(0x3be670c1bd02a900), "OU=Copyright (c) 1997 Microsoft Corp., OU=Microsoft Corporation, CN=Microsoft Root Authority" },
+ { UINT64_C(0x4d3835aa4180b200), "C=US, ST=Washington, L=Redmond, O=Microsoft Corporation, CN=Microsoft Root Certificate Authority 2011" },
+ { UINT64_C(0x646e3fe3ba08df00), "C=US, O=MSFT, CN=Microsoft Authenticode(tm) Root Authority" },
+ { UINT64_C(0xece4e4289e08b900), "C=US, ST=Washington, L=Redmond, O=Microsoft Corporation, CN=Microsoft Root Certificate Authority 2010" },
+ { UINT64_C(0x59faf1086271bf00), "C=US, ST=Arizona, L=Scottsdale, O=GoDaddy.com, Inc., CN=Go Daddy Root Certificate Authority - G2" },
+ { UINT64_C(0x3d98ab22bb04a300), "C=IE, O=Baltimore, OU=CyberTrust, CN=Baltimore CyberTrust Root" },
+ { UINT64_C(0x91e3728b8b40d000), "C=GB, ST=Greater Manchester, L=Salford, O=COMODO CA Limited, CN=COMODO Certification Authority" },
+ { UINT64_C(0x61a3a33f81aace00), "C=US, ST=UT, L=Salt Lake City, O=The USERTRUST Network, OU=http://www.usertrust.com, CN=UTN-USERFirst-Object" },
+ { UINT64_C(0x9e5bc2d78b6a3636), "C=ZA, ST=Western Cape, L=Cape Town, O=Thawte Consulting cc, OU=Certification Services Division, CN=Thawte Premium Server CA, Email=premium-server@thawte.com" },
+ { UINT64_C(0xf4fd306318ccda00), "C=US, O=GeoTrust Inc., CN=GeoTrust Global CA" },
+ { UINT64_C(0xa0ee62086758b15d), "C=US, O=Equifax, OU=Equifax Secure Certificate Authority" },
+ { UINT64_C(0x8ff6fc03c1edbd00), "C=US, ST=Arizona, L=Scottsdale, O=Starfield Technologies, Inc., CN=Starfield Root Certificate Authority - G2" },
+ { UINT64_C(0xa3ce8d99e60eda00), "C=BE, O=GlobalSign nv-sa, OU=Root CA, CN=GlobalSign Root CA" },
+ { UINT64_C(0xa671e9fec832b700), "C=US, O=Starfield Technologies, Inc., OU=Starfield Class 2 Certification Authority" },
+ { UINT64_C(0xa8de7211e13be200), "C=US, O=DigiCert Inc, OU=www.digicert.com, CN=DigiCert Global Root CA" },
+ { UINT64_C(0x0ff3891b54348328), "C=US, O=Entrust.net, OU=www.entrust.net/CPS incorp. by ref. (limits liab.), OU=(c) 1999 Entrust.net Limited, CN=Entrust.netSecure Server Certification Authority" },
+ { UINT64_C(0x7ae89c50f0b6a00f), "C=US, O=GTE Corporation, OU=GTE CyberTrust Solutions, Inc., CN=GTE CyberTrust Global Root" },
+ { UINT64_C(0xd45980fbf0a0ac00), "C=US, O=thawte, Inc., OU=Certification Services Division, OU=(c) 2006 thawte, Inc. - For authorized use only, CN=thawte Primary Root CA" },
+ { UINT64_C(0x9e5bc2d78b6a3636), "C=ZA, ST=Western Cape, L=Cape Town, O=Thawte Consulting cc, OU=Certification Services Division, CN=Thawte Premium Server CA, Email=premium-server@thawte.com" },
+ { UINT64_C(0x7c4fd32ec1b1ce00), "C=PL, O=Unizeto Sp. z o.o., CN=Certum CA" },
+ { UINT64_C(0xd4fbe673e5ccc600), "C=US, O=DigiCert Inc, OU=www.digicert.com, CN=DigiCert High Assurance EV Root CA" },
+ { UINT64_C(0x16e64d2a56ccf200), "C=US, ST=Arizona, L=Scottsdale, O=Starfield Technologies, Inc., OU=http://certificates.starfieldtech.com/repository/, CN=Starfield Services Root Certificate Authority" },
+ { UINT64_C(0x6e2ba21058eedf00), "C=US, ST=UT, L=Salt Lake City, O=The USERTRUST Network, OU=http://www.usertrust.com, CN=UTN - DATACorp SGC" },
+ { UINT64_C(0xb28612a94b4dad00), "O=Entrust.net, OU=www.entrust.net/CPS_2048 incorp. by ref. (limits liab.), OU=(c) 1999 Entrust.net Limited, CN=Entrust.netCertification Authority (2048)" },
+ { UINT64_C(0x357a29080824af00), "C=US, O=VeriSign, Inc., OU=VeriSign Trust Network, OU=(c) 2006 VeriSign, Inc. - For authorized use only, CN=VeriSign Class3 Public Primary Certification Authority - G5" },
+ { UINT64_C(0x466cbc09db88c100), "C=IL, O=StartCom Ltd., OU=Secure Digital Certificate Signing, CN=StartCom Certification Authority" },
+ { UINT64_C(0x9259c8abe5ca713a), "L=ValiCert Validation Network, O=ValiCert, Inc., OU=ValiCert Class 2 Policy Validation Authority, CN=http://www.valicert.com/, Email=info@valicert.com" },
+ { UINT64_C(0x1f78fc529cbacb00), "C=US, O=VeriSign, Inc., OU=VeriSign Trust Network, OU=(c) 1999 VeriSign, Inc. - For authorized use only, CN=VeriSign Class3 Public Primary Certification Authority - G3" },
+ { UINT64_C(0x8043e4ce150ead00), "C=US, O=DigiCert Inc, OU=www.digicert.com, CN=DigiCert Assured ID Root CA" },
+ { UINT64_C(0x00f2e6331af7b700), "C=SE, O=AddTrust AB, OU=AddTrust External TTP Network, CN=AddTrust External CA Root" },
+ };
+
+
+ uint32_t i = RT_ELEMENTS(s_aWanted);
+ while (i-- > 0)
+ if ( s_aWanted[i].u64KeyId == u64KeyId
+ || s_aWanted[i].u64KeyId == UINT64_MAX)
+ if (RTCrX509Name_MatchWithString(&pCert->TbsCertificate.Subject, s_aWanted[i].pszName))
+ {
+ SUP_DPRINTF(("supR3HardenedWinIsDesiredRootCA: Adding %#llx %s\n", u64KeyId, szSubject));
+ return true;
+ }
+
+ SUP_DPRINTF(("supR3HardenedWinIsDesiredRootCA: skipping %#llx %s\n", u64KeyId, szSubject));
+ return false;
+# else
+ /*
+ * Blacklist approach.
+ */
+ static struct
+ {
+ uint64_t u64KeyId;
+ const char *pszName;
+ } const s_aUnwanted[] =
+ {
+ { UINT64_C(0xffffffffffffffff), "C=US, O=U.S. Robots and Mechanical Men, Inc., OU=V.I.K.I." }, /* dummy entry */
+ };
+
+ uint32_t i = RT_ELEMENTS(s_aUnwanted);
+ while (i-- > 0)
+ if ( s_aUnwanted[i].u64KeyId == u64KeyId
+ || s_aUnwanted[i].u64KeyId == UINT64_MAX)
+ if (RTCrX509Name_MatchWithString(&pCert->TbsCertificate.Subject, s_aUnwanted[i].pszName))
+ {
+ SUP_DPRINTF(("supR3HardenedWinIsDesiredRootCA: skipping - blacklisted: %#llx %s\n", u64KeyId, szSubject));
+ return false;
+ }
+
+ SUP_DPRINTF(("supR3HardenedWinIsDesiredRootCA: Adding %#llx %s\n", u64KeyId, szSubject));
+ return true;
+# endif
+}
+
+
+/**
+ * Loads a module in the system32 directory.
+ *
+ * @returns Module handle on success. Won't return on failure if fMandatory = true.
+ * @param pszName The name of the DLL to load.
+ * @param fMandatory Whether the library is mandatory.
+ */
+DECLHIDDEN(HMODULE) supR3HardenedWinLoadSystem32Dll(const char *pszName, bool fMandatory)
+{
+ WCHAR wszName[200+60];
+ UINT cwcDir = GetSystemDirectoryW(wszName, RT_ELEMENTS(wszName) - 60);
+ wszName[cwcDir] = '\\';
+ RTUtf16CopyAscii(&wszName[cwcDir + 1], RT_ELEMENTS(wszName) - cwcDir, pszName);
+
+ DWORD fFlags = 0;
+ if (g_uNtVerCombined >= SUP_MAKE_NT_VER_SIMPLE(6, 0))
+ fFlags = LOAD_LIBRARY_SEARCH_SYSTEM32;
+ HMODULE hMod = LoadLibraryExW(wszName, NULL, fFlags);
+ if ( hMod == NULL
+ && fFlags
+ && g_uNtVerCombined < SUP_MAKE_NT_VER_SIMPLE(6, 2)
+ && RtlGetLastWin32Error() == ERROR_INVALID_PARAMETER)
+ {
+ fFlags = 0;
+ hMod = LoadLibraryExW(wszName, NULL, fFlags);
+ }
+ if ( hMod == NULL
+ && fMandatory)
+ supR3HardenedFatal("Error loading '%s': %u [%ls]", pszName, RtlGetLastWin32Error(), wszName);
+ return hMod;
+}
+
+
+/**
+ * Called by supR3HardenedWinResolveVerifyTrustApiAndHookThreadCreation to
+ * import selected root CAs from the system certificate store.
+ *
+ * These certificates permits us to correctly validate third party DLLs.
+ */
+static void supR3HardenedWinRetrieveTrustedRootCAs(void)
+{
+ uint32_t cAdded = 0;
+
+ /*
+ * Load crypt32.dll and resolve the APIs we need.
+ */
+ HMODULE hCrypt32 = supR3HardenedWinLoadSystem32Dll("crypt32.dll", true /*fMandatory*/);
+
+#define RESOLVE_CRYPT32_API(a_Name, a_pfnType) \
+ a_pfnType pfn##a_Name = (a_pfnType)GetProcAddress(hCrypt32, #a_Name); \
+ if (pfn##a_Name == NULL) supR3HardenedFatal("Error locating '" #a_Name "' in 'crypt32.dll': %u", RtlGetLastWin32Error())
+ RESOLVE_CRYPT32_API(CertOpenStore, PFNCERTOPENSTORE);
+ RESOLVE_CRYPT32_API(CertCloseStore, PFNCERTCLOSESTORE);
+ RESOLVE_CRYPT32_API(CertEnumCertificatesInStore, PFNCERTENUMCERTIFICATESINSTORE);
+#undef RESOLVE_CRYPT32_API
+
+ /*
+ * Open the root store and look for the certificates we wish to use.
+ */
+ DWORD fOpenStore = CERT_STORE_OPEN_EXISTING_FLAG | CERT_STORE_READONLY_FLAG;
+ HCERTSTORE hStore = pfnCertOpenStore(CERT_STORE_PROV_SYSTEM_W, PKCS_7_ASN_ENCODING | X509_ASN_ENCODING,
+ NULL /* hCryptProv = default */, CERT_SYSTEM_STORE_LOCAL_MACHINE | fOpenStore, L"Root");
+ if (!hStore)
+ hStore = pfnCertOpenStore(CERT_STORE_PROV_SYSTEM_W, PKCS_7_ASN_ENCODING | X509_ASN_ENCODING,
+ NULL /* hCryptProv = default */, CERT_SYSTEM_STORE_CURRENT_USER | fOpenStore, L"Root");
+ if (hStore)
+ {
+ PCCERT_CONTEXT pCurCtx = NULL;
+ while ((pCurCtx = pfnCertEnumCertificatesInStore(hStore, pCurCtx)) != NULL)
+ {
+ if (pCurCtx->dwCertEncodingType & X509_ASN_ENCODING)
+ {
+ RTERRINFOSTATIC StaticErrInfo;
+ RTASN1CURSORPRIMARY PrimaryCursor;
+ RTAsn1CursorInitPrimary(&PrimaryCursor, pCurCtx->pbCertEncoded, pCurCtx->cbCertEncoded,
+ RTErrInfoInitStatic(&StaticErrInfo),
+ &g_RTAsn1DefaultAllocator, RTASN1CURSOR_FLAGS_DER, "CurCtx");
+ RTCRX509CERTIFICATE MyCert;
+ int rc = RTCrX509Certificate_DecodeAsn1(&PrimaryCursor.Cursor, 0, &MyCert, "Cert");
+ if (RT_SUCCESS(rc))
+ {
+ if (supR3HardenedWinIsDesiredRootCA(&MyCert))
+ {
+ rc = RTCrStoreCertAddEncoded(g_hSpcRootStore, RTCRCERTCTX_F_ENC_X509_DER,
+ pCurCtx->pbCertEncoded, pCurCtx->cbCertEncoded, NULL /*pErrInfo*/);
+ AssertRC(rc);
+
+ rc = RTCrStoreCertAddEncoded(g_hSpcAndNtKernelRootStore, RTCRCERTCTX_F_ENC_X509_DER,
+ pCurCtx->pbCertEncoded, pCurCtx->cbCertEncoded, NULL /*pErrInfo*/);
+ AssertRC(rc);
+ cAdded++;
+ }
+
+ RTCrX509Certificate_Delete(&MyCert);
+ }
+ /* XP root certificate "C&W HKT SecureNet CA SGC Root" has non-standard validity
+ timestamps, the UTC formatting isn't Zulu time but specifies timezone offsets.
+ Ignore these failures and certificates. */
+ else if (rc != VERR_ASN1_INVALID_UTC_TIME_ENCODING)
+ AssertMsgFailed(("RTCrX509Certificate_DecodeAsn1 failed: rc=%#x: %s\n", rc, StaticErrInfo.szMsg));
+ }
+ }
+ pfnCertCloseStore(hStore, CERT_CLOSE_STORE_CHECK_FLAG);
+ g_fHaveOtherRoots = true;
+ }
+ SUP_DPRINTF(("supR3HardenedWinRetrieveTrustedRootCAs: cAdded=%u\n", cAdded));
+}
+
+
+/**
+ * Resolves the WinVerifyTrust API after the process has been verified and
+ * installs a thread creation hook.
+ *
+ * The WinVerifyTrust API is used in addition our own Authenticode verification
+ * code. If the image has the IMAGE_DLLCHARACTERISTICS_FORCE_INTEGRITY flag
+ * set, it will be checked again by the kernel. All our image has this flag set
+ * and we require all VBox extensions to have it set as well. In effect, the
+ * authenticode signature will be checked two or three times.
+ *
+ * @param pszProgName The program name.
+ */
+DECLHIDDEN(void) supR3HardenedWinResolveVerifyTrustApiAndHookThreadCreation(const char *pszProgName)
+{
+# ifdef IN_SUP_HARDENED_R3
+ /*
+ * Load our the support library DLL that does the thread hooking as the
+ * security API may trigger the creation of COM worker threads (or
+ * whatever they are).
+ *
+ * The thread creation hook makes the threads very slippery to debuggers by
+ * irreversably disabling most (if not all) debug events for them.
+ */
+ char szPath[RTPATH_MAX];
+ supR3HardenedPathAppSharedLibs(szPath, sizeof(szPath) - sizeof("/VBoxSupLib.DLL"));
+ suplibHardenedStrCat(szPath, "/VBoxSupLib.DLL");
+ HMODULE hSupLibMod = (HMODULE)supR3HardenedWinLoadLibrary(szPath, true /*fSystem32Only*/, 0 /*fMainFlags*/);
+ if (hSupLibMod == NULL)
+ supR3HardenedFatal("Error loading '%s': %u", szPath, RtlGetLastWin32Error());
+# endif
+
+ /*
+ * Allocate TLS entry for WinVerifyTrust recursion prevention.
+ */
+ DWORD iTls = TlsAlloc();
+ if (iTls != TLS_OUT_OF_INDEXES)
+ g_iTlsWinVerifyTrustRecursion = iTls;
+ else
+ supR3HardenedError(RtlGetLastWin32Error(), false /*fFatal*/, "TlsAlloc failed");
+
+ /*
+ * Resolve the imports we need.
+ */
+ HMODULE hWintrust = supR3HardenedWinLoadSystem32Dll("Wintrust.dll", true /*fMandatory*/);
+#define RESOLVE_CRYPT_API(a_Name, a_pfnType, a_uMinWinVer) \
+ do { \
+ g_pfn##a_Name = (a_pfnType)GetProcAddress(hWintrust, #a_Name); \
+ if (g_pfn##a_Name == NULL && (a_uMinWinVer) < g_uNtVerCombined) \
+ supR3HardenedFatal("Error locating '" #a_Name "' in 'Wintrust.dll': %u", RtlGetLastWin32Error()); \
+ } while (0)
+
+ PFNWINVERIFYTRUST pfnWinVerifyTrust = (PFNWINVERIFYTRUST)GetProcAddress(hWintrust, "WinVerifyTrust");
+ if (!pfnWinVerifyTrust)
+ supR3HardenedFatal("Error locating 'WinVerifyTrust' in 'Wintrust.dll': %u", RtlGetLastWin32Error());
+
+ RESOLVE_CRYPT_API(CryptCATAdminAcquireContext, PFNCRYPTCATADMINACQUIRECONTEXT, 0);
+ RESOLVE_CRYPT_API(CryptCATAdminCalcHashFromFileHandle, PFNCRYPTCATADMINCALCHASHFROMFILEHANDLE, 0);
+ RESOLVE_CRYPT_API(CryptCATAdminEnumCatalogFromHash, PFNCRYPTCATADMINENUMCATALOGFROMHASH, 0);
+ RESOLVE_CRYPT_API(CryptCATAdminReleaseCatalogContext, PFNCRYPTCATADMINRELEASECATALOGCONTEXT, 0);
+ RESOLVE_CRYPT_API(CryptCATAdminReleaseContext, PFNCRYPTCATDADMINRELEASECONTEXT, 0);
+ RESOLVE_CRYPT_API(CryptCATCatalogInfoFromContext, PFNCRYPTCATCATALOGINFOFROMCONTEXT, 0);
+
+ RESOLVE_CRYPT_API(CryptCATAdminAcquireContext2, PFNCRYPTCATADMINACQUIRECONTEXT2, SUP_NT_VER_W80);
+ RESOLVE_CRYPT_API(CryptCATAdminCalcHashFromFileHandle2, PFNCRYPTCATADMINCALCHASHFROMFILEHANDLE2, SUP_NT_VER_W80);
+
+# ifdef IN_SUP_HARDENED_R3
+ /*
+ * Load bcrypt.dll and instantiate a few hashing and signing providers to
+ * make sure the providers are cached for later us. Avoid recursion issues.
+ */
+ HMODULE hBCrypt = supR3HardenedWinLoadSystem32Dll("bcrypt.dll", false /*fMandatory*/);
+ if (hBCrypt)
+ {
+ PFNBCRYPTOPENALGORTIHMPROVIDER pfnOpenAlgoProvider;
+ pfnOpenAlgoProvider = (PFNBCRYPTOPENALGORTIHMPROVIDER)GetProcAddress(hBCrypt, "BCryptOpenAlgorithmProvider");
+ if (pfnOpenAlgoProvider)
+ {
+ SUP_DPRINTF(("bcrypt.dll loaded at %p, BCryptOpenAlgorithmProvider at %p, preloading providers:\n",
+ hBCrypt, pfnOpenAlgoProvider));
+# define PRELOAD_ALGO_PROVIDER(a_Name) \
+ do { \
+ BCRYPT_ALG_HANDLE hAlgo = NULL; \
+ NTSTATUS rcNt = pfnOpenAlgoProvider(&hAlgo, a_Name, NULL, 0); \
+ SUP_DPRINTF(("%sBCryptOpenAlgorithmProvider(,'%ls',0,0) -> %#x (hAlgo=%p)\n", \
+ NT_SUCCESS(rcNt) ? " " : "warning: ", a_Name, rcNt, hAlgo)); \
+ } while (0)
+ PRELOAD_ALGO_PROVIDER(BCRYPT_MD2_ALGORITHM);
+ PRELOAD_ALGO_PROVIDER(BCRYPT_MD4_ALGORITHM);
+ PRELOAD_ALGO_PROVIDER(BCRYPT_MD5_ALGORITHM);
+ PRELOAD_ALGO_PROVIDER(BCRYPT_SHA1_ALGORITHM);
+ PRELOAD_ALGO_PROVIDER(BCRYPT_SHA256_ALGORITHM);
+ PRELOAD_ALGO_PROVIDER(BCRYPT_SHA512_ALGORITHM);
+ PRELOAD_ALGO_PROVIDER(BCRYPT_RSA_ALGORITHM);
+ PRELOAD_ALGO_PROVIDER(BCRYPT_DSA_ALGORITHM);
+# undef PRELOAD_ALGO_PROVIDER
+ }
+ else
+ SUP_DPRINTF(("Warning! Failed to find BCryptOpenAlgorithmProvider in bcrypt.dll\n"));
+ }
+ else
+ SUP_DPRINTF(("Warning! Failed to load bcrypt.dll\n"));
+
+ /*
+ * Call the verification API on ourselves and ntdll to make sure it works
+ * and loads more stuff it needs, preventing any recursive fun we'd run
+ * into after we set g_pfnWinVerifyTrust.
+ */
+ RTERRINFOSTATIC ErrInfoStatic;
+ RTErrInfoInitStatic(&ErrInfoStatic);
+ int rc = supR3HardNtViCallWinVerifyTrust(NULL, g_SupLibHardenedExeNtPath.UniStr.Buffer, 0,
+ &ErrInfoStatic.Core, pfnWinVerifyTrust, NULL);
+ if (RT_FAILURE(rc))
+ supR3HardenedFatalMsg(pszProgName, kSupInitOp_Integrity, rc,
+ "WinVerifyTrust failed on stub executable: %s", ErrInfoStatic.szMsg);
+# else
+ RT_NOREF1(pszProgName);
+# endif
+
+ if (g_uNtVerCombined >= SUP_MAKE_NT_VER_SIMPLE(6, 0)) /* ntdll isn't signed on XP, assuming this is the case on W2K3 for now. */
+ supR3HardNtViCallWinVerifyTrust(NULL, L"\\SystemRoot\\System32\\ntdll.dll", 0, NULL, pfnWinVerifyTrust, NULL);
+ supR3HardNtViCallWinVerifyTrustCatFile(NULL, L"\\SystemRoot\\System32\\ntdll.dll", 0, NULL, pfnWinVerifyTrust);
+
+ g_pfnWinVerifyTrust = pfnWinVerifyTrust;
+ SUP_DPRINTF(("g_pfnWinVerifyTrust=%p\n", pfnWinVerifyTrust));
+
+# ifdef IN_SUP_HARDENED_R3
+ /*
+ * Load some problematic DLLs into the verifier cache to prevent
+ * recursion trouble.
+ */
+ supR3HardenedWinVerifyCachePreload(L"\\SystemRoot\\System32\\crypt32.dll");
+ supR3HardenedWinVerifyCachePreload(L"\\SystemRoot\\System32\\Wintrust.dll");
+# endif
+
+ /*
+ * Now, get trusted root CAs so we can verify a broader scope of signatures.
+ */
+ supR3HardenedWinRetrieveTrustedRootCAs();
+}
+
+
+static int supR3HardNtViNtToWinPath(PCRTUTF16 pwszNtName, PCRTUTF16 *ppwszWinPath,
+ PRTUTF16 pwszWinPathBuf, size_t cwcWinPathBuf)
+{
+ static const RTUTF16 s_wszPrefix[] = L"\\\\.\\GLOBALROOT";
+
+ if (*pwszNtName != '\\' && *pwszNtName != '/')
+ return VERR_PATH_DOES_NOT_START_WITH_ROOT;
+
+ size_t cwcNtName = RTUtf16Len(pwszNtName);
+ if (RT_ELEMENTS(s_wszPrefix) + cwcNtName > cwcWinPathBuf)
+ return VERR_FILENAME_TOO_LONG;
+
+ memcpy(pwszWinPathBuf, s_wszPrefix, sizeof(s_wszPrefix));
+ memcpy(&pwszWinPathBuf[sizeof(s_wszPrefix) / sizeof(RTUTF16) - 1], pwszNtName, (cwcNtName + 1) * sizeof(RTUTF16));
+ *ppwszWinPath = pwszWinPathBuf;
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * Calls WinVerifyTrust to verify an PE image.
+ *
+ * @returns VBox status code.
+ * @param hFile File handle to the executable file.
+ * @param pwszName Full NT path to the DLL in question, used for
+ * dealing with unsigned system dlls as well as for
+ * error/logging.
+ * @param fFlags Flags, SUPHNTVI_F_XXX.
+ * @param pErrInfo Pointer to error info structure. Optional.
+ * @param pfnWinVerifyTrust Pointer to the API.
+ * @param phrcWinVerifyTrust Where to WinVerifyTrust error status on failure,
+ * optional.
+ */
+static int supR3HardNtViCallWinVerifyTrust(HANDLE hFile, PCRTUTF16 pwszName, uint32_t fFlags, PRTERRINFO pErrInfo,
+ PFNWINVERIFYTRUST pfnWinVerifyTrust, HRESULT *phrcWinVerifyTrust)
+{
+ RT_NOREF1(fFlags);
+ if (phrcWinVerifyTrust)
+ *phrcWinVerifyTrust = S_OK;
+
+ /*
+ * Convert the name into a Windows name.
+ */
+ RTUTF16 wszWinPathBuf[MAX_PATH];
+ PCRTUTF16 pwszWinPath;
+ int rc = supR3HardNtViNtToWinPath(pwszName, &pwszWinPath, wszWinPathBuf, RT_ELEMENTS(wszWinPathBuf));
+ if (RT_FAILURE(rc))
+ return RTErrInfoSetF(pErrInfo, rc, "Bad path passed to supR3HardNtViCallWinVerifyTrust: rc=%Rrc '%ls'", rc, pwszName);
+
+ /*
+ * Construct input parameters and call the API.
+ */
+ WINTRUST_FILE_INFO FileInfo;
+ RT_ZERO(FileInfo);
+ FileInfo.cbStruct = sizeof(FileInfo);
+ FileInfo.pcwszFilePath = pwszWinPath;
+ FileInfo.hFile = hFile;
+
+ GUID PolicyActionGuid = WINTRUST_ACTION_GENERIC_VERIFY_V2;
+
+ WINTRUST_DATA TrustData;
+ RT_ZERO(TrustData);
+ TrustData.cbStruct = sizeof(TrustData);
+ TrustData.fdwRevocationChecks = WTD_REVOKE_NONE; /* Keep simple for now. */
+ TrustData.dwStateAction = WTD_STATEACTION_VERIFY;
+ TrustData.dwUIChoice = WTD_UI_NONE;
+ TrustData.dwProvFlags = 0;
+ if (g_uNtVerCombined >= SUP_MAKE_NT_VER_SIMPLE(6, 0))
+ TrustData.dwProvFlags = WTD_CACHE_ONLY_URL_RETRIEVAL;
+ else
+ TrustData.dwProvFlags = WTD_REVOCATION_CHECK_NONE;
+ TrustData.dwUnionChoice = WTD_CHOICE_FILE;
+ TrustData.pFile = &FileInfo;
+
+ HRESULT hrc = pfnWinVerifyTrust(NULL /*hwnd*/, &PolicyActionGuid, &TrustData);
+# ifdef DEBUG_bird /* TEMP HACK */
+ if (hrc == CERT_E_EXPIRED)
+ hrc = S_OK;
+# endif
+ if (hrc == S_OK)
+ rc = VINF_SUCCESS;
+ else
+ {
+ /*
+ * Failed. Format a nice error message.
+ */
+# ifdef DEBUG_bird
+ if (hrc != CERT_E_CHAINING /* Un-updated vistas, XPs, ++ */)
+ __debugbreak();
+# endif
+ const char *pszErrConst = NULL;
+ switch (hrc)
+ {
+ case TRUST_E_SYSTEM_ERROR: pszErrConst = "TRUST_E_SYSTEM_ERROR"; break;
+ case TRUST_E_NO_SIGNER_CERT: pszErrConst = "TRUST_E_NO_SIGNER_CERT"; break;
+ case TRUST_E_COUNTER_SIGNER: pszErrConst = "TRUST_E_COUNTER_SIGNER"; break;
+ case TRUST_E_CERT_SIGNATURE: pszErrConst = "TRUST_E_CERT_SIGNATURE"; break;
+ case TRUST_E_TIME_STAMP: pszErrConst = "TRUST_E_TIME_STAMP"; break;
+ case TRUST_E_BAD_DIGEST: pszErrConst = "TRUST_E_BAD_DIGEST"; break;
+ case TRUST_E_BASIC_CONSTRAINTS: pszErrConst = "TRUST_E_BASIC_CONSTRAINTS"; break;
+ case TRUST_E_FINANCIAL_CRITERIA: pszErrConst = "TRUST_E_FINANCIAL_CRITERIA"; break;
+ case TRUST_E_PROVIDER_UNKNOWN: pszErrConst = "TRUST_E_PROVIDER_UNKNOWN"; break;
+ case TRUST_E_ACTION_UNKNOWN: pszErrConst = "TRUST_E_ACTION_UNKNOWN"; break;
+ case TRUST_E_SUBJECT_FORM_UNKNOWN: pszErrConst = "TRUST_E_SUBJECT_FORM_UNKNOWN"; break;
+ case TRUST_E_SUBJECT_NOT_TRUSTED: pszErrConst = "TRUST_E_SUBJECT_NOT_TRUSTED"; break;
+ case TRUST_E_NOSIGNATURE: pszErrConst = "TRUST_E_NOSIGNATURE"; break;
+ case TRUST_E_FAIL: pszErrConst = "TRUST_E_FAIL"; break;
+ case TRUST_E_EXPLICIT_DISTRUST: pszErrConst = "TRUST_E_EXPLICIT_DISTRUST"; break;
+ case CERT_E_EXPIRED: pszErrConst = "CERT_E_EXPIRED"; break;
+ case CERT_E_VALIDITYPERIODNESTING: pszErrConst = "CERT_E_VALIDITYPERIODNESTING"; break;
+ case CERT_E_ROLE: pszErrConst = "CERT_E_ROLE"; break;
+ case CERT_E_PATHLENCONST: pszErrConst = "CERT_E_PATHLENCONST"; break;
+ case CERT_E_CRITICAL: pszErrConst = "CERT_E_CRITICAL"; break;
+ case CERT_E_PURPOSE: pszErrConst = "CERT_E_PURPOSE"; break;
+ case CERT_E_ISSUERCHAINING: pszErrConst = "CERT_E_ISSUERCHAINING"; break;
+ case CERT_E_MALFORMED: pszErrConst = "CERT_E_MALFORMED"; break;
+ case CERT_E_UNTRUSTEDROOT: pszErrConst = "CERT_E_UNTRUSTEDROOT"; break;
+ case CERT_E_CHAINING: pszErrConst = "CERT_E_CHAINING"; break;
+ case CERT_E_REVOKED: pszErrConst = "CERT_E_REVOKED"; break;
+ case CERT_E_UNTRUSTEDTESTROOT: pszErrConst = "CERT_E_UNTRUSTEDTESTROOT"; break;
+ case CERT_E_REVOCATION_FAILURE: pszErrConst = "CERT_E_REVOCATION_FAILURE"; break;
+ case CERT_E_CN_NO_MATCH: pszErrConst = "CERT_E_CN_NO_MATCH"; break;
+ case CERT_E_WRONG_USAGE: pszErrConst = "CERT_E_WRONG_USAGE"; break;
+ case CERT_E_UNTRUSTEDCA: pszErrConst = "CERT_E_UNTRUSTEDCA"; break;
+ case CERT_E_INVALID_POLICY: pszErrConst = "CERT_E_INVALID_POLICY"; break;
+ case CERT_E_INVALID_NAME: pszErrConst = "CERT_E_INVALID_NAME"; break;
+ case CRYPT_E_FILE_ERROR: pszErrConst = "CRYPT_E_FILE_ERROR"; break;
+ case CRYPT_E_REVOKED: pszErrConst = "CRYPT_E_REVOKED"; break;
+ }
+ if (pszErrConst)
+ rc = RTErrInfoSetF(pErrInfo, VERR_LDRVI_UNSUPPORTED_ARCH,
+ "WinVerifyTrust failed with hrc=%s on '%ls'", pszErrConst, pwszName);
+ else
+ rc = RTErrInfoSetF(pErrInfo, VERR_LDRVI_UNSUPPORTED_ARCH,
+ "WinVerifyTrust failed with hrc=%Rhrc on '%ls'", hrc, pwszName);
+ SUP_DPRINTF(("supR3HardNtViCallWinVerifyTrust: WinVerifyTrust failed with %#x (%s) on '%ls'\n",
+ hrc, pszErrConst, pwszName));
+ if (phrcWinVerifyTrust)
+ *phrcWinVerifyTrust = hrc;
+ }
+
+ /* clean up state data. */
+ TrustData.dwStateAction = WTD_STATEACTION_CLOSE;
+ FileInfo.hFile = NULL;
+ hrc = pfnWinVerifyTrust(NULL /*hwnd*/, &PolicyActionGuid, &TrustData);
+
+ return rc;
+}
+
+
+/**
+ * Calls WinVerifyTrust to verify an PE image via catalog files.
+ *
+ * @returns VBox status code.
+ * @param hFile File handle to the executable file.
+ * @param pwszName Full NT path to the DLL in question, used for
+ * dealing with unsigned system dlls as well as for
+ * error/logging.
+ * @param fFlags Flags, SUPHNTVI_F_XXX.
+ * @param pErrInfo Pointer to error info structure. Optional.
+ * @param pfnWinVerifyTrust Pointer to the API.
+ */
+static int supR3HardNtViCallWinVerifyTrustCatFile(HANDLE hFile, PCRTUTF16 pwszName, uint32_t fFlags, PRTERRINFO pErrInfo,
+ PFNWINVERIFYTRUST pfnWinVerifyTrust)
+{
+ RT_NOREF1(fFlags);
+ SUP_DPRINTF(("supR3HardNtViCallWinVerifyTrustCatFile: hFile=%p pwszName=%ls\n", hFile, pwszName));
+
+ /*
+ * Convert the name into a Windows name.
+ */
+ RTUTF16 wszWinPathBuf[MAX_PATH];
+ PCRTUTF16 pwszWinPath;
+ int rc = supR3HardNtViNtToWinPath(pwszName, &pwszWinPath, wszWinPathBuf, RT_ELEMENTS(wszWinPathBuf));
+ if (RT_FAILURE(rc))
+ return RTErrInfoSetF(pErrInfo, rc, "Bad path passed to supR3HardNtViCallWinVerifyTrustCatFile: rc=%Rrc '%ls'", rc, pwszName);
+
+ /*
+ * Open the file if we didn't get a handle.
+ */
+ HANDLE hFileClose = NULL;
+ if (hFile == RTNT_INVALID_HANDLE_VALUE || hFile == NULL)
+ {
+ hFile = RTNT_INVALID_HANDLE_VALUE;
+ IO_STATUS_BLOCK Ios = RTNT_IO_STATUS_BLOCK_INITIALIZER;
+
+ UNICODE_STRING NtName;
+ NtName.Buffer = (PWSTR)pwszName;
+ NtName.Length = (USHORT)(RTUtf16Len(pwszName) * sizeof(WCHAR));
+ NtName.MaximumLength = NtName.Length + sizeof(WCHAR);
+
+ OBJECT_ATTRIBUTES ObjAttr;
+ InitializeObjectAttributes(&ObjAttr, &NtName, OBJ_CASE_INSENSITIVE, NULL /*hRootDir*/, NULL /*pSecDesc*/);
+
+ NTSTATUS rcNt = NtCreateFile(&hFile,
+ FILE_READ_DATA | READ_CONTROL | SYNCHRONIZE,
+ &ObjAttr,
+ &Ios,
+ NULL /* Allocation Size*/,
+ FILE_ATTRIBUTE_NORMAL,
+ FILE_SHARE_READ,
+ FILE_OPEN,
+ FILE_NON_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT,
+ NULL /*EaBuffer*/,
+ 0 /*EaLength*/);
+ if (NT_SUCCESS(rcNt))
+ rcNt = Ios.Status;
+ if (!NT_SUCCESS(rcNt))
+ return RTErrInfoSetF(pErrInfo, RTErrConvertFromNtStatus(rcNt),
+ "NtCreateFile returned %#x opening '%ls'.", rcNt, pwszName);
+ hFileClose = hFile;
+ }
+
+ /*
+ * On Windows 8.0 and later there are more than one digest choice.
+ */
+ int fNoSignedCatalogFound = -1;
+ rc = VERR_LDRVI_NOT_SIGNED;
+ static struct
+ {
+ /** The digest algorithm name. */
+ const WCHAR *pszAlgorithm;
+ /** Cached catalog admin handle. */
+ HCATADMIN volatile hCachedCatAdmin;
+ } s_aHashes[] =
+ {
+ { NULL, NULL },
+ { L"SHA256", NULL },
+ };
+ for (uint32_t i = 0; i < RT_ELEMENTS(s_aHashes); i++)
+ {
+ /*
+ * Another loop for dealing with different trust provider policies
+ * required for successfully validating different catalog signatures.
+ */
+ bool fTryNextPolicy;
+ uint32_t iPolicy = 0;
+ static const GUID s_aPolicies[] =
+ {
+ DRIVER_ACTION_VERIFY, /* Works with microsoft bits. Most frequently used, thus first. */
+ WINTRUST_ACTION_GENERIC_VERIFY_V2, /* Works with ATI and other SPC kernel-code signed stuff. */
+ };
+ do
+ {
+ /*
+ * Create a context.
+ */
+ fTryNextPolicy = false;
+ bool fFreshContext = false;
+ BOOL fRc;
+ HCATADMIN hCatAdmin = ASMAtomicXchgPtr(&s_aHashes[i].hCachedCatAdmin, NULL);
+ if (hCatAdmin)
+ {
+ SUP_DPRINTF(("supR3HardNtViCallWinVerifyTrustCatFile: Cached context %p\n", hCatAdmin));
+ fFreshContext = false;
+ fRc = TRUE;
+ }
+ else
+ {
+l_fresh_context:
+ fFreshContext = true;
+ if (g_pfnCryptCATAdminAcquireContext2)
+ fRc = g_pfnCryptCATAdminAcquireContext2(&hCatAdmin, &s_aPolicies[iPolicy], s_aHashes[i].pszAlgorithm,
+ NULL /*pStrongHashPolicy*/, 0 /*dwFlags*/);
+ else
+ fRc = g_pfnCryptCATAdminAcquireContext(&hCatAdmin, &s_aPolicies[iPolicy], 0 /*dwFlags*/);
+ SUP_DPRINTF(("supR3HardNtViCallWinVerifyTrustCatFile: New context %p\n", hCatAdmin));
+ }
+ if (fRc)
+ {
+ SUP_DPRINTF(("supR3HardNtViCallWinVerifyTrustCatFile: hCatAdmin=%p\n", hCatAdmin));
+
+ /*
+ * Hash the file.
+ */
+ BYTE abHash[SUPHARDNTVI_MAX_CAT_HASH_SIZE];
+ DWORD cbHash = sizeof(abHash);
+ if (g_pfnCryptCATAdminCalcHashFromFileHandle2)
+ fRc = g_pfnCryptCATAdminCalcHashFromFileHandle2(hCatAdmin, hFile, &cbHash, abHash, 0 /*dwFlags*/);
+ else
+ fRc = g_pfnCryptCATAdminCalcHashFromFileHandle(hFile, &cbHash, abHash, 0 /*dwFlags*/);
+ if (fRc)
+ {
+ /* Produce a string version of it that we can pass to WinVerifyTrust. */
+ RTUTF16 wszDigest[SUPHARDNTVI_MAX_CAT_HASH_SIZE * 2 + 1];
+ int rc2 = RTUtf16PrintHexBytes(wszDigest, RT_ELEMENTS(wszDigest), abHash, cbHash, RTSTRPRINTHEXBYTES_F_UPPER);
+ if (RT_SUCCESS(rc2))
+ {
+ SUP_DPRINTF(("supR3HardNtViCallWinVerifyTrustCatFile: cbHash=%u wszDigest=%ls\n", cbHash, wszDigest));
+
+ /*
+ * Enumerate catalog information that matches the hash.
+ */
+ uint32_t iCat = 0;
+ HCATINFO hCatInfoPrev = NULL;
+ do
+ {
+ /* Get the next match. */
+ HCATINFO hCatInfo = g_pfnCryptCATAdminEnumCatalogFromHash(hCatAdmin, abHash, cbHash, 0, &hCatInfoPrev);
+ if (!hCatInfo)
+ {
+ if (!fFreshContext)
+ {
+ SUP_DPRINTF(("supR3HardNtViCallWinVerifyTrustCatFile: Retrying with fresh context (CryptCATAdminEnumCatalogFromHash -> %u; iCat=%#x)\n", RtlGetLastWin32Error(), iCat));
+ if (hCatInfoPrev != NULL)
+ g_pfnCryptCATAdminReleaseCatalogContext(hCatAdmin, hCatInfoPrev, 0 /*dwFlags*/);
+ g_pfnCryptCATAdminReleaseContext(hCatAdmin, 0 /*dwFlags*/);
+ goto l_fresh_context;
+ }
+ ULONG ulErr = RtlGetLastWin32Error();
+ fNoSignedCatalogFound = ulErr == ERROR_NOT_FOUND && fNoSignedCatalogFound != 0;
+ if (iCat == 0)
+ SUP_DPRINTF(("supR3HardNtViCallWinVerifyTrustCatFile: CryptCATAdminEnumCatalogFromHash failed ERROR_NOT_FOUND (%u)\n", ulErr));
+ else if (iCat == 0)
+ SUP_DPRINTF(("supR3HardNtViCallWinVerifyTrustCatFile: CryptCATAdminEnumCatalogFromHash failed %u\n", ulErr));
+ break;
+ }
+ fNoSignedCatalogFound = 0;
+ Assert(hCatInfoPrev == NULL);
+ hCatInfoPrev = hCatInfo;
+
+ /*
+ * Call WinVerifyTrust.
+ */
+ CATALOG_INFO CatInfo;
+ CatInfo.cbStruct = sizeof(CatInfo);
+ CatInfo.wszCatalogFile[0] = '\0';
+ if (g_pfnCryptCATCatalogInfoFromContext(hCatInfo, &CatInfo, 0 /*dwFlags*/))
+ {
+ WINTRUST_CATALOG_INFO WtCatInfo;
+ RT_ZERO(WtCatInfo);
+ WtCatInfo.cbStruct = sizeof(WtCatInfo);
+ WtCatInfo.dwCatalogVersion = 0;
+ WtCatInfo.pcwszCatalogFilePath = CatInfo.wszCatalogFile;
+ WtCatInfo.pcwszMemberTag = wszDigest;
+ WtCatInfo.pcwszMemberFilePath = pwszWinPath;
+ WtCatInfo.pbCalculatedFileHash = abHash;
+ WtCatInfo.cbCalculatedFileHash = cbHash;
+ WtCatInfo.pcCatalogContext = NULL;
+
+ WINTRUST_DATA TrustData;
+ RT_ZERO(TrustData);
+ TrustData.cbStruct = sizeof(TrustData);
+ TrustData.fdwRevocationChecks = WTD_REVOKE_NONE; /* Keep simple for now. */
+ TrustData.dwStateAction = WTD_STATEACTION_VERIFY;
+ TrustData.dwUIChoice = WTD_UI_NONE;
+ TrustData.dwProvFlags = 0;
+ if (g_uNtVerCombined >= SUP_MAKE_NT_VER_SIMPLE(6, 0))
+ TrustData.dwProvFlags = WTD_CACHE_ONLY_URL_RETRIEVAL;
+ else
+ TrustData.dwProvFlags = WTD_REVOCATION_CHECK_NONE;
+ TrustData.dwUnionChoice = WTD_CHOICE_CATALOG;
+ TrustData.pCatalog = &WtCatInfo;
+
+ HRESULT hrc = pfnWinVerifyTrust(NULL /*hwnd*/, &s_aPolicies[iPolicy], &TrustData);
+ SUP_DPRINTF(("supR3HardNtViCallWinVerifyTrustCatFile: WinVerifyTrust => %#x; cat='%ls'; file='%ls'\n",
+ hrc, CatInfo.wszCatalogFile, pwszName));
+
+ if (SUCCEEDED(hrc))
+ rc = VINF_SUCCESS;
+ else if (hrc == TRUST_E_NOSIGNATURE)
+ { /* ignore because it's useless. */ }
+ else if (hrc == ERROR_INVALID_PARAMETER)
+ { /* This is returned if the given file isn't found in the catalog, it seems. */ }
+ else
+ {
+ rc = RTErrInfoSetF(pErrInfo, VERR_SUP_VP_WINTRUST_CAT_FAILURE,
+ "WinVerifyTrust failed with hrc=%#x on '%ls' and .cat-file='%ls'.",
+ hrc, pwszWinPath, CatInfo.wszCatalogFile);
+ fTryNextPolicy |= (hrc == CERT_E_UNTRUSTEDROOT);
+ }
+
+ /* clean up state data. */
+ TrustData.dwStateAction = WTD_STATEACTION_CLOSE;
+ hrc = pfnWinVerifyTrust(NULL /*hwnd*/, &s_aPolicies[iPolicy], &TrustData);
+ Assert(SUCCEEDED(hrc));
+ }
+ else
+ {
+ rc = RTErrInfoSetF(pErrInfo, RTErrConvertFromWin32(RtlGetLastWin32Error()),
+ "CryptCATCatalogInfoFromContext failed: %d [file=%s]",
+ RtlGetLastWin32Error(), pwszName);
+ SUP_DPRINTF(("supR3HardNtViCallWinVerifyTrustCatFile: CryptCATCatalogInfoFromContext failed\n"));
+ }
+ iCat++;
+ } while (rc == VERR_LDRVI_NOT_SIGNED && iCat < 128);
+
+ if (hCatInfoPrev != NULL)
+ if (!g_pfnCryptCATAdminReleaseCatalogContext(hCatAdmin, hCatInfoPrev, 0 /*dwFlags*/))
+ AssertFailed();
+ }
+ else
+ rc = RTErrInfoSetF(pErrInfo, rc2, "RTUtf16PrintHexBytes failed: %Rrc", rc);
+ }
+ else
+ rc = RTErrInfoSetF(pErrInfo, RTErrConvertFromWin32(RtlGetLastWin32Error()),
+ "CryptCATAdminCalcHashFromFileHandle[2] failed: %d [file=%s]", RtlGetLastWin32Error(), pwszName);
+
+ if (!ASMAtomicCmpXchgPtr(&s_aHashes[i].hCachedCatAdmin, hCatAdmin, NULL))
+ if (!g_pfnCryptCATAdminReleaseContext(hCatAdmin, 0 /*dwFlags*/))
+ AssertFailed();
+ }
+ else
+ rc = RTErrInfoSetF(pErrInfo, RTErrConvertFromWin32(RtlGetLastWin32Error()),
+ "CryptCATAdminAcquireContext[2] failed: %d [file=%s]", RtlGetLastWin32Error(), pwszName);
+ iPolicy++;
+ } while ( fTryNextPolicy
+ && iPolicy < RT_ELEMENTS(s_aPolicies));
+
+ /*
+ * Only repeat if we've got g_pfnCryptCATAdminAcquireContext2 and can specify the hash algorithm.
+ */
+ if (!g_pfnCryptCATAdminAcquireContext2)
+ break;
+ if (rc != VERR_LDRVI_NOT_SIGNED)
+ break;
+ }
+
+ if (hFileClose != NULL)
+ NtClose(hFileClose);
+
+ /*
+ * DLLs that are likely candidates for local modifications.
+ */
+ if (rc == VERR_LDRVI_NOT_SIGNED)
+ {
+ bool fCoreSystemDll = false;
+ PCRTUTF16 pwsz;
+ uint32_t cwcName = (uint32_t)RTUtf16Len(pwszName);
+ uint32_t cwcOther = g_System32NtPath.UniStr.Length / sizeof(WCHAR);
+ if (supHardViUtf16PathStartsWithEx(pwszName, cwcName, g_System32NtPath.UniStr.Buffer, cwcOther, true /*fCheckSlash*/))
+ {
+ pwsz = pwszName + cwcOther + 1;
+ if ( supHardViUtf16PathIsEqual(pwsz, "uxtheme.dll")
+ || supHardViUtf16PathIsEqual(pwsz, "user32.dll")
+ || supHardViUtf16PathIsEqual(pwsz, "gdi32.dll")
+ || supHardViUtf16PathIsEqual(pwsz, "opengl32.dll")
+ || (fCoreSystemDll = supHardViUtf16PathIsEqual(pwsz, "KernelBase.dll"))
+ || (fCoreSystemDll = supHardViUtf16PathIsEqual(pwsz, "kernel32.dll"))
+ || (fCoreSystemDll = supHardViUtf16PathIsEqual(pwsz, "ntdll.dll"))
+ )
+ {
+ if (RTErrInfoIsSet(pErrInfo))
+ RTErrInfoAdd(pErrInfo, rc, "\n");
+ RTErrInfoAddF(pErrInfo, rc, "'%ls' is most likely modified.", pwszName);
+ }
+ }
+
+ /* Kludge for ancient windows versions we don't want to support but
+ users still wants to use. Keep things as safe as possible without
+ unnecessary effort. Problem is that 3rd party catalog files cannot
+ easily be found. Showstopper for ATI users. */
+ if ( fNoSignedCatalogFound == 1
+ && g_uNtVerCombined < SUP_NT_VER_VISTA
+ && !fCoreSystemDll)
+ {
+ rc = VINF_LDRVI_NOT_SIGNED;
+ }
+ }
+
+ return rc;
+}
+
+
+/**
+ * Verifies the given image using WinVerifyTrust in some way.
+ *
+ * This is used by supHardenedWinVerifyImageByLdrMod as well as
+ * supR3HardenedScreenImage.
+ *
+ * @returns IPRT status code, modified @a rc.
+ * @param hFile Handle of the file to verify.
+ * @param pwszName Full NT path to the DLL in question, used for
+ * dealing with unsigned system dlls as well as for
+ * error/logging.
+ * @param fFlags SUPHNTVI_F_XXX.
+ * @param rc The current status code.
+ * @param pfWinVerifyTrust Where to return whether WinVerifyTrust was
+ * actually used.
+ * @param pErrInfo Pointer to error info structure. Optional.
+ */
+DECLHIDDEN(int) supHardenedWinVerifyImageTrust(HANDLE hFile, PCRTUTF16 pwszName, uint32_t fFlags, int rc,
+ bool *pfWinVerifyTrust, PRTERRINFO pErrInfo)
+{
+ if (pfWinVerifyTrust)
+ *pfWinVerifyTrust = false;
+
+ /*
+ * Call the windows verify trust API if we've resolved it and aren't in
+ * some obvious recursion.
+ */
+ if (g_pfnWinVerifyTrust != NULL)
+ {
+ uint32_t const idCurrentThread = RTNtCurrentThreadId();
+
+ /* Check if loader lock owner. */
+ struct _RTL_CRITICAL_SECTION volatile *pLoaderLock = NtCurrentPeb()->LoaderLock;
+ bool fOwnsLoaderLock = pLoaderLock
+ && pLoaderLock->OwningThread == (HANDLE)(uintptr_t)idCurrentThread
+ && pLoaderLock->RecursionCount > 0;
+ if (!fOwnsLoaderLock)
+ {
+ /* Check for recursion. */
+ bool fNoRecursion;
+ if (g_iTlsWinVerifyTrustRecursion != UINT32_MAX)
+ {
+ fNoRecursion = TlsGetValue(g_iTlsWinVerifyTrustRecursion) == 0;
+ if (fNoRecursion)
+ TlsSetValue(g_iTlsWinVerifyTrustRecursion, (void *)1);
+ }
+ else
+ fNoRecursion = ASMAtomicCmpXchgU32(&g_idActiveThread, idCurrentThread, UINT32_MAX);
+
+ if (fNoRecursion && !fOwnsLoaderLock)
+ {
+ /* We can call WinVerifyTrust. */
+ if (pfWinVerifyTrust)
+ *pfWinVerifyTrust = true;
+
+ if (rc != VERR_LDRVI_NOT_SIGNED)
+ {
+ if (rc == VINF_LDRVI_NOT_SIGNED)
+ {
+ if (fFlags & SUPHNTVI_F_ALLOW_CAT_FILE_VERIFICATION)
+ {
+ int rc2 = supR3HardNtViCallWinVerifyTrustCatFile(hFile, pwszName, fFlags, pErrInfo,
+ g_pfnWinVerifyTrust);
+ SUP_DPRINTF(("supR3HardNtViCallWinVerifyTrustCatFile -> %d (org %d)\n", rc2, rc));
+ rc = rc2;
+ }
+ else
+ {
+ AssertFailed();
+ rc = VERR_LDRVI_NOT_SIGNED;
+ }
+ }
+ else if (RT_SUCCESS(rc))
+ {
+ HRESULT hrcWinVerifyTrust;
+ rc = supR3HardNtViCallWinVerifyTrust(hFile, pwszName, fFlags, pErrInfo, g_pfnWinVerifyTrust,
+ &hrcWinVerifyTrust);
+
+ /* DLLs signed with special roots, like "Microsoft Digital Media Authority 2005",
+ may fail here because the root cert is not in the normal certificate stores
+ (if any). Our verification code has the basics of these certificates included
+ and can verify them, which is why we end up here instead of in the
+ VINF_LDRVI_NOT_SIGNED case above. Current workaround is to do as above.
+ (Intel graphics driver DLLs, like igdusc64.dll. */
+ if ( RT_FAILURE(rc)
+ && hrcWinVerifyTrust == CERT_E_CHAINING
+ && (fFlags & SUPHNTVI_F_ALLOW_CAT_FILE_VERIFICATION))
+ {
+ rc = supR3HardNtViCallWinVerifyTrustCatFile(hFile, pwszName, fFlags, pErrInfo, g_pfnWinVerifyTrust);
+ SUP_DPRINTF(("supR3HardNtViCallWinVerifyTrustCatFile -> %d (was CERT_E_CHAINING)\n", rc));
+ }
+ }
+ else
+ {
+ int rc2 = supR3HardNtViCallWinVerifyTrust(hFile, pwszName, fFlags, pErrInfo, g_pfnWinVerifyTrust, NULL);
+ AssertMsg(RT_FAILURE_NP(rc2),
+ ("rc=%Rrc, rc2=%Rrc %s", rc, rc2, pErrInfo ? pErrInfo->pszMsg : "<no-err-info>"));
+ RT_NOREF_PV(rc2);
+ }
+ }
+
+ /* Unwind recursion. */
+ if (g_iTlsWinVerifyTrustRecursion != UINT32_MAX)
+ TlsSetValue(g_iTlsWinVerifyTrustRecursion, (void *)0);
+ else
+ ASMAtomicWriteU32(&g_idActiveThread, UINT32_MAX);
+ }
+ /*
+ * No can do.
+ */
+ else
+ SUP_DPRINTF(("Detected WinVerifyTrust recursion: rc=%Rrc '%ls'.\n", rc, pwszName));
+ }
+ else
+ SUP_DPRINTF(("Detected loader lock ownership: rc=%Rrc '%ls'.\n", rc, pwszName));
+ }
+ return rc;
+}
+
+
+/**
+ * Checks if WinVerifyTrust is callable on the current thread.
+ *
+ * Used by the main code to figure whether it makes sense to try revalidate an
+ * image that hasn't passed thru WinVerifyTrust yet.
+ *
+ * @returns true if callable on current thread, false if not.
+ */
+DECLHIDDEN(bool) supHardenedWinIsWinVerifyTrustCallable(void)
+{
+ return g_pfnWinVerifyTrust != NULL
+ && ( g_iTlsWinVerifyTrustRecursion != UINT32_MAX
+ ? (uintptr_t)TlsGetValue(g_iTlsWinVerifyTrustRecursion) == 0
+ : g_idActiveThread != RTNtCurrentThreadId() );
+}
+
+
+
+/**
+ * Initializes g_uNtVerCombined and g_NtVerInfo.
+ * Called from suplibHardenedWindowsMain and suplibOsInit.
+ */
+DECLHIDDEN(void) supR3HardenedWinInitVersion(bool fEarly)
+{
+ /*
+ * Get the windows version. Use RtlGetVersion as GetVersionExW and
+ * GetVersion might not be telling the whole truth (8.0 on 8.1 depending on
+ * the application manifest).
+ *
+ * Note! Windows 10 build 14267+ touches BSS when calling RtlGetVersion, so we
+ * have to use the fallback for the call from the early init code.
+ */
+ OSVERSIONINFOEXW NtVerInfo;
+
+ RT_ZERO(NtVerInfo);
+ NtVerInfo.dwOSVersionInfoSize = sizeof(RTL_OSVERSIONINFOEXW);
+ if ( fEarly
+ || !NT_SUCCESS(RtlGetVersion((PRTL_OSVERSIONINFOW)&NtVerInfo)))
+ {
+ RT_ZERO(NtVerInfo);
+ PPEB pPeb = NtCurrentPeb();
+ NtVerInfo.dwMajorVersion = pPeb->OSMajorVersion;
+ NtVerInfo.dwMinorVersion = pPeb->OSMinorVersion;
+ NtVerInfo.dwBuildNumber = pPeb->OSBuildNumber;
+ }
+
+ g_uNtVerCombined = SUP_MAKE_NT_VER_COMBINED(NtVerInfo.dwMajorVersion, NtVerInfo.dwMinorVersion, NtVerInfo.dwBuildNumber,
+ NtVerInfo.wServicePackMajor, NtVerInfo.wServicePackMinor);
+}
+
+#endif /* IN_RING3 */
+
diff --git a/src/VBox/HostDrivers/Support/win/SUPHardenedVerifyProcess-win.cpp b/src/VBox/HostDrivers/Support/win/SUPHardenedVerifyProcess-win.cpp
new file mode 100644
index 00000000..ec6b6a0a
--- /dev/null
+++ b/src/VBox/HostDrivers/Support/win/SUPHardenedVerifyProcess-win.cpp
@@ -0,0 +1,2681 @@
+/* $Id: SUPHardenedVerifyProcess-win.cpp $ */
+/** @file
+ * VirtualBox Support Library/Driver - Hardened Process Verification, Windows.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#ifdef IN_RING0
+# ifndef IPRT_NT_MAP_TO_ZW
+# define IPRT_NT_MAP_TO_ZW
+# endif
+# include <iprt/nt/nt.h>
+# include <ntimage.h>
+#else
+# include <iprt/nt/nt-and-windows.h>
+#endif
+
+#include <VBox/sup.h>
+#include <VBox/err.h>
+#include <iprt/alloca.h>
+#include <iprt/ctype.h>
+#include <iprt/param.h>
+#include <iprt/string.h>
+#include <iprt/utf16.h>
+#include <iprt/zero.h>
+
+#ifdef IN_RING0
+# include "SUPDrvInternal.h"
+#else
+# include "SUPLibInternal.h"
+#endif
+#include "win/SUPHardenedVerify-win.h"
+
+
+/*********************************************************************************************************************************
+* Structures and Typedefs *
+*********************************************************************************************************************************/
+/**
+ * Virtual address space region.
+ */
+typedef struct SUPHNTVPREGION
+{
+ /** The RVA of the region. */
+ uint32_t uRva;
+ /** The size of the region. */
+ uint32_t cb;
+ /** The protection of the region. */
+ uint32_t fProt;
+} SUPHNTVPREGION;
+/** Pointer to a virtual address space region. */
+typedef SUPHNTVPREGION *PSUPHNTVPREGION;
+
+/**
+ * Virtual address space image information.
+ */
+typedef struct SUPHNTVPIMAGE
+{
+ /** The base address of the image. */
+ uintptr_t uImageBase;
+ /** The size of the image mapping. */
+ uintptr_t cbImage;
+
+ /** The name from the allowed lists. */
+ const char *pszName;
+ /** Name structure for NtQueryVirtualMemory/MemorySectionName. */
+ struct
+ {
+ /** The full unicode name. */
+ UNICODE_STRING UniStr;
+ /** Buffer space. */
+ WCHAR awcBuffer[260];
+ } Name;
+
+ /** The number of mapping regions. */
+ uint32_t cRegions;
+ /** Mapping regions. */
+ SUPHNTVPREGION aRegions[16];
+
+ /** The image characteristics from the FileHeader. */
+ uint16_t fImageCharecteristics;
+ /** The DLL characteristics from the OptionalHeader. */
+ uint16_t fDllCharecteristics;
+
+ /** Set if this is the DLL. */
+ bool fDll;
+ /** Set if the image is NTDLL an the verficiation code needs to watch out for
+ * the NtCreateSection patch. */
+ bool fNtCreateSectionPatch;
+ /** Whether the API set schema hack needs to be applied when verifying memory
+ * content. The hack means that we only check if the 1st section is mapped. */
+ bool fApiSetSchemaOnlySection1;
+ /** This may be a 32-bit resource DLL. */
+ bool f32bitResourceDll;
+
+ /** Pointer to the loader cache entry for the image. */
+ PSUPHNTLDRCACHEENTRY pCacheEntry;
+#ifdef IN_RING0
+ /** In ring-0 we don't currently cache images, so put it here. */
+ SUPHNTLDRCACHEENTRY CacheEntry;
+#endif
+} SUPHNTVPIMAGE;
+/** Pointer to image info from the virtual address space scan. */
+typedef SUPHNTVPIMAGE *PSUPHNTVPIMAGE;
+
+/**
+ * Virtual address space scanning state.
+ */
+typedef struct SUPHNTVPSTATE
+{
+ /** Type of verification to perform. */
+ SUPHARDNTVPKIND enmKind;
+ /** Combination of SUPHARDNTVP_F_XXX. */
+ uint32_t fFlags;
+ /** The result. */
+ int rcResult;
+ /** Number of fixes we've done.
+ * Only applicable in the purification modes. */
+ uint32_t cFixes;
+ /** Number of images in aImages. */
+ uint32_t cImages;
+ /** The index of the last image we looked up. */
+ uint32_t iImageHint;
+ /** The process handle. */
+ HANDLE hProcess;
+ /** Images found in the process.
+ * The array is large enough to hold the executable, all allowed DLLs, and one
+ * more so we can get the image name of the first unwanted DLL. */
+ SUPHNTVPIMAGE aImages[1 + 6 + 1
+#ifdef VBOX_PERMIT_VERIFIER_DLL
+ + 1
+#endif
+#ifdef VBOX_PERMIT_MORE
+ + 5
+#endif
+#ifdef VBOX_PERMIT_VISUAL_STUDIO_PROFILING
+ + 16
+#endif
+ ];
+ /** Memory compare scratch buffer.*/
+ uint8_t abMemory[_4K];
+ /** File compare scratch buffer.*/
+ uint8_t abFile[_4K];
+ /** Section headers for use when comparing file and loaded image. */
+ IMAGE_SECTION_HEADER aSecHdrs[16];
+ /** Pointer to the error info. */
+ PRTERRINFO pErrInfo;
+} SUPHNTVPSTATE;
+/** Pointer to stat information of a virtual address space scan. */
+typedef SUPHNTVPSTATE *PSUPHNTVPSTATE;
+
+
+/*********************************************************************************************************************************
+* Global Variables *
+*********************************************************************************************************************************/
+/**
+ * System DLLs allowed to be loaded into the process.
+ * @remarks supHardNtVpCheckDlls assumes these are lower case.
+ */
+static const char *g_apszSupNtVpAllowedDlls[] =
+{
+ "ntdll.dll",
+ "kernel32.dll",
+ "kernelbase.dll",
+ "apphelp.dll",
+ "apisetschema.dll",
+#ifdef VBOX_PERMIT_VERIFIER_DLL
+ "verifier.dll",
+#endif
+#ifdef VBOX_PERMIT_MORE
+# define VBOX_PERMIT_MORE_FIRST_IDX 5
+ "sfc.dll",
+ "sfc_os.dll",
+ "user32.dll",
+ "acres.dll",
+ "acgenral.dll",
+#endif
+#ifdef VBOX_PERMIT_VISUAL_STUDIO_PROFILING
+ "psapi.dll",
+ "msvcrt.dll",
+ "advapi32.dll",
+ "sechost.dll",
+ "rpcrt4.dll",
+ "SamplingRuntime.dll",
+#endif
+};
+
+/**
+ * VBox executables allowed to start VMs.
+ * @remarks Remember to keep in sync with g_aSupInstallFiles in
+ * SUPR3HardenedVerify.cpp.
+ */
+static const char *g_apszSupNtVpAllowedVmExes[] =
+{
+ "VBoxHeadless.exe",
+ "VirtualBoxVM.exe",
+ "VBoxSDL.exe",
+ "VBoxNetDHCP.exe",
+ "VBoxNetNAT.exe",
+ "VBoxVMMPreload.exe",
+
+ "tstMicro.exe",
+ "tstPDMAsyncCompletion.exe",
+ "tstPDMAsyncCompletionStress.exe",
+ "tstVMM.exe",
+ "tstVMREQ.exe",
+ "tstCFGM.exe",
+ "tstGIP-2.exe",
+ "tstIntNet-1.exe",
+ "tstMMHyperHeap.exe",
+ "tstRTR0ThreadPreemptionDriver.exe",
+ "tstRTR0MemUserKernelDriver.exe",
+ "tstRTR0SemMutexDriver.exe",
+ "tstRTR0TimerDriver.exe",
+ "tstSSM.exe",
+};
+
+/** Pointer to NtQueryVirtualMemory. Initialized by SUPDrv-win.cpp in
+ * ring-0, in ring-3 it's just a slightly confusing define. */
+#ifdef IN_RING0
+PFNNTQUERYVIRTUALMEMORY g_pfnNtQueryVirtualMemory = NULL;
+#else
+# define g_pfnNtQueryVirtualMemory NtQueryVirtualMemory
+#endif
+
+#ifdef IN_RING3
+/** The number of valid entries in the loader cache. */
+static uint32_t g_cSupNtVpLdrCacheEntries = 0;
+/** The loader cache entries. */
+static SUPHNTLDRCACHEENTRY g_aSupNtVpLdrCacheEntries[RT_ELEMENTS(g_apszSupNtVpAllowedDlls) + 1 + 3];
+#endif
+
+
+/**
+ * Fills in error information.
+ *
+ * @returns @a rc.
+ * @param pErrInfo Pointer to the extended error info structure.
+ * Can be NULL.
+ * @param rc The status to return.
+ * @param pszMsg The format string for the message.
+ * @param ... The arguments for the format string.
+ */
+static int supHardNtVpSetInfo1(PRTERRINFO pErrInfo, int rc, const char *pszMsg, ...)
+{
+ va_list va;
+#ifdef IN_RING3
+ va_start(va, pszMsg);
+ supR3HardenedError(rc, false /*fFatal*/, "%N\n", pszMsg, &va);
+ va_end(va);
+#endif
+
+ va_start(va, pszMsg);
+ RTErrInfoSetV(pErrInfo, rc, pszMsg, va);
+ va_end(va);
+
+ return rc;
+}
+
+
+/**
+ * Adds error information.
+ *
+ * @returns @a rc.
+ * @param pErrInfo Pointer to the extended error info structure
+ * which may contain some details already. Can be
+ * NULL.
+ * @param rc The status to return.
+ * @param pszMsg The format string for the message.
+ * @param ... The arguments for the format string.
+ */
+static int supHardNtVpAddInfo1(PRTERRINFO pErrInfo, int rc, const char *pszMsg, ...)
+{
+ va_list va;
+#ifdef IN_RING3
+ va_start(va, pszMsg);
+ if (pErrInfo && pErrInfo->pszMsg)
+ supR3HardenedError(rc, false /*fFatal*/, "%N - %s\n", pszMsg, &va, pErrInfo->pszMsg);
+ else
+ supR3HardenedError(rc, false /*fFatal*/, "%N\n", pszMsg, &va);
+ va_end(va);
+#endif
+
+ va_start(va, pszMsg);
+ RTErrInfoAddV(pErrInfo, rc, pszMsg, va);
+ va_end(va);
+
+ return rc;
+}
+
+
+/**
+ * Fills in error information.
+ *
+ * @returns @a rc.
+ * @param pThis The process validator instance.
+ * @param rc The status to return.
+ * @param pszMsg The format string for the message.
+ * @param ... The arguments for the format string.
+ */
+static int supHardNtVpSetInfo2(PSUPHNTVPSTATE pThis, int rc, const char *pszMsg, ...)
+{
+ va_list va;
+#ifdef IN_RING3
+ va_start(va, pszMsg);
+ supR3HardenedError(rc, false /*fFatal*/, "%N\n", pszMsg, &va);
+ va_end(va);
+#endif
+
+ va_start(va, pszMsg);
+#ifdef IN_RING0
+ RTErrInfoSetV(pThis->pErrInfo, rc, pszMsg, va);
+ pThis->rcResult = rc;
+#else
+ if (RT_SUCCESS(pThis->rcResult))
+ {
+ RTErrInfoSetV(pThis->pErrInfo, rc, pszMsg, va);
+ pThis->rcResult = rc;
+ }
+ else
+ {
+ RTErrInfoAddF(pThis->pErrInfo, rc, " \n[rc=%d] ", rc);
+ RTErrInfoAddV(pThis->pErrInfo, rc, pszMsg, va);
+ }
+#endif
+ va_end(va);
+
+ return pThis->rcResult;
+}
+
+
+static int supHardNtVpReadImage(PSUPHNTVPIMAGE pImage, uint64_t off, void *pvBuf, size_t cbRead)
+{
+ return pImage->pCacheEntry->pNtViRdr->Core.pfnRead(&pImage->pCacheEntry->pNtViRdr->Core, pvBuf, cbRead, off);
+}
+
+
+static NTSTATUS supHardNtVpReadMem(HANDLE hProcess, uintptr_t uPtr, void *pvBuf, size_t cbRead)
+{
+#ifdef IN_RING0
+ /* ASSUMES hProcess is the current process. */
+ RT_NOREF1(hProcess);
+ /** @todo use MmCopyVirtualMemory where available! */
+ int rc = RTR0MemUserCopyFrom(pvBuf, uPtr, cbRead);
+ if (RT_SUCCESS(rc))
+ return STATUS_SUCCESS;
+ return STATUS_ACCESS_DENIED;
+#else
+ SIZE_T cbIgn;
+ NTSTATUS rcNt = NtReadVirtualMemory(hProcess, (PVOID)uPtr, pvBuf, cbRead, &cbIgn);
+ if (NT_SUCCESS(rcNt) && cbIgn != cbRead)
+ rcNt = STATUS_IO_DEVICE_ERROR;
+ return rcNt;
+#endif
+}
+
+
+#ifdef IN_RING3
+static NTSTATUS supHardNtVpFileMemRestore(PSUPHNTVPSTATE pThis, PVOID pvRestoreAddr, uint8_t const *pbFile, uint32_t cbToRestore,
+ uint32_t fCorrectProtection)
+{
+ PVOID pvProt = pvRestoreAddr;
+ SIZE_T cbProt = cbToRestore;
+ ULONG fOldProt = 0;
+ NTSTATUS rcNt = NtProtectVirtualMemory(pThis->hProcess, &pvProt, &cbProt, PAGE_READWRITE, &fOldProt);
+ if (NT_SUCCESS(rcNt))
+ {
+ SIZE_T cbIgnored;
+ rcNt = NtWriteVirtualMemory(pThis->hProcess, pvRestoreAddr, pbFile, cbToRestore, &cbIgnored);
+
+ pvProt = pvRestoreAddr;
+ cbProt = cbToRestore;
+ NTSTATUS rcNt2 = NtProtectVirtualMemory(pThis->hProcess, &pvProt, &cbProt, fCorrectProtection, &fOldProt);
+ if (NT_SUCCESS(rcNt))
+ rcNt = rcNt2;
+ }
+ pThis->cFixes++;
+ return rcNt;
+}
+#endif /* IN_RING3 */
+
+
+typedef struct SUPHNTVPSKIPAREA
+{
+ uint32_t uRva;
+ uint32_t cb;
+} SUPHNTVPSKIPAREA;
+typedef SUPHNTVPSKIPAREA *PSUPHNTVPSKIPAREA;
+
+static int supHardNtVpFileMemCompareSection(PSUPHNTVPSTATE pThis, PSUPHNTVPIMAGE pImage,
+ uint32_t uRva, uint32_t cb, const uint8_t *pbFile,
+ int32_t iSh, PSUPHNTVPSKIPAREA paSkipAreas, uint32_t cSkipAreas,
+ uint32_t fCorrectProtection)
+{
+#ifndef IN_RING3
+ RT_NOREF1(fCorrectProtection);
+#endif
+ AssertCompileAdjacentMembers(SUPHNTVPSTATE, abMemory, abFile); /* Use both the memory and file buffers here. Parfait might hate me for this... */
+ uint32_t const cbMemory = sizeof(pThis->abMemory) + sizeof(pThis->abFile);
+ uint8_t * const pbMemory = &pThis->abMemory[0];
+
+ while (cb > 0)
+ {
+ uint32_t cbThis = RT_MIN(cb, cbMemory);
+
+ /* Clipping. */
+ uint32_t uNextRva = uRva + cbThis;
+ if (cSkipAreas)
+ {
+ uint32_t uRvaEnd = uNextRva;
+ uint32_t i = cSkipAreas;
+ while (i-- > 0)
+ {
+ uint32_t uSkipEnd = paSkipAreas[i].uRva + paSkipAreas[i].cb;
+ if ( uRva < uSkipEnd
+ && uRvaEnd > paSkipAreas[i].uRva)
+ {
+ if (uRva < paSkipAreas[i].uRva)
+ {
+ cbThis = paSkipAreas[i].uRva - uRva;
+ uRvaEnd = paSkipAreas[i].uRva;
+ uNextRva = uSkipEnd;
+ }
+ else if (uRvaEnd >= uSkipEnd)
+ {
+ cbThis -= uSkipEnd - uRva;
+ pbFile += uSkipEnd - uRva;
+ uRva = uSkipEnd;
+ }
+ else
+ {
+ uNextRva = uSkipEnd;
+ cbThis = 0;
+ break;
+ }
+ }
+ }
+ }
+
+ /* Read the memory. */
+ NTSTATUS rcNt = supHardNtVpReadMem(pThis->hProcess, pImage->uImageBase + uRva, pbMemory, cbThis);
+ if (!NT_SUCCESS(rcNt))
+ return supHardNtVpSetInfo2(pThis, VERR_SUP_VP_MEMORY_READ_ERROR,
+ "%s: Error reading %#x bytes at %p (rva %#x, #%u, %.8s) from memory: %#x",
+ pImage->pszName, cbThis, pImage->uImageBase + uRva, uRva, iSh + 1,
+ iSh >= 0 ? (char *)pThis->aSecHdrs[iSh].Name : "headers", rcNt);
+
+ /* Do the compare. */
+ if (memcmp(pbFile, pbMemory, cbThis) != 0)
+ {
+ const char *pachSectNm = iSh >= 0 ? (char *)pThis->aSecHdrs[iSh].Name : "headers";
+ SUP_DPRINTF(("%s: Differences in section #%u (%s) between file and memory:\n", pImage->pszName, iSh + 1, pachSectNm));
+
+ uint32_t off = 0;
+ while (off < cbThis && pbFile[off] == pbMemory[off])
+ off++;
+ SUP_DPRINTF((" %p / %#09x: %02x != %02x\n",
+ pImage->uImageBase + uRva + off, uRva + off, pbFile[off], pbMemory[off]));
+ uint32_t offLast = off;
+ uint32_t cDiffs = 1;
+ for (uint32_t off2 = off + 1; off2 < cbThis; off2++)
+ if (pbFile[off2] != pbMemory[off2])
+ {
+ SUP_DPRINTF((" %p / %#09x: %02x != %02x\n",
+ pImage->uImageBase + uRva + off2, uRva + off2, pbFile[off2], pbMemory[off2]));
+ cDiffs++;
+ offLast = off2;
+ }
+
+#ifdef IN_RING3
+ if ( pThis->enmKind == SUPHARDNTVPKIND_CHILD_PURIFICATION
+ || pThis->enmKind == SUPHARDNTVPKIND_SELF_PURIFICATION
+ || pThis->enmKind == SUPHARDNTVPKIND_SELF_PURIFICATION_LIMITED)
+ {
+ PVOID pvRestoreAddr = (uint8_t *)pImage->uImageBase + uRva;
+ rcNt = supHardNtVpFileMemRestore(pThis, pvRestoreAddr, pbFile, cbThis, fCorrectProtection);
+ if (NT_SUCCESS(rcNt))
+ SUP_DPRINTF((" Restored %#x bytes of original file content at %p\n", cbThis, pvRestoreAddr));
+ else
+ return supHardNtVpSetInfo2(pThis, VERR_SUP_VP_MEMORY_VS_FILE_MISMATCH,
+ "%s: Failed to restore %#x bytes at %p (%#x, #%u, %s): %#x (cDiffs=%#x, first=%#x)",
+ pImage->pszName, cbThis, pvRestoreAddr, uRva, iSh + 1, pachSectNm, rcNt,
+ cDiffs, uRva + off);
+ }
+ else
+#endif /* IN_RING3 */
+ return supHardNtVpSetInfo2(pThis, VERR_SUP_VP_MEMORY_VS_FILE_MISMATCH,
+ "%s: %u differences between %#x and %#x in #%u (%.8s), first: %02x != %02x",
+ pImage->pszName, cDiffs, uRva + off, uRva + offLast, iSh + 1,
+ pachSectNm, pbFile[off], pbMemory[off]);
+ }
+
+ /* Advance. The clipping makes it a little bit complicated. */
+ cbThis = uNextRva - uRva;
+ if (cbThis >= cb)
+ break;
+ cb -= cbThis;
+ pbFile += cbThis;
+ uRva = uNextRva;
+ }
+ return VINF_SUCCESS;
+}
+
+
+
+static int supHardNtVpCheckSectionProtection(PSUPHNTVPSTATE pThis, PSUPHNTVPIMAGE pImage,
+ uint32_t uRva, uint32_t cb, uint32_t fProt)
+{
+ uint32_t const cbOrg = cb;
+ if (!cb)
+ return VINF_SUCCESS;
+ if ( pThis->enmKind == SUPHARDNTVPKIND_CHILD_PURIFICATION
+ || pThis->enmKind == SUPHARDNTVPKIND_SELF_PURIFICATION
+ || pThis->enmKind == SUPHARDNTVPKIND_SELF_PURIFICATION_LIMITED)
+ return VINF_SUCCESS;
+
+ for (uint32_t i = 0; i < pImage->cRegions; i++)
+ {
+ uint32_t offRegion = uRva - pImage->aRegions[i].uRva;
+ if (offRegion < pImage->aRegions[i].cb)
+ {
+ uint32_t cbLeft = pImage->aRegions[i].cb - offRegion;
+ if ( pImage->aRegions[i].fProt != fProt
+ && ( fProt != PAGE_READWRITE
+ || pImage->aRegions[i].fProt != PAGE_WRITECOPY))
+ return supHardNtVpSetInfo2(pThis, VERR_SUP_VP_SECTION_PROTECTION_MISMATCH,
+ "%s: RVA range %#x-%#x protection is %#x, expected %#x. (cb=%#x)",
+ pImage->pszName, uRva, uRva + cbLeft - 1, pImage->aRegions[i].fProt, fProt, cb);
+ if (cbLeft >= cb)
+ return VINF_SUCCESS;
+ cb -= cbLeft;
+ uRva += cbLeft;
+
+#if 0 /* This shouldn't ever be necessary. */
+ if ( i + 1 < pImage->cRegions
+ && uRva < pImage->aRegions[i + 1].uRva)
+ {
+ cbLeft = pImage->aRegions[i + 1].uRva - uRva;
+ if (cbLeft >= cb)
+ return VINF_SUCCESS;
+ cb -= cbLeft;
+ uRva += cbLeft;
+ }
+#endif
+ }
+ }
+
+ return supHardNtVpSetInfo2(pThis, cbOrg == cb ? VERR_SUP_VP_SECTION_NOT_MAPPED : VERR_SUP_VP_SECTION_NOT_FULLY_MAPPED,
+ "%s: RVA range %#x-%#x is not mapped?", pImage->pszName, uRva, uRva + cb - 1);
+}
+
+
+DECLINLINE(bool) supHardNtVpIsModuleNameMatch(PSUPHNTVPIMAGE pImage, const char *pszModule)
+{
+ if (pImage->fDll)
+ {
+ const char *pszImageNm = pImage->pszName;
+ for (;;)
+ {
+ char chLeft = *pszImageNm++;
+ char chRight = *pszModule++;
+ if (chLeft != chRight)
+ {
+ Assert(chLeft == RT_C_TO_LOWER(chLeft));
+ if (chLeft != RT_C_TO_LOWER(chRight))
+ {
+ if ( chRight == '\0'
+ && chLeft == '.'
+ && pszImageNm[0] == 'd'
+ && pszImageNm[1] == 'l'
+ && pszImageNm[2] == 'l'
+ && pszImageNm[3] == '\0')
+ return true;
+ break;
+ }
+ }
+
+ if (chLeft == '\0')
+ return true;
+ }
+ }
+
+ return false;
+}
+
+
+/**
+ * Worker for supHardNtVpGetImport that looks up a module in the module table.
+ *
+ * @returns Pointer to the module if found, NULL if not found.
+ * @param pThis The process validator instance.
+ * @param pszModule The name of the module we're looking for.
+ */
+static PSUPHNTVPIMAGE supHardNtVpFindModule(PSUPHNTVPSTATE pThis, const char *pszModule)
+{
+ /*
+ * Check out the hint first.
+ */
+ if ( pThis->iImageHint < pThis->cImages
+ && supHardNtVpIsModuleNameMatch(&pThis->aImages[pThis->iImageHint], pszModule))
+ return &pThis->aImages[pThis->iImageHint];
+
+ /*
+ * Linear array search next.
+ */
+ uint32_t i = pThis->cImages;
+ while (i-- > 0)
+ if (supHardNtVpIsModuleNameMatch(&pThis->aImages[i], pszModule))
+ {
+ pThis->iImageHint = i;
+ return &pThis->aImages[i];
+ }
+
+ /* No cigar. */
+ return NULL;
+}
+
+
+/**
+ * @callback_method_impl{FNRTLDRIMPORT}
+ */
+static DECLCALLBACK(int) supHardNtVpGetImport(RTLDRMOD hLdrMod, const char *pszModule, const char *pszSymbol, unsigned uSymbol,
+ PRTLDRADDR pValue, void *pvUser)
+{
+ RT_NOREF1(hLdrMod);
+ /*SUP_DPRINTF(("supHardNtVpGetImport: %s / %#x / %s.\n", pszModule, uSymbol, pszSymbol));*/
+ PSUPHNTVPSTATE pThis = (PSUPHNTVPSTATE)pvUser;
+
+ int rc = VERR_MODULE_NOT_FOUND;
+ PSUPHNTVPIMAGE pImage = supHardNtVpFindModule(pThis, pszModule);
+ if (pImage)
+ {
+ rc = RTLdrGetSymbolEx(pImage->pCacheEntry->hLdrMod, pImage->pCacheEntry->pbBits,
+ pImage->uImageBase, uSymbol, pszSymbol, pValue);
+ if (RT_SUCCESS(rc))
+ return rc;
+ }
+ /*
+ * API set hacks.
+ */
+ else if (!RTStrNICmp(pszModule, RT_STR_TUPLE("api-ms-win-")))
+ {
+ static const char * const s_apszDlls[] = { "ntdll.dll", "kernelbase.dll", "kernel32.dll" };
+ for (uint32_t i = 0; i < RT_ELEMENTS(s_apszDlls); i++)
+ {
+ pImage = supHardNtVpFindModule(pThis, s_apszDlls[i]);
+ if (pImage)
+ {
+ rc = RTLdrGetSymbolEx(pImage->pCacheEntry->hLdrMod, pImage->pCacheEntry->pbBits,
+ pImage->uImageBase, uSymbol, pszSymbol, pValue);
+ if (RT_SUCCESS(rc))
+ return rc;
+ if (rc != VERR_SYMBOL_NOT_FOUND)
+ break;
+ }
+ }
+ }
+
+ /*
+ * Deal with forwarders.
+ * ASSUMES no forwarders thru any api-ms-win-core-*.dll.
+ * ASSUMES forwarders are resolved after one redirection.
+ */
+ if (rc == VERR_LDR_FORWARDER)
+ {
+ size_t cbInfo = RT_MIN((uint32_t)*pValue, sizeof(RTLDRIMPORTINFO) + 32);
+ PRTLDRIMPORTINFO pInfo = (PRTLDRIMPORTINFO)alloca(cbInfo);
+ rc = RTLdrQueryForwarderInfo(pImage->pCacheEntry->hLdrMod, pImage->pCacheEntry->pbBits,
+ uSymbol, pszSymbol, pInfo, cbInfo);
+ if (RT_SUCCESS(rc))
+ {
+ rc = VERR_MODULE_NOT_FOUND;
+ pImage = supHardNtVpFindModule(pThis, pInfo->szModule);
+ if (pImage)
+ {
+ rc = RTLdrGetSymbolEx(pImage->pCacheEntry->hLdrMod, pImage->pCacheEntry->pbBits,
+ pImage->uImageBase, pInfo->iOrdinal, pInfo->pszSymbol, pValue);
+ if (RT_SUCCESS(rc))
+ return rc;
+
+ SUP_DPRINTF(("supHardNtVpGetImport: Failed to find symbol '%s' in '%s' (forwarded from %s / %s): %Rrc\n",
+ pInfo->pszSymbol, pInfo->szModule, pszModule, pszSymbol, rc));
+ if (rc == VERR_LDR_FORWARDER)
+ rc = VERR_LDR_FORWARDER_CHAIN_TOO_LONG;
+ }
+ else
+ SUP_DPRINTF(("supHardNtVpGetImport: Failed to find forwarder module '%s' (%#x / %s; originally %s / %#x / %s): %Rrc\n",
+ pInfo->szModule, pInfo->iOrdinal, pInfo->pszSymbol, pszModule, uSymbol, pszSymbol, rc));
+ }
+ else
+ SUP_DPRINTF(("supHardNtVpGetImport: RTLdrQueryForwarderInfo failed on symbol %#x/'%s' in '%s': %Rrc\n",
+ uSymbol, pszSymbol, pszModule, rc));
+ }
+ else
+ SUP_DPRINTF(("supHardNtVpGetImport: Failed to find symbol %#x / '%s' in '%s': %Rrc\n",
+ uSymbol, pszSymbol, pszModule, rc));
+ return rc;
+}
+
+
+/**
+ * Compares process memory with the disk content.
+ *
+ * @returns VBox status code.
+ * @param pThis The process scanning state structure (for the
+ * two scratch buffers).
+ * @param pImage The image data collected during the address
+ * space scan.
+ */
+static int supHardNtVpVerifyImageMemoryCompare(PSUPHNTVPSTATE pThis, PSUPHNTVPIMAGE pImage)
+{
+
+ /*
+ * Read and find the file headers.
+ */
+ int rc = supHardNtVpReadImage(pImage, 0 /*off*/, pThis->abFile, sizeof(pThis->abFile));
+ if (RT_FAILURE(rc))
+ return supHardNtVpSetInfo2(pThis, VERR_SUP_VP_IMAGE_HDR_READ_ERROR,
+ "%s: Error reading image header: %Rrc", pImage->pszName, rc);
+
+ uint32_t offNtHdrs = 0;
+ PIMAGE_DOS_HEADER pDosHdr = (PIMAGE_DOS_HEADER)&pThis->abFile[0];
+ if (pDosHdr->e_magic == IMAGE_DOS_SIGNATURE)
+ {
+ offNtHdrs = pDosHdr->e_lfanew;
+ if (offNtHdrs > 512 || offNtHdrs < sizeof(*pDosHdr))
+ return supHardNtVpSetInfo2(pThis, VERR_SUP_VP_BAD_MZ_OFFSET,
+ "%s: Unexpected e_lfanew value: %#x", pImage->pszName, offNtHdrs);
+ }
+ PIMAGE_NT_HEADERS pNtHdrs = (PIMAGE_NT_HEADERS)&pThis->abFile[offNtHdrs];
+ PIMAGE_NT_HEADERS32 pNtHdrs32 = (PIMAGE_NT_HEADERS32)pNtHdrs;
+ if (pNtHdrs->Signature != IMAGE_NT_SIGNATURE)
+ return supHardNtVpSetInfo2(pThis, VERR_SUP_VP_BAD_IMAGE_SIGNATURE,
+ "%s: No PE signature at %#x: %#x", pImage->pszName, offNtHdrs, pNtHdrs->Signature);
+
+ /*
+ * Do basic header validation.
+ */
+#ifdef RT_ARCH_AMD64
+ if (pNtHdrs->FileHeader.Machine != IMAGE_FILE_MACHINE_AMD64 && !pImage->f32bitResourceDll)
+#else
+ if (pNtHdrs->FileHeader.Machine != IMAGE_FILE_MACHINE_I386)
+#endif
+ return supHardNtVpSetInfo2(pThis, VERR_SUP_VP_UNEXPECTED_IMAGE_MACHINE,
+ "%s: Unexpected machine: %#x", pImage->pszName, pNtHdrs->FileHeader.Machine);
+ bool const fIs32Bit = pNtHdrs->FileHeader.Machine == IMAGE_FILE_MACHINE_I386;
+
+ if (pNtHdrs->FileHeader.SizeOfOptionalHeader != (fIs32Bit ? sizeof(IMAGE_OPTIONAL_HEADER32) : sizeof(IMAGE_OPTIONAL_HEADER64)))
+ return supHardNtVpSetInfo2(pThis, VERR_SUP_VP_BAD_OPTIONAL_HEADER,
+ "%s: Unexpected optional header size: %#x",
+ pImage->pszName, pNtHdrs->FileHeader.SizeOfOptionalHeader);
+
+ if (pNtHdrs->OptionalHeader.Magic != (fIs32Bit ? IMAGE_NT_OPTIONAL_HDR32_MAGIC : IMAGE_NT_OPTIONAL_HDR64_MAGIC))
+ return supHardNtVpSetInfo2(pThis, VERR_SUP_VP_BAD_OPTIONAL_HEADER,
+ "%s: Unexpected optional header magic: %#x", pImage->pszName, pNtHdrs->OptionalHeader.Magic);
+
+ uint32_t cDirs = (fIs32Bit ? pNtHdrs32->OptionalHeader.NumberOfRvaAndSizes : pNtHdrs->OptionalHeader.NumberOfRvaAndSizes);
+ if (cDirs != IMAGE_NUMBEROF_DIRECTORY_ENTRIES)
+ return supHardNtVpSetInfo2(pThis, VERR_SUP_VP_BAD_OPTIONAL_HEADER,
+ "%s: Unexpected data dirs: %#x", pImage->pszName, cDirs);
+
+ /*
+ * Before we start comparing things, store what we need to know from the headers.
+ */
+ uint32_t const cSections = pNtHdrs->FileHeader.NumberOfSections;
+ if (cSections > RT_ELEMENTS(pThis->aSecHdrs))
+ return supHardNtVpSetInfo2(pThis, VERR_SUP_VP_TOO_MANY_SECTIONS,
+ "%s: Too many section headers: %#x", pImage->pszName, cSections);
+ suplibHardenedMemCopy(pThis->aSecHdrs, (fIs32Bit ? (void *)(pNtHdrs32 + 1) : (void *)(pNtHdrs + 1)),
+ cSections * sizeof(IMAGE_SECTION_HEADER));
+
+ uintptr_t const uImageBase = fIs32Bit ? pNtHdrs32->OptionalHeader.ImageBase : pNtHdrs->OptionalHeader.ImageBase;
+ if (uImageBase & PAGE_OFFSET_MASK)
+ return supHardNtVpSetInfo2(pThis, VERR_SUP_VP_BAD_IMAGE_BASE,
+ "%s: Invalid image base: %p", pImage->pszName, uImageBase);
+
+ uint32_t const cbImage = fIs32Bit ? pNtHdrs32->OptionalHeader.SizeOfImage : pNtHdrs->OptionalHeader.SizeOfImage;
+ if (RT_ALIGN_32(pImage->cbImage, PAGE_SIZE) != RT_ALIGN_32(cbImage, PAGE_SIZE) && !pImage->fApiSetSchemaOnlySection1)
+ return supHardNtVpSetInfo2(pThis, VERR_SUP_VP_BAD_IMAGE_SIZE,
+ "%s: SizeOfImage (%#x) isn't close enough to the mapping size (%#x)",
+ pImage->pszName, cbImage, pImage->cbImage);
+ if (cbImage != RTLdrSize(pImage->pCacheEntry->hLdrMod))
+ return supHardNtVpSetInfo2(pThis, VERR_SUP_VP_BAD_IMAGE_SIZE,
+ "%s: SizeOfImage (%#x) differs from what RTLdrSize returns (%#zx)",
+ pImage->pszName, cbImage, RTLdrSize(pImage->pCacheEntry->hLdrMod));
+
+ uint32_t const cbSectAlign = fIs32Bit ? pNtHdrs32->OptionalHeader.SectionAlignment : pNtHdrs->OptionalHeader.SectionAlignment;
+ if ( !RT_IS_POWER_OF_TWO(cbSectAlign)
+ || cbSectAlign < PAGE_SIZE
+ || cbSectAlign > (pImage->fApiSetSchemaOnlySection1 ? _64K : (uint32_t)PAGE_SIZE) )
+ return supHardNtVpSetInfo2(pThis, VERR_SUP_VP_BAD_SECTION_ALIGNMENT_VALUE,
+ "%s: Unexpected SectionAlignment value: %#x", pImage->pszName, cbSectAlign);
+
+ uint32_t const cbFileAlign = fIs32Bit ? pNtHdrs32->OptionalHeader.FileAlignment : pNtHdrs->OptionalHeader.FileAlignment;
+ if (!RT_IS_POWER_OF_TWO(cbFileAlign) || cbFileAlign < 512 || cbFileAlign > PAGE_SIZE || cbFileAlign > cbSectAlign)
+ return supHardNtVpSetInfo2(pThis, VERR_SUP_VP_BAD_FILE_ALIGNMENT_VALUE,
+ "%s: Unexpected FileAlignment value: %#x (cbSectAlign=%#x)",
+ pImage->pszName, cbFileAlign, cbSectAlign);
+
+ uint32_t const cbHeaders = fIs32Bit ? pNtHdrs32->OptionalHeader.SizeOfHeaders : pNtHdrs->OptionalHeader.SizeOfHeaders;
+ uint32_t const cbMinHdrs = offNtHdrs + (fIs32Bit ? sizeof(*pNtHdrs32) : sizeof(*pNtHdrs) )
+ + sizeof(IMAGE_SECTION_HEADER) * cSections;
+ if (cbHeaders < cbMinHdrs)
+ return supHardNtVpSetInfo2(pThis, VERR_SUP_VP_BAD_SIZE_OF_HEADERS,
+ "%s: Headers are too small: %#x < %#x (cSections=%#x)",
+ pImage->pszName, cbHeaders, cbMinHdrs, cSections);
+ uint32_t const cbHdrsFile = RT_ALIGN_32(cbHeaders, cbFileAlign);
+ if (cbHdrsFile > sizeof(pThis->abFile))
+ return supHardNtVpSetInfo2(pThis, VERR_SUP_VP_BAD_SIZE_OF_HEADERS,
+ "%s: Headers are larger than expected: %#x/%#x (expected max %zx)",
+ pImage->pszName, cbHeaders, cbHdrsFile, sizeof(pThis->abFile));
+
+ /*
+ * Save some header fields we might be using later on.
+ */
+ pImage->fImageCharecteristics = pNtHdrs->FileHeader.Characteristics;
+ pImage->fDllCharecteristics = fIs32Bit ? pNtHdrs32->OptionalHeader.DllCharacteristics : pNtHdrs->OptionalHeader.DllCharacteristics;
+
+ /*
+ * Correct the apisetschema image base, size and region rva.
+ */
+ if (pImage->fApiSetSchemaOnlySection1)
+ {
+ pImage->uImageBase -= pThis->aSecHdrs[0].VirtualAddress;
+ pImage->cbImage += pThis->aSecHdrs[0].VirtualAddress;
+ pImage->aRegions[0].uRva = pThis->aSecHdrs[0].VirtualAddress;
+ }
+
+ /*
+ * Get relocated bits.
+ */
+ uint8_t *pbBits;
+ if (pThis->enmKind == SUPHARDNTVPKIND_CHILD_PURIFICATION)
+ rc = supHardNtLdrCacheEntryGetBits(pImage->pCacheEntry, &pbBits, pImage->uImageBase, NULL /*pfnGetImport*/, pThis,
+ pThis->pErrInfo);
+ else
+ rc = supHardNtLdrCacheEntryGetBits(pImage->pCacheEntry, &pbBits, pImage->uImageBase, supHardNtVpGetImport, pThis,
+ pThis->pErrInfo);
+ if (RT_FAILURE(rc))
+ return rc;
+
+ /* XP SP3 does not set ImageBase to load address. It fixes up the image on load time though. */
+ if (g_uNtVerCombined >= SUP_NT_VER_VISTA)
+ {
+ if (fIs32Bit)
+ ((PIMAGE_NT_HEADERS32)&pbBits[offNtHdrs])->OptionalHeader.ImageBase = (uint32_t)pImage->uImageBase;
+ else
+ ((PIMAGE_NT_HEADERS)&pbBits[offNtHdrs])->OptionalHeader.ImageBase = pImage->uImageBase;
+ }
+
+ /*
+ * Figure out areas we should skip during comparison.
+ */
+ uint32_t cSkipAreas = 0;
+ SUPHNTVPSKIPAREA aSkipAreas[7];
+ if (pImage->fNtCreateSectionPatch)
+ {
+ RTLDRADDR uValue;
+ if (pThis->enmKind == SUPHARDNTVPKIND_VERIFY_ONLY)
+ {
+ /* Ignore our NtCreateSection hack. */
+ rc = RTLdrGetSymbolEx(pImage->pCacheEntry->hLdrMod, pbBits, 0, UINT32_MAX, "NtCreateSection", &uValue);
+ if (RT_FAILURE(rc))
+ return supHardNtVpSetInfo2(pThis, rc, "%s: Failed to find 'NtCreateSection': %Rrc", pImage->pszName, rc);
+ aSkipAreas[cSkipAreas].uRva = (uint32_t)uValue;
+ aSkipAreas[cSkipAreas++].cb = ARCH_BITS == 32 ? 5 : 12;
+
+ /* Ignore our LdrLoadDll hack. */
+ rc = RTLdrGetSymbolEx(pImage->pCacheEntry->hLdrMod, pbBits, 0, UINT32_MAX, "LdrLoadDll", &uValue);
+ if (RT_FAILURE(rc))
+ return supHardNtVpSetInfo2(pThis, rc, "%s: Failed to find 'LdrLoadDll': %Rrc", pImage->pszName, rc);
+ aSkipAreas[cSkipAreas].uRva = (uint32_t)uValue;
+ aSkipAreas[cSkipAreas++].cb = ARCH_BITS == 32 ? 5 : 12;
+ }
+
+ /* Ignore our patched LdrInitializeThunk hack. */
+ rc = RTLdrGetSymbolEx(pImage->pCacheEntry->hLdrMod, pbBits, 0, UINT32_MAX, "LdrInitializeThunk", &uValue);
+ if (RT_FAILURE(rc))
+ return supHardNtVpSetInfo2(pThis, rc, "%s: Failed to find 'LdrInitializeThunk': %Rrc", pImage->pszName, rc);
+ aSkipAreas[cSkipAreas].uRva = (uint32_t)uValue;
+ aSkipAreas[cSkipAreas++].cb = 14;
+
+ /* Ignore our patched KiUserApcDispatcher hack. */
+ rc = RTLdrGetSymbolEx(pImage->pCacheEntry->hLdrMod, pbBits, 0, UINT32_MAX, "KiUserApcDispatcher", &uValue);
+ if (RT_FAILURE(rc))
+ return supHardNtVpSetInfo2(pThis, rc, "%s: Failed to find 'KiUserApcDispatcher': %Rrc", pImage->pszName, rc);
+ aSkipAreas[cSkipAreas].uRva = (uint32_t)uValue;
+ aSkipAreas[cSkipAreas++].cb = 14;
+
+#ifndef VBOX_WITHOUT_HARDENDED_XCPT_LOGGING
+ /* Ignore our patched KiUserExceptionDispatcher hack. */
+ rc = RTLdrGetSymbolEx(pImage->pCacheEntry->hLdrMod, pbBits, 0, UINT32_MAX, "KiUserExceptionDispatcher", &uValue);
+ if (RT_FAILURE(rc))
+ return supHardNtVpSetInfo2(pThis, rc, "%s: Failed to find 'KiUserExceptionDispatcher': %Rrc", pImage->pszName, rc);
+ aSkipAreas[cSkipAreas].uRva = (uint32_t)uValue + (HC_ARCH_BITS == 64);
+ aSkipAreas[cSkipAreas++].cb = HC_ARCH_BITS == 64 ? 13 : 12;
+#endif
+
+ /* LdrSystemDllInitBlock is filled in by the kernel. It mainly contains addresses of 32-bit ntdll method for wow64. */
+ rc = RTLdrGetSymbolEx(pImage->pCacheEntry->hLdrMod, pbBits, 0, UINT32_MAX, "LdrSystemDllInitBlock", &uValue);
+ if (RT_SUCCESS(rc))
+ {
+ aSkipAreas[cSkipAreas].uRva = (uint32_t)uValue;
+ aSkipAreas[cSkipAreas++].cb = RT_MAX(pbBits[(uint32_t)uValue], 0x50);
+ }
+
+ Assert(cSkipAreas <= RT_ELEMENTS(aSkipAreas));
+ }
+
+ /*
+ * Compare the file header with the loaded bits. The loader will fiddle
+ * with image base, changing it to the actual load address.
+ */
+ if (!pImage->fApiSetSchemaOnlySection1)
+ {
+ rc = supHardNtVpFileMemCompareSection(pThis, pImage, 0 /*uRva*/, cbHdrsFile, pbBits, -1, NULL, 0, PAGE_READONLY);
+ if (RT_FAILURE(rc))
+ return rc;
+
+ rc = supHardNtVpCheckSectionProtection(pThis, pImage, 0 /*uRva*/, cbHdrsFile, PAGE_READONLY);
+ if (RT_FAILURE(rc))
+ return rc;
+ }
+
+ /*
+ * Validate sections:
+ * - Check them against the mapping regions.
+ * - Check section bits according to enmKind.
+ */
+ uint32_t fPrevProt = PAGE_READONLY;
+ uint32_t uRva = cbHdrsFile;
+ for (uint32_t i = 0; i < cSections; i++)
+ {
+ /* Validate the section. */
+ uint32_t uSectRva = pThis->aSecHdrs[i].VirtualAddress;
+ if (uSectRva < uRva || uSectRva > cbImage || RT_ALIGN_32(uSectRva, cbSectAlign) != uSectRva)
+ return supHardNtVpSetInfo2(pThis, VERR_SUP_VP_BAD_SECTION_RVA,
+ "%s: Section %u: Invalid virtual address: %#x (uRva=%#x, cbImage=%#x, cbSectAlign=%#x)",
+ pImage->pszName, i, uSectRva, uRva, cbImage, cbSectAlign);
+ uint32_t cbMap = pThis->aSecHdrs[i].Misc.VirtualSize;
+ if (cbMap > cbImage || uRva + cbMap > cbImage)
+ return supHardNtVpSetInfo2(pThis, VERR_SUP_VP_BAD_SECTION_VIRTUAL_SIZE,
+ "%s: Section %u: Invalid virtual size: %#x (uSectRva=%#x, uRva=%#x, cbImage=%#x)",
+ pImage->pszName, i, cbMap, uSectRva, uRva, cbImage);
+ uint32_t cbFile = pThis->aSecHdrs[i].SizeOfRawData;
+ if (cbFile != RT_ALIGN_32(cbFile, cbFileAlign) || cbFile > RT_ALIGN_32(cbMap, cbSectAlign))
+ return supHardNtVpSetInfo2(pThis, VERR_SUP_VP_BAD_SECTION_FILE_SIZE,
+ "%s: Section %u: Invalid file size: %#x (cbMap=%#x, uSectRva=%#x)",
+ pImage->pszName, i, cbFile, cbMap, uSectRva);
+
+ /* Validate the protection and bits. */
+ if (!pImage->fApiSetSchemaOnlySection1 || i == 0)
+ {
+ uint32_t fProt;
+ switch (pThis->aSecHdrs[i].Characteristics & (IMAGE_SCN_MEM_EXECUTE | IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_WRITE))
+ {
+ case IMAGE_SCN_MEM_READ:
+ fProt = PAGE_READONLY;
+ break;
+ case IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_WRITE:
+ fProt = PAGE_READWRITE;
+ if ( pThis->enmKind != SUPHARDNTVPKIND_VERIFY_ONLY
+ && pThis->enmKind != SUPHARDNTVPKIND_CHILD_PURIFICATION
+ && !suplibHardenedMemComp(pThis->aSecHdrs[i].Name, ".mrdata", 8)) /* w8.1, ntdll. Changed by proc init. */
+ fProt = PAGE_READONLY;
+ break;
+ case IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_EXECUTE:
+ fProt = PAGE_EXECUTE_READ;
+ break;
+ case IMAGE_SCN_MEM_EXECUTE:
+ fProt = PAGE_EXECUTE;
+ break;
+ case IMAGE_SCN_MEM_EXECUTE | IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_WRITE:
+ /* Only the executable is allowed to have this section,
+ and it's protected after we're done patching. */
+ if (!pImage->fDll)
+ {
+ if (pThis->enmKind == SUPHARDNTVPKIND_CHILD_PURIFICATION)
+ fProt = PAGE_EXECUTE_READWRITE;
+ else
+ fProt = PAGE_EXECUTE_READ;
+ break;
+ }
+ default:
+ return supHardNtVpSetInfo2(pThis, VERR_SUP_VP_UNEXPECTED_SECTION_FLAGS,
+ "%s: Section %u: Unexpected characteristics: %#x (uSectRva=%#x, cbMap=%#x)",
+ pImage->pszName, i, pThis->aSecHdrs[i].Characteristics, uSectRva, cbMap);
+ }
+
+ /* The section bits. Child purification verifies all, normal
+ verification verifies all except where the executable is
+ concerned (due to opening vboxdrv during early process init). */
+ if ( ( (pThis->aSecHdrs[i].Characteristics & (IMAGE_SCN_MEM_EXECUTE | IMAGE_SCN_CNT_CODE))
+ && !(pThis->aSecHdrs[i].Characteristics & IMAGE_SCN_MEM_WRITE))
+ || (pThis->aSecHdrs[i].Characteristics & (IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_WRITE)) == IMAGE_SCN_MEM_READ
+ || (pThis->enmKind == SUPHARDNTVPKIND_VERIFY_ONLY && pImage->fDll)
+ || pThis->enmKind == SUPHARDNTVPKIND_CHILD_PURIFICATION)
+ {
+ rc = VINF_SUCCESS;
+ if (uRva < uSectRva && !pImage->fApiSetSchemaOnlySection1) /* Any gap worth checking? */
+ rc = supHardNtVpFileMemCompareSection(pThis, pImage, uRva, uSectRva - uRva, pbBits + uRva,
+ i - 1, NULL, 0, fPrevProt);
+ if (RT_SUCCESS(rc))
+ rc = supHardNtVpFileMemCompareSection(pThis, pImage, uSectRva, cbMap, pbBits + uSectRva,
+ i, aSkipAreas, cSkipAreas, fProt);
+ if (RT_SUCCESS(rc))
+ {
+ uint32_t cbMapAligned = i + 1 < cSections && !pImage->fApiSetSchemaOnlySection1
+ ? RT_ALIGN_32(cbMap, cbSectAlign) : RT_ALIGN_32(cbMap, PAGE_SIZE);
+ if (cbMapAligned > cbMap)
+ rc = supHardNtVpFileMemCompareSection(pThis, pImage, uSectRva + cbMap, cbMapAligned - cbMap,
+ g_abRTZeroPage, i, NULL, 0, fProt);
+ }
+ if (RT_FAILURE(rc))
+ return rc;
+ }
+
+ /* The protection (must be checked afterwards!). */
+ rc = supHardNtVpCheckSectionProtection(pThis, pImage, uSectRva, RT_ALIGN_32(cbMap, PAGE_SIZE), fProt);
+ if (RT_FAILURE(rc))
+ return rc;
+
+ fPrevProt = fProt;
+ }
+
+ /* Advance the RVA. */
+ uRva = uSectRva + RT_ALIGN_32(cbMap, cbSectAlign);
+ }
+
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * Verifies the signature of the given image on disk, then checks if the memory
+ * mapping matches what we verified.
+ *
+ * @returns VBox status code.
+ * @param pThis The process scanning state structure (for the
+ * two scratch buffers).
+ * @param pImage The image data collected during the address
+ * space scan.
+ */
+static int supHardNtVpVerifyImage(PSUPHNTVPSTATE pThis, PSUPHNTVPIMAGE pImage)
+{
+ /*
+ * Validate the file signature first, then do the memory compare.
+ */
+ int rc;
+ if ( pImage->pCacheEntry != NULL
+ && pImage->pCacheEntry->hLdrMod != NIL_RTLDRMOD)
+ {
+ rc = supHardNtLdrCacheEntryVerify(pImage->pCacheEntry, pImage->Name.UniStr.Buffer, pThis->pErrInfo);
+ if (RT_SUCCESS(rc))
+ rc = supHardNtVpVerifyImageMemoryCompare(pThis, pImage);
+ }
+ else
+ rc = supHardNtVpSetInfo2(pThis, VERR_OPEN_FAILED, "pCacheEntry/hLdrMod is NIL! Impossible!");
+ return rc;
+}
+
+
+/**
+ * Verifies that there is only one thread in the process.
+ *
+ * @returns VBox status code.
+ * @param hProcess The process.
+ * @param hThread The thread.
+ * @param pErrInfo Pointer to error info structure. Optional.
+ */
+DECLHIDDEN(int) supHardNtVpThread(HANDLE hProcess, HANDLE hThread, PRTERRINFO pErrInfo)
+{
+ RT_NOREF1(hProcess);
+
+ /*
+ * Use the ThreadAmILastThread request to check that there is only one
+ * thread in the process.
+ * Seems this isn't entirely reliable when hThread isn't the current thread?
+ */
+ ULONG cbIgn = 0;
+ ULONG fAmI = 0;
+ NTSTATUS rcNt = NtQueryInformationThread(hThread, ThreadAmILastThread, &fAmI, sizeof(fAmI), &cbIgn);
+ if (!NT_SUCCESS(rcNt))
+ return supHardNtVpSetInfo1(pErrInfo, VERR_SUP_VP_NT_QI_THREAD_ERROR,
+ "NtQueryInformationThread/ThreadAmILastThread -> %#x", rcNt);
+ if (!fAmI)
+ return supHardNtVpSetInfo1(pErrInfo, VERR_SUP_VP_THREAD_NOT_ALONE,
+ "More than one thread in process");
+
+ /** @todo Would be nice to verify the relationship between hProcess and hThread
+ * as well... */
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * Verifies that there isn't a debugger attached to the process.
+ *
+ * @returns VBox status code.
+ * @param hProcess The process.
+ * @param pErrInfo Pointer to error info structure. Optional.
+ */
+DECLHIDDEN(int) supHardNtVpDebugger(HANDLE hProcess, PRTERRINFO pErrInfo)
+{
+#ifndef VBOX_WITHOUT_DEBUGGER_CHECKS
+ /*
+ * Use the ProcessDebugPort request to check there is no debugger
+ * currently attached to the process.
+ */
+ ULONG cbIgn = 0;
+ uintptr_t uPtr = ~(uintptr_t)0;
+ NTSTATUS rcNt = NtQueryInformationProcess(hProcess,
+ ProcessDebugPort,
+ &uPtr, sizeof(uPtr), &cbIgn);
+ if (!NT_SUCCESS(rcNt))
+ return supHardNtVpSetInfo1(pErrInfo, VERR_SUP_VP_NT_QI_PROCESS_DBG_PORT_ERROR,
+ "NtQueryInformationProcess/ProcessDebugPort -> %#x", rcNt);
+ if (uPtr != 0)
+ return supHardNtVpSetInfo1(pErrInfo, VERR_SUP_VP_DEBUGGED,
+ "Debugger attached (%#zx)", uPtr);
+#else
+ RT_NOREF2(hProcess, pErrInfo);
+#endif /* !VBOX_WITHOUT_DEBUGGER_CHECKS */
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * Matches two UNICODE_STRING structures in a case sensitive fashion.
+ *
+ * @returns true if equal, false if not.
+ * @param pUniStr1 The first unicode string.
+ * @param pUniStr2 The first unicode string.
+ */
+static bool supHardNtVpAreUniStringsEqual(PCUNICODE_STRING pUniStr1, PCUNICODE_STRING pUniStr2)
+{
+ if (pUniStr1->Length != pUniStr2->Length)
+ return false;
+ return suplibHardenedMemComp(pUniStr1->Buffer, pUniStr2->Buffer, pUniStr1->Length) == 0;
+}
+
+
+/**
+ * Performs a case insensitive comparison of an ASCII and an UTF-16 file name.
+ *
+ * @returns true / false
+ * @param pszName1 The ASCII name.
+ * @param pwszName2 The UTF-16 name.
+ */
+static bool supHardNtVpAreNamesEqual(const char *pszName1, PCRTUTF16 pwszName2)
+{
+ for (;;)
+ {
+ char ch1 = *pszName1++;
+ RTUTF16 wc2 = *pwszName2++;
+ if (ch1 != wc2)
+ {
+ ch1 = RT_C_TO_LOWER(ch1);
+ wc2 = wc2 < 0x80 ? RT_C_TO_LOWER(wc2) : wc2;
+ if (ch1 != wc2)
+ return false;
+ }
+ if (!ch1)
+ return true;
+ }
+}
+
+
+/**
+ * Compares two paths, expanding 8.3 short names as needed.
+ *
+ * @returns true / false.
+ * @param pUniStr1 The first path. Must be zero terminated!
+ * @param pUniStr2 The second path. Must be zero terminated!
+ */
+static bool supHardNtVpArePathsEqual(PCUNICODE_STRING pUniStr1, PCUNICODE_STRING pUniStr2)
+{
+ /* Both strings must be null terminated. */
+ Assert(pUniStr1->Buffer[pUniStr1->Length / sizeof(WCHAR)] == '\0');
+ Assert(pUniStr2->Buffer[pUniStr1->Length / sizeof(WCHAR)] == '\0');
+
+ /* Simple compare first.*/
+ if (supHardNtVpAreUniStringsEqual(pUniStr1, pUniStr2))
+ return true;
+
+ /* Make long names if needed. */
+ UNICODE_STRING UniStrLong1 = { 0, 0, NULL };
+ if (RTNtPathFindPossible8dot3Name(pUniStr1->Buffer))
+ {
+ int rc = RTNtPathExpand8dot3PathA(pUniStr1, false /*fPathOnly*/, &UniStrLong1);
+ if (RT_SUCCESS(rc))
+ pUniStr1 = &UniStrLong1;
+ }
+
+ UNICODE_STRING UniStrLong2 = { 0, 0, NULL };
+ if (RTNtPathFindPossible8dot3Name(pUniStr2->Buffer))
+ {
+ int rc = RTNtPathExpand8dot3PathA(pUniStr2, false /*fPathOnly*/, &UniStrLong2);
+ if (RT_SUCCESS(rc))
+ pUniStr2 = &UniStrLong2;
+ }
+
+ /* Compare again. */
+ bool fCompare = supHardNtVpAreUniStringsEqual(pUniStr1, pUniStr2);
+
+ /* Clean up. */
+ if (UniStrLong1.Buffer)
+ RTUtf16Free(UniStrLong1.Buffer);
+ if (UniStrLong2.Buffer)
+ RTUtf16Free(UniStrLong2.Buffer);
+
+ return fCompare;
+}
+
+
+/**
+ * Records an additional memory region for an image.
+ *
+ * May trash pThis->abMemory.
+ *
+ * @returns VBox status code.
+ * @retval VINF_OBJECT_DESTROYED if we've unmapped the image (child
+ * purification only).
+ * @param pThis The process scanning state structure.
+ * @param pImage The new image structure. Only the unicode name
+ * buffer is valid (it's zero-terminated).
+ * @param pMemInfo The memory information for the image.
+ */
+static int supHardNtVpNewImage(PSUPHNTVPSTATE pThis, PSUPHNTVPIMAGE pImage, PMEMORY_BASIC_INFORMATION pMemInfo)
+{
+ /*
+ * If the filename or path contains short names, we have to get the long
+ * path so that we will recognize the DLLs and their location.
+ */
+ int rc83Exp = VERR_IGNORED;
+ PUNICODE_STRING pLongName = &pImage->Name.UniStr;
+ if (RTNtPathFindPossible8dot3Name(pLongName->Buffer))
+ {
+ AssertCompile(sizeof(pThis->abMemory) > sizeof(pImage->Name));
+ PUNICODE_STRING pTmp = (PUNICODE_STRING)pThis->abMemory;
+ pTmp->MaximumLength = (USHORT)RT_MIN(_64K - 1, sizeof(pThis->abMemory) - sizeof(*pTmp)) - sizeof(RTUTF16);
+ pTmp->Length = pImage->Name.UniStr.Length;
+ pTmp->Buffer = (PRTUTF16)(pTmp + 1);
+ memcpy(pTmp->Buffer, pLongName->Buffer, pLongName->Length + sizeof(RTUTF16));
+
+ rc83Exp = RTNtPathExpand8dot3Path(pTmp, false /*fPathOnly*/);
+ Assert(rc83Exp == VINF_SUCCESS);
+ Assert(pTmp->Buffer[pTmp->Length / sizeof(RTUTF16)] == '\0');
+ if (rc83Exp == VINF_SUCCESS)
+ SUP_DPRINTF(("supHardNtVpNewImage: 8dot3 -> long: '%ls' -> '%ls'\n", pLongName->Buffer, pTmp->Buffer));
+ else
+ SUP_DPRINTF(("supHardNtVpNewImage: RTNtPathExpand8dot3Path returns %Rrc for '%ls' (-> '%ls')\n",
+ rc83Exp, pLongName->Buffer, pTmp->Buffer));
+
+ pLongName = pTmp;
+ }
+
+ /*
+ * Extract the final component.
+ */
+ RTUTF16 wc;
+ unsigned cwcDirName = pLongName->Length / sizeof(WCHAR);
+ PCRTUTF16 pwszFilename = &pLongName->Buffer[cwcDirName];
+ while ( cwcDirName > 0
+ && (wc = pwszFilename[-1]) != '\\'
+ && wc != '/'
+ && wc != ':')
+ {
+ pwszFilename--;
+ cwcDirName--;
+ }
+ if (!*pwszFilename)
+ return supHardNtVpSetInfo2(pThis, VERR_SUP_VP_NO_IMAGE_MAPPING_NAME,
+ "Empty filename (len=%u) for image at %p.", pLongName->Length, pMemInfo->BaseAddress);
+
+ /*
+ * Drop trailing slashes from the directory name.
+ */
+ while ( cwcDirName > 0
+ && ( pLongName->Buffer[cwcDirName - 1] == '\\'
+ || pLongName->Buffer[cwcDirName - 1] == '/'))
+ cwcDirName--;
+
+ /*
+ * Match it against known DLLs.
+ */
+ pImage->pszName = NULL;
+ for (uint32_t i = 0; i < RT_ELEMENTS(g_apszSupNtVpAllowedDlls); i++)
+ if (supHardNtVpAreNamesEqual(g_apszSupNtVpAllowedDlls[i], pwszFilename))
+ {
+ pImage->pszName = g_apszSupNtVpAllowedDlls[i];
+ pImage->fDll = true;
+
+#ifndef VBOX_PERMIT_VISUAL_STUDIO_PROFILING
+ /* The directory name must match the one we've got for System32. */
+ if ( ( cwcDirName * sizeof(WCHAR) != g_System32NtPath.UniStr.Length
+ || suplibHardenedMemComp(pLongName->Buffer, g_System32NtPath.UniStr.Buffer, cwcDirName * sizeof(WCHAR)) )
+# ifdef VBOX_PERMIT_MORE
+ && ( pImage->pszName[0] != 'a'
+ || pImage->pszName[1] != 'c'
+ || !supHardViIsAppPatchDir(pLongName->Buffer, pLongName->Length / sizeof(WCHAR)) )
+# endif
+ )
+ return supHardNtVpSetInfo2(pThis, VERR_SUP_VP_NON_SYSTEM32_DLL,
+ "Expected %ls to be loaded from %ls.",
+ pLongName->Buffer, g_System32NtPath.UniStr.Buffer);
+# ifdef VBOX_PERMIT_MORE
+ if (g_uNtVerCombined < SUP_NT_VER_W70 && i >= VBOX_PERMIT_MORE_FIRST_IDX)
+ pImage->pszName = NULL; /* hard limit: user32.dll is unwanted prior to w7. */
+# endif
+
+#endif /* VBOX_PERMIT_VISUAL_STUDIO_PROFILING */
+ break;
+ }
+ if (!pImage->pszName)
+ {
+ /*
+ * Not a known DLL, is it a known executable?
+ */
+ for (uint32_t i = 0; i < RT_ELEMENTS(g_apszSupNtVpAllowedVmExes); i++)
+ if (supHardNtVpAreNamesEqual(g_apszSupNtVpAllowedVmExes[i], pwszFilename))
+ {
+ pImage->pszName = g_apszSupNtVpAllowedVmExes[i];
+ pImage->fDll = false;
+ break;
+ }
+ }
+ if (!pImage->pszName)
+ {
+ /*
+ * Unknown image.
+ *
+ * If we're cleaning up a child process, we can unmap the offending
+ * DLL... Might have interesting side effects, or at least interesting
+ * as in "may you live in interesting times".
+ */
+#ifdef IN_RING3
+ if ( pMemInfo->AllocationBase == pMemInfo->BaseAddress
+ && pThis->enmKind == SUPHARDNTVPKIND_CHILD_PURIFICATION)
+ {
+ SUP_DPRINTF(("supHardNtVpScanVirtualMemory: Unmapping image mem at %p (%p LB %#zx) - '%ls'\n",
+ pMemInfo->AllocationBase, pMemInfo->BaseAddress, pMemInfo->RegionSize, pwszFilename));
+ NTSTATUS rcNt = NtUnmapViewOfSection(pThis->hProcess, pMemInfo->AllocationBase);
+ if (NT_SUCCESS(rcNt))
+ return VINF_OBJECT_DESTROYED;
+ pThis->cFixes++;
+ SUP_DPRINTF(("supHardNtVpScanVirtualMemory: NtUnmapViewOfSection(,%p) failed: %#x\n", pMemInfo->AllocationBase, rcNt));
+ }
+ else if (pThis->enmKind == SUPHARDNTVPKIND_SELF_PURIFICATION_LIMITED)
+ {
+ SUP_DPRINTF(("supHardNtVpScanVirtualMemory: Ignoring unknown mem at %p LB %#zx (base %p) - '%ls'\n",
+ pMemInfo->BaseAddress, pMemInfo->RegionSize, pMemInfo->AllocationBase, pwszFilename));
+ return VINF_OBJECT_DESTROYED;
+ }
+#endif
+ /*
+ * Special error message if we can.
+ */
+ if ( pMemInfo->AllocationBase == pMemInfo->BaseAddress
+ && ( supHardNtVpAreNamesEqual("sysfer.dll", pwszFilename)
+ || supHardNtVpAreNamesEqual("sysfer32.dll", pwszFilename)
+ || supHardNtVpAreNamesEqual("sysfer64.dll", pwszFilename)
+ || supHardNtVpAreNamesEqual("sysfrethunk.dll", pwszFilename)) )
+ {
+ supHardNtVpSetInfo2(pThis, VERR_SUP_VP_SYSFER_DLL,
+ "Found %ls at %p - This is probably part of Symantec Endpoint Protection. \n"
+ "You or your admin need to add and exception to the Application and Device Control (ADC) "
+ "component (or disable it) to prevent ADC from injecting itself into the VirtualBox VM processes. "
+ "See http://www.symantec.com/connect/articles/creating-application-control-exclusions-symantec-endpoint-protection-121"
+ , pLongName->Buffer, pMemInfo->BaseAddress);
+ return pThis->rcResult = VERR_SUP_VP_SYSFER_DLL; /* Try make sure this is what the user sees first! */
+ }
+ return supHardNtVpSetInfo2(pThis, VERR_SUP_VP_NOT_KNOWN_DLL_OR_EXE,
+ "Unknown image file %ls at %p. (rc83Exp=%Rrc)",
+ pLongName->Buffer, pMemInfo->BaseAddress, rc83Exp);
+ }
+
+ /*
+ * Checks for multiple mappings of the same DLL but with different image file paths.
+ */
+ uint32_t i = pThis->cImages;
+ while (i-- > 1)
+ if (pImage->pszName == pThis->aImages[i].pszName)
+ return supHardNtVpSetInfo2(pThis, VERR_SUP_VP_DUPLICATE_DLL_MAPPING,
+ "Duplicate image entries for %s: %ls and %ls",
+ pImage->pszName, pImage->Name.UniStr.Buffer, pThis->aImages[i].Name.UniStr.Buffer);
+
+ /*
+ * Since it's a new image, we expect to be at the start of the mapping now.
+ */
+ if (pMemInfo->AllocationBase != pMemInfo->BaseAddress)
+ return supHardNtVpSetInfo2(pThis, VERR_SUP_VP_IMAGE_MAPPING_BASE_ERROR,
+ "Invalid AllocationBase/BaseAddress for %s: %p vs %p.",
+ pImage->pszName, pMemInfo->AllocationBase, pMemInfo->BaseAddress);
+
+ /*
+ * Check for size/rva overflow.
+ */
+ if (pMemInfo->RegionSize >= _2G)
+ return supHardNtVpSetInfo2(pThis, VERR_SUP_VP_TOO_LARGE_REGION,
+ "Region 0 of image %s is too large: %p.", pImage->pszName, pMemInfo->RegionSize);
+
+ /*
+ * Fill in details from the memory info.
+ */
+ pImage->uImageBase = (uintptr_t)pMemInfo->AllocationBase;
+ pImage->cbImage = pMemInfo->RegionSize;
+ pImage->pCacheEntry= NULL;
+ pImage->cRegions = 1;
+ pImage->aRegions[0].uRva = 0;
+ pImage->aRegions[0].cb = (uint32_t)pMemInfo->RegionSize;
+ pImage->aRegions[0].fProt = pMemInfo->Protect;
+
+ if (suplibHardenedStrCmp(pImage->pszName, "ntdll.dll") == 0)
+ pImage->fNtCreateSectionPatch = true;
+ else if (suplibHardenedStrCmp(pImage->pszName, "apisetschema.dll") == 0)
+ pImage->fApiSetSchemaOnlySection1 = true; /** @todo Check the ApiSetMap field in the PEB. */
+#ifdef VBOX_PERMIT_MORE
+ else if (suplibHardenedStrCmp(pImage->pszName, "acres.dll") == 0)
+ pImage->f32bitResourceDll = true;
+#endif
+
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * Records an additional memory region for an image.
+ *
+ * @returns VBox status code.
+ * @param pThis The process scanning state structure.
+ * @param pImage The image.
+ * @param pMemInfo The memory information for the region.
+ */
+static int supHardNtVpAddRegion(PSUPHNTVPSTATE pThis, PSUPHNTVPIMAGE pImage, PMEMORY_BASIC_INFORMATION pMemInfo)
+{
+ /*
+ * Make sure the base address matches.
+ */
+ if (pImage->uImageBase != (uintptr_t)pMemInfo->AllocationBase)
+ return supHardNtVpSetInfo2(pThis, VERR_SUPLIB_NT_PROCESS_UNTRUSTED_3,
+ "Base address mismatch for %s: have %p, found %p for region %p LB %#zx.",
+ pImage->pszName, pImage->uImageBase, pMemInfo->AllocationBase,
+ pMemInfo->BaseAddress, pMemInfo->RegionSize);
+
+ /*
+ * Check for size and rva overflows.
+ */
+ uintptr_t uRva = (uintptr_t)pMemInfo->BaseAddress - pImage->uImageBase;
+ if (pMemInfo->RegionSize >= _2G)
+ return supHardNtVpSetInfo2(pThis, VERR_SUP_VP_TOO_LARGE_REGION,
+ "Region %u of image %s is too large: %p/%p.", pImage->pszName, pMemInfo->RegionSize, uRva);
+ if (uRva >= _2G)
+ return supHardNtVpSetInfo2(pThis, VERR_SUP_VP_TOO_HIGH_REGION_RVA,
+ "Region %u of image %s is too high: %p/%p.", pImage->pszName, pMemInfo->RegionSize, uRva);
+
+
+ /*
+ * Record the region.
+ */
+ uint32_t iRegion = pImage->cRegions;
+ if (iRegion + 1 >= RT_ELEMENTS(pImage->aRegions))
+ return supHardNtVpSetInfo2(pThis, VERR_SUP_VP_TOO_MANY_IMAGE_REGIONS,
+ "Too many regions for %s.", pImage->pszName);
+ pImage->aRegions[iRegion].uRva = (uint32_t)uRva;
+ pImage->aRegions[iRegion].cb = (uint32_t)pMemInfo->RegionSize;
+ pImage->aRegions[iRegion].fProt = pMemInfo->Protect;
+ pImage->cbImage = pImage->aRegions[iRegion].uRva + pImage->aRegions[iRegion].cb;
+ pImage->cRegions++;
+ pImage->fApiSetSchemaOnlySection1 = false;
+
+ return VINF_SUCCESS;
+}
+
+
+#ifdef IN_RING3
+/**
+ * Frees (or replaces) executable memory of allocation type private.
+ *
+ * @returns True if nothing really bad happen, false if to quit ASAP because we
+ * killed the process being scanned.
+ * @param pThis The process scanning state structure. Details
+ * about images are added to this.
+ * @param hProcess The process to verify.
+ * @param pMemInfo The information we've got on this private
+ * executable memory.
+ */
+static bool supHardNtVpFreeOrReplacePrivateExecMemory(PSUPHNTVPSTATE pThis, HANDLE hProcess,
+ MEMORY_BASIC_INFORMATION const *pMemInfo)
+{
+ NTSTATUS rcNt;
+
+ /*
+ * Try figure the entire allocation size. Free/Alloc may fail otherwise.
+ */
+ PVOID pvFree = pMemInfo->AllocationBase;
+ SIZE_T cbFree = pMemInfo->RegionSize + ((uintptr_t)pMemInfo->BaseAddress - (uintptr_t)pMemInfo->AllocationBase);
+ for (;;)
+ {
+ SIZE_T cbActual = 0;
+ MEMORY_BASIC_INFORMATION MemInfo2 = { 0, 0, 0, 0, 0, 0, 0 };
+ uintptr_t uPtrNext = (uintptr_t)pvFree + cbFree;
+ rcNt = g_pfnNtQueryVirtualMemory(hProcess,
+ (void const *)uPtrNext,
+ MemoryBasicInformation,
+ &MemInfo2,
+ sizeof(MemInfo2),
+ &cbActual);
+ if (!NT_SUCCESS(rcNt))
+ break;
+ if (pMemInfo->AllocationBase != MemInfo2.AllocationBase)
+ break;
+ if (MemInfo2.RegionSize == 0)
+ break;
+ cbFree += MemInfo2.RegionSize;
+ }
+ SUP_DPRINTF(("supHardNtVpFreeOrReplacePrivateExecMemory: %s exec mem at %p (LB %#zx, %p LB %#zx)\n",
+ pThis->fFlags & SUPHARDNTVP_F_EXEC_ALLOC_REPLACE_WITH_RW ? "Replacing" : "Freeing",
+ pvFree, cbFree, pMemInfo->BaseAddress, pMemInfo->RegionSize));
+
+ /*
+ * In the BSOD workaround mode, we need to make a copy of the memory before
+ * freeing it. Bird abuses this code for logging purposes too.
+ */
+ uintptr_t uCopySrc = (uintptr_t)pvFree;
+ size_t cbCopy = 0;
+ void *pvCopy = NULL;
+ //if (pThis->fFlags & SUPHARDNTVP_F_EXEC_ALLOC_REPLACE_WITH_RW)
+ {
+ cbCopy = cbFree;
+ pvCopy = RTMemAllocZ(cbCopy);
+ if (!pvCopy)
+ {
+ supHardNtVpSetInfo2(pThis, VERR_SUP_VP_REPLACE_VIRTUAL_MEMORY_FAILED, "RTMemAllocZ(%#zx) failed", cbCopy);
+ return true;
+ }
+
+ rcNt = supHardNtVpReadMem(hProcess, uCopySrc, pvCopy, cbCopy);
+ if (!NT_SUCCESS(rcNt))
+ supHardNtVpSetInfo2(pThis, VERR_SUP_VP_REPLACE_VIRTUAL_MEMORY_FAILED,
+ "Error reading data from original alloc: %#x (%p LB %#zx)", rcNt, uCopySrc, cbCopy, rcNt);
+ for (size_t off = 0; off < cbCopy; off += 256)
+ {
+ size_t const cbChunk = RT_MIN(256, cbCopy - off);
+ void const *pvChunk = (uint8_t const *)pvCopy + off;
+ if (!ASMMemIsZero(pvChunk, cbChunk))
+ SUP_DPRINTF(("%.*RhxD\n", cbChunk, pvChunk));
+ }
+ if (pThis->fFlags & SUPHARDNTVP_F_EXEC_ALLOC_REPLACE_WITH_RW)
+ supR3HardenedLogFlush();
+ }
+
+ /*
+ * Free the memory.
+ */
+ for (uint32_t i = 0; i < 10; i++)
+ {
+ PVOID pvFreeInOut = pvFree;
+ SIZE_T cbFreeInOut = 0;
+ rcNt = NtFreeVirtualMemory(hProcess, &pvFreeInOut, &cbFreeInOut, MEM_RELEASE);
+ if (NT_SUCCESS(rcNt))
+ {
+ SUP_DPRINTF(("supHardNtVpFreeOrReplacePrivateExecMemory: Free attempt #1 succeeded: %#x [%p/%p LB 0/%#zx]\n",
+ rcNt, pvFree, pvFreeInOut, cbFreeInOut));
+ supR3HardenedLogFlush();
+ }
+ else
+ {
+ SUP_DPRINTF(("supHardNtVpFreeOrReplacePrivateExecMemory: Free attempt #1 failed: %#x [%p LB 0]\n", rcNt, pvFree));
+ supR3HardenedLogFlush();
+ pvFreeInOut = pvFree;
+ cbFreeInOut = cbFree;
+ rcNt = NtFreeVirtualMemory(hProcess, &pvFreeInOut, &cbFreeInOut, MEM_RELEASE);
+ if (NT_SUCCESS(rcNt))
+ {
+ SUP_DPRINTF(("supHardNtVpFreeOrReplacePrivateExecMemory: Free attempt #2 succeeded: %#x [%p/%p LB %#zx/%#zx]\n",
+ rcNt, pvFree, pvFreeInOut, cbFree, cbFreeInOut));
+ supR3HardenedLogFlush();
+ }
+ else
+ {
+ SUP_DPRINTF(("supHardNtVpFreeOrReplacePrivateExecMemory: Free attempt #2 failed: %#x [%p LB %#zx]\n",
+ rcNt, pvFree, cbFree));
+ supR3HardenedLogFlush();
+ pvFreeInOut = pMemInfo->BaseAddress;
+ cbFreeInOut = pMemInfo->RegionSize;
+ rcNt = NtFreeVirtualMemory(hProcess, &pvFreeInOut, &cbFreeInOut, MEM_RELEASE);
+ if (NT_SUCCESS(rcNt))
+ {
+ pvFree = pMemInfo->BaseAddress;
+ cbFree = pMemInfo->RegionSize;
+ SUP_DPRINTF(("supHardNtVpFreeOrReplacePrivateExecMemory: Free attempt #3 succeeded [%p LB %#zx]\n",
+ pvFree, cbFree));
+ supR3HardenedLogFlush();
+ }
+ else
+ supHardNtVpSetInfo2(pThis, VERR_SUP_VP_FREE_VIRTUAL_MEMORY_FAILED,
+ "NtFreeVirtualMemory [%p LB %#zx and %p LB %#zx] failed: %#x",
+ pvFree, cbFree, pMemInfo->BaseAddress, pMemInfo->RegionSize, rcNt);
+ }
+ }
+
+ /*
+ * Query the region again, redo the free operation if there's still memory there.
+ */
+ if (!NT_SUCCESS(rcNt))
+ break;
+ SIZE_T cbActual = 0;
+ MEMORY_BASIC_INFORMATION MemInfo3 = { 0, 0, 0, 0, 0, 0, 0 };
+ NTSTATUS rcNt2 = g_pfnNtQueryVirtualMemory(hProcess, pvFree, MemoryBasicInformation,
+ &MemInfo3, sizeof(MemInfo3), &cbActual);
+ if (!NT_SUCCESS(rcNt2))
+ break;
+ SUP_DPRINTF(("supHardNtVpFreeOrReplacePrivateExecMemory: QVM after free %u: [%p]/%p LB %#zx s=%#x ap=%#x rp=%#p\n",
+ i, MemInfo3.AllocationBase, MemInfo3.BaseAddress, MemInfo3.RegionSize, MemInfo3.State,
+ MemInfo3.AllocationProtect, MemInfo3.Protect));
+ supR3HardenedLogFlush();
+ if (MemInfo3.State == MEM_FREE || !(pThis->fFlags & SUPHARDNTVP_F_EXEC_ALLOC_REPLACE_WITH_RW))
+ break;
+ NtYieldExecution();
+ SUP_DPRINTF(("supHardNtVpFreeOrReplacePrivateExecMemory: Retrying free...\n"));
+ supR3HardenedLogFlush();
+ }
+
+ /*
+ * Restore memory as non-executable - Kludge for Trend Micro sakfile.sys
+ * and Digital Guardian dgmaster.sys BSODs.
+ */
+ if (NT_SUCCESS(rcNt) && (pThis->fFlags & SUPHARDNTVP_F_EXEC_ALLOC_REPLACE_WITH_RW))
+ {
+ PVOID pvAlloc = pvFree;
+ SIZE_T cbAlloc = cbFree;
+ rcNt = NtAllocateVirtualMemory(hProcess, &pvAlloc, 0, &cbAlloc, MEM_COMMIT, PAGE_READWRITE);
+ if (!NT_SUCCESS(rcNt))
+ {
+ supHardNtVpSetInfo2(pThis, VERR_SUP_VP_REPLACE_VIRTUAL_MEMORY_FAILED,
+ "NtAllocateVirtualMemory (%p LB %#zx) failed with rcNt=%#x allocating "
+ "replacement memory for working around buggy protection software. "
+ "See VBoxStartup.log for more details",
+ pvAlloc, cbFree, rcNt);
+ supR3HardenedLogFlush();
+ NtTerminateProcess(hProcess, VERR_SUP_VP_REPLACE_VIRTUAL_MEMORY_FAILED);
+ return false;
+ }
+
+ if ( (uintptr_t)pvFree < (uintptr_t)pvAlloc
+ || (uintptr_t)pvFree + cbFree > (uintptr_t)pvAlloc + cbFree)
+ {
+ supHardNtVpSetInfo2(pThis, VERR_SUP_VP_REPLACE_VIRTUAL_MEMORY_FAILED,
+ "We wanted NtAllocateVirtualMemory to get us %p LB %#zx, but it returned %p LB %#zx.",
+ pMemInfo->BaseAddress, pMemInfo->RegionSize, pvFree, cbFree, rcNt);
+ supR3HardenedLogFlush();
+ NtTerminateProcess(hProcess, VERR_SUP_VP_REPLACE_VIRTUAL_MEMORY_FAILED);
+ return false;
+ }
+
+ /*
+ * Copy what we can, considering the 2nd free attempt.
+ */
+ uint8_t *pbDst = (uint8_t *)pvFree;
+ size_t cbDst = cbFree;
+ uint8_t *pbSrc = (uint8_t *)pvCopy;
+ size_t cbSrc = cbCopy;
+ if ((uintptr_t)pbDst != uCopySrc)
+ {
+ if ((uintptr_t)pbDst > uCopySrc)
+ {
+ uintptr_t cbAdj = (uintptr_t)pbDst - uCopySrc;
+ pbSrc += cbAdj;
+ cbSrc -= cbAdj;
+ }
+ else
+ {
+ uintptr_t cbAdj = uCopySrc - (uintptr_t)pbDst;
+ pbDst += cbAdj;
+ cbDst -= cbAdj;
+ }
+ }
+ if (cbSrc > cbDst)
+ cbSrc = cbDst;
+
+ SIZE_T cbWritten;
+ rcNt = NtWriteVirtualMemory(hProcess, pbDst, pbSrc, cbSrc, &cbWritten);
+ if (NT_SUCCESS(rcNt))
+ {
+ SUP_DPRINTF(("supHardNtVpFreeOrReplacePrivateExecMemory: Restored the exec memory as non-exec.\n"));
+ supR3HardenedLogFlush();
+ }
+ else
+ {
+ supHardNtVpSetInfo2(pThis, VERR_SUP_VP_FREE_VIRTUAL_MEMORY_FAILED,
+ "NtWriteVirtualMemory (%p LB %#zx) failed: %#x",
+ pMemInfo->BaseAddress, pMemInfo->RegionSize, rcNt);
+ supR3HardenedLogFlush();
+ NtTerminateProcess(hProcess, VERR_SUP_VP_REPLACE_VIRTUAL_MEMORY_FAILED);
+ return false;
+ }
+ }
+ if (pvCopy)
+ RTMemFree(pvCopy);
+ return true;
+}
+#endif /* IN_RING3 */
+
+
+/**
+ * Scans the virtual memory of the process.
+ *
+ * This collects the locations of DLLs and the EXE, and verifies that executable
+ * memory is only associated with these. May trash pThis->abMemory.
+ *
+ * @returns VBox status code.
+ * @param pThis The process scanning state structure. Details
+ * about images are added to this.
+ * @param hProcess The process to verify.
+ */
+static int supHardNtVpScanVirtualMemory(PSUPHNTVPSTATE pThis, HANDLE hProcess)
+{
+ SUP_DPRINTF(("supHardNtVpScanVirtualMemory: enmKind=%s\n",
+ pThis->enmKind == SUPHARDNTVPKIND_VERIFY_ONLY ? "VERIFY_ONLY" :
+ pThis->enmKind == SUPHARDNTVPKIND_CHILD_PURIFICATION ? "CHILD_PURIFICATION" : "SELF_PURIFICATION"));
+
+ uint32_t cXpExceptions = 0;
+ uintptr_t cbAdvance = 0;
+ uintptr_t uPtrWhere = 0;
+#ifdef VBOX_PERMIT_VERIFIER_DLL
+ for (uint32_t i = 0; i < 10240; i++)
+#else
+ for (uint32_t i = 0; i < 1024; i++)
+#endif
+ {
+ SIZE_T cbActual = 0;
+ MEMORY_BASIC_INFORMATION MemInfo = { 0, 0, 0, 0, 0, 0, 0 };
+ NTSTATUS rcNt = g_pfnNtQueryVirtualMemory(hProcess,
+ (void const *)uPtrWhere,
+ MemoryBasicInformation,
+ &MemInfo,
+ sizeof(MemInfo),
+ &cbActual);
+ if (!NT_SUCCESS(rcNt))
+ {
+ if (rcNt == STATUS_INVALID_PARAMETER)
+ return pThis->rcResult;
+ return supHardNtVpSetInfo2(pThis, VERR_SUP_VP_NT_QI_VIRTUAL_MEMORY_ERROR,
+ "NtQueryVirtualMemory failed for %p: %#x", uPtrWhere, rcNt);
+ }
+
+ /*
+ * Record images.
+ */
+ if ( MemInfo.Type == SEC_IMAGE
+ || MemInfo.Type == SEC_PROTECTED_IMAGE
+ || MemInfo.Type == (SEC_IMAGE | SEC_PROTECTED_IMAGE))
+ {
+ uint32_t iImg = pThis->cImages;
+ rcNt = g_pfnNtQueryVirtualMemory(hProcess,
+ (void const *)uPtrWhere,
+ MemorySectionName,
+ &pThis->aImages[iImg].Name,
+ sizeof(pThis->aImages[iImg].Name) - sizeof(WCHAR),
+ &cbActual);
+ if (!NT_SUCCESS(rcNt))
+ return supHardNtVpSetInfo2(pThis, VERR_SUP_VP_NT_QI_VIRTUAL_MEMORY_NM_ERROR,
+ "NtQueryVirtualMemory/MemorySectionName failed for %p: %#x", uPtrWhere, rcNt);
+ pThis->aImages[iImg].Name.UniStr.Buffer[pThis->aImages[iImg].Name.UniStr.Length / sizeof(WCHAR)] = '\0';
+ SUP_DPRINTF((MemInfo.AllocationBase == MemInfo.BaseAddress
+ ? " *%p-%p %#06x/%#06x %#09x %ls\n"
+ : " %p-%p %#06x/%#06x %#09x %ls\n",
+ MemInfo.BaseAddress, (uintptr_t)MemInfo.BaseAddress + MemInfo.RegionSize - 1, MemInfo.Protect,
+ MemInfo.AllocationProtect, MemInfo.Type, pThis->aImages[iImg].Name.UniStr.Buffer));
+
+ /* New or existing image? */
+ bool fNew = true;
+ uint32_t iSearch = iImg;
+ while (iSearch-- > 0)
+ if (supHardNtVpAreUniStringsEqual(&pThis->aImages[iSearch].Name.UniStr, &pThis->aImages[iImg].Name.UniStr))
+ {
+ int rc = supHardNtVpAddRegion(pThis, &pThis->aImages[iSearch], &MemInfo);
+ if (RT_FAILURE(rc))
+ return rc;
+ fNew = false;
+ break;
+ }
+ else if (pThis->aImages[iSearch].uImageBase == (uintptr_t)MemInfo.AllocationBase)
+ return supHardNtVpSetInfo2(pThis, VERR_SUP_VP_NT_MAPPING_NAME_CHANGED,
+ "Unexpected base address match");
+
+ if (fNew)
+ {
+ int rc = supHardNtVpNewImage(pThis, &pThis->aImages[iImg], &MemInfo);
+ if (RT_SUCCESS(rc))
+ {
+ if (rc != VINF_OBJECT_DESTROYED)
+ {
+ pThis->cImages++;
+ if (pThis->cImages >= RT_ELEMENTS(pThis->aImages))
+ return supHardNtVpSetInfo2(pThis, VERR_SUP_VP_TOO_MANY_DLLS_LOADED,
+ "Internal error: aImages is full.\n");
+ }
+ }
+#ifdef IN_RING3 /* Continue and add more information if unknown DLLs are found. */
+ else if (rc != VERR_SUP_VP_NOT_KNOWN_DLL_OR_EXE && rc != VERR_SUP_VP_NON_SYSTEM32_DLL)
+ return rc;
+#else
+ else
+ return rc;
+#endif
+ }
+ }
+ /*
+ * XP, W2K3: Ignore the CSRSS read-only region as best we can.
+ */
+ else if ( (MemInfo.Protect & (PAGE_EXECUTE | PAGE_EXECUTE_READ | PAGE_EXECUTE_READWRITE | PAGE_EXECUTE_WRITECOPY))
+ == PAGE_EXECUTE_READ
+ && cXpExceptions == 0
+ && (uintptr_t)MemInfo.BaseAddress >= UINT32_C(0x78000000)
+ /* && MemInfo.BaseAddress == pPeb->ReadOnlySharedMemoryBase */
+ && g_uNtVerCombined < SUP_MAKE_NT_VER_SIMPLE(6, 0) )
+ {
+ cXpExceptions++;
+ SUP_DPRINTF((" %p-%p %#06x/%#06x %#09x XP CSRSS read-only region\n", MemInfo.BaseAddress,
+ (uintptr_t)MemInfo.BaseAddress + MemInfo.RegionSize - 1, MemInfo.Protect,
+ MemInfo.AllocationProtect, MemInfo.Type));
+ }
+ /*
+ * Executable memory?
+ */
+#ifndef VBOX_PERMIT_VISUAL_STUDIO_PROFILING
+ else if (MemInfo.Protect & (PAGE_EXECUTE | PAGE_EXECUTE_READ | PAGE_EXECUTE_READWRITE | PAGE_EXECUTE_WRITECOPY))
+ {
+ SUP_DPRINTF((MemInfo.AllocationBase == MemInfo.BaseAddress
+ ? " *%p-%p %#06x/%#06x %#09x !!\n"
+ : " %p-%p %#06x/%#06x %#09x !!\n",
+ MemInfo.BaseAddress, (uintptr_t)MemInfo.BaseAddress + MemInfo.RegionSize - 1,
+ MemInfo.Protect, MemInfo.AllocationProtect, MemInfo.Type));
+# ifdef IN_RING3
+ if (pThis->enmKind == SUPHARDNTVPKIND_CHILD_PURIFICATION)
+ {
+ /*
+ * Free any private executable memory (sysplant.sys allocates executable memory).
+ */
+ if (MemInfo.Type == MEM_PRIVATE)
+ {
+ if (!supHardNtVpFreeOrReplacePrivateExecMemory(pThis, hProcess, &MemInfo))
+ break;
+ }
+ /*
+ * Unmap mapped memory, failing that, drop exec privileges.
+ */
+ else if (MemInfo.Type == MEM_MAPPED)
+ {
+ SUP_DPRINTF(("supHardNtVpScanVirtualMemory: Unmapping exec mem at %p (%p/%p LB %#zx)\n",
+ uPtrWhere, MemInfo.AllocationBase, MemInfo.BaseAddress, MemInfo.RegionSize));
+ rcNt = NtUnmapViewOfSection(hProcess, MemInfo.AllocationBase);
+ if (!NT_SUCCESS(rcNt))
+ {
+ PVOID pvCopy = MemInfo.BaseAddress;
+ SIZE_T cbCopy = MemInfo.RegionSize;
+ NTSTATUS rcNt2 = NtProtectVirtualMemory(hProcess, &pvCopy, &cbCopy, PAGE_NOACCESS, NULL);
+ if (!NT_SUCCESS(rcNt2))
+ rcNt2 = NtProtectVirtualMemory(hProcess, &pvCopy, &cbCopy, PAGE_READONLY, NULL);
+ if (!NT_SUCCESS(rcNt2))
+ supHardNtVpSetInfo2(pThis, VERR_SUP_VP_UNMAP_AND_PROTECT_FAILED,
+ "NtUnmapViewOfSection (%p/%p LB %#zx) failed: %#x (%#x)",
+ MemInfo.AllocationBase, MemInfo.BaseAddress, MemInfo.RegionSize, rcNt, rcNt2);
+ }
+ }
+ else
+ supHardNtVpSetInfo2(pThis, VERR_SUP_VP_UNKOWN_MEM_TYPE,
+ "Unknown executable memory type %#x at %p/%p LB %#zx",
+ MemInfo.Type, MemInfo.AllocationBase, MemInfo.BaseAddress, MemInfo.RegionSize);
+ pThis->cFixes++;
+ }
+ else if (pThis->enmKind != SUPHARDNTVPKIND_SELF_PURIFICATION_LIMITED)
+# endif /* IN_RING3 */
+ supHardNtVpSetInfo2(pThis, VERR_SUP_VP_FOUND_EXEC_MEMORY,
+ "Found executable memory at %p (%p LB %#zx): type=%#x prot=%#x state=%#x aprot=%#x abase=%p",
+ uPtrWhere, MemInfo.BaseAddress, MemInfo.RegionSize, MemInfo.Type, MemInfo.Protect,
+ MemInfo.State, MemInfo.AllocationBase, MemInfo.AllocationProtect);
+
+# ifndef IN_RING3
+ if (RT_FAILURE(pThis->rcResult))
+ return pThis->rcResult;
+# endif
+ /* Continue add more information about the problematic process. */
+ }
+#endif /* VBOX_PERMIT_VISUAL_STUDIO_PROFILING */
+ else
+ SUP_DPRINTF((MemInfo.AllocationBase == MemInfo.BaseAddress
+ ? " *%p-%p %#06x/%#06x %#09x\n"
+ : " %p-%p %#06x/%#06x %#09x\n",
+ MemInfo.BaseAddress, (uintptr_t)MemInfo.BaseAddress + MemInfo.RegionSize - 1,
+ MemInfo.Protect, MemInfo.AllocationProtect, MemInfo.Type));
+
+ /*
+ * Advance.
+ */
+ cbAdvance = MemInfo.RegionSize;
+ if (uPtrWhere + cbAdvance <= uPtrWhere)
+ return supHardNtVpSetInfo2(pThis, VERR_SUP_VP_EMPTY_REGION_TOO_LARGE,
+ "Empty region at %p.", uPtrWhere);
+ uPtrWhere += MemInfo.RegionSize;
+ }
+
+ return supHardNtVpSetInfo2(pThis, VERR_SUP_VP_TOO_MANY_MEMORY_REGIONS,
+ "Too many virtual memory regions.\n");
+}
+
+
+/**
+ * Verifies the loader image, i.e. check cryptographic signatures if present.
+ *
+ * @returns VBox status code.
+ * @param pEntry The loader cache entry.
+ * @param pwszName The filename to use in error messages.
+ * @param pErrInfo Where to return extened error information.
+ */
+DECLHIDDEN(int) supHardNtLdrCacheEntryVerify(PSUPHNTLDRCACHEENTRY pEntry, PCRTUTF16 pwszName, PRTERRINFO pErrInfo)
+{
+ int rc = VINF_SUCCESS;
+ if (!pEntry->fVerified)
+ {
+ rc = supHardenedWinVerifyImageByLdrMod(pEntry->hLdrMod, pwszName, pEntry->pNtViRdr,
+ false /*fAvoidWinVerifyTrust*/, NULL /*pfWinVerifyTrust*/, pErrInfo);
+ pEntry->fVerified = RT_SUCCESS(rc);
+ }
+ return rc;
+}
+
+
+/**
+ * Allocates a image bits buffer and calls RTLdrGetBits on them.
+ *
+ * An assumption here is that there won't ever be concurrent use of the cache.
+ * It's currently 104% single threaded, non-reentrant. Thus, we can't reuse the
+ * pbBits allocation.
+ *
+ * @returns VBox status code
+ * @param pEntry The loader cache entry.
+ * @param ppbBits Where to return the pointer to the allocation.
+ * @param uBaseAddress The image base address, see RTLdrGetBits.
+ * @param pfnGetImport Import getter, see RTLdrGetBits.
+ * @param pvUser The user argument for @a pfnGetImport.
+ * @param pErrInfo Where to return extened error information.
+ */
+DECLHIDDEN(int) supHardNtLdrCacheEntryGetBits(PSUPHNTLDRCACHEENTRY pEntry, uint8_t **ppbBits,
+ RTLDRADDR uBaseAddress, PFNRTLDRIMPORT pfnGetImport, void *pvUser,
+ PRTERRINFO pErrInfo)
+{
+ int rc;
+
+ /*
+ * First time around we have to allocate memory before we can get the image bits.
+ */
+ if (!pEntry->pbBits)
+ {
+ size_t cbBits = RTLdrSize(pEntry->hLdrMod);
+ if (cbBits >= _1M*32U)
+ return supHardNtVpSetInfo1(pErrInfo, VERR_SUP_VP_IMAGE_TOO_BIG, "Image %s is too large: %zu bytes (%#zx).",
+ pEntry->pszName, cbBits, cbBits);
+
+ pEntry->pbBits = (uint8_t *)RTMemAllocZ(cbBits);
+ if (!pEntry->pbBits)
+ return supHardNtVpSetInfo1(pErrInfo, VERR_SUP_VP_NO_MEMORY, "Failed to allocate %zu bytes for image %s.",
+ cbBits, pEntry->pszName);
+
+ pEntry->fValidBits = false; /* paranoia */
+
+ rc = RTLdrGetBits(pEntry->hLdrMod, pEntry->pbBits, uBaseAddress, pfnGetImport, pvUser);
+ if (RT_FAILURE(rc))
+ return supHardNtVpSetInfo1(pErrInfo, VERR_SUP_VP_NO_MEMORY, "RTLdrGetBits failed on image %s: %Rrc",
+ pEntry->pszName, rc);
+ pEntry->uImageBase = uBaseAddress;
+ pEntry->fValidBits = pfnGetImport == NULL;
+
+ }
+ /*
+ * Cache hit? No?
+ *
+ * Note! We cannot currently cache image bits for images with imports as we
+ * don't control the way they're resolved. Fortunately, NTDLL and
+ * the VM process images all have no imports.
+ */
+ else if ( !pEntry->fValidBits
+ || pEntry->uImageBase != uBaseAddress
+ || pfnGetImport)
+ {
+ pEntry->fValidBits = false;
+
+ rc = RTLdrGetBits(pEntry->hLdrMod, pEntry->pbBits, uBaseAddress, pfnGetImport, pvUser);
+ if (RT_FAILURE(rc))
+ return supHardNtVpSetInfo1(pErrInfo, VERR_SUP_VP_NO_MEMORY, "RTLdrGetBits failed on image %s: %Rrc",
+ pEntry->pszName, rc);
+ pEntry->uImageBase = uBaseAddress;
+ pEntry->fValidBits = pfnGetImport == NULL;
+ }
+
+ *ppbBits = pEntry->pbBits;
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * Frees all resources associated with a cache entry and wipes the members
+ * clean.
+ *
+ * @param pEntry The entry to delete.
+ */
+static void supHardNTLdrCacheDeleteEntry(PSUPHNTLDRCACHEENTRY pEntry)
+{
+ if (pEntry->pbBits)
+ {
+ RTMemFree(pEntry->pbBits);
+ pEntry->pbBits = NULL;
+ }
+
+ if (pEntry->hLdrMod != NIL_RTLDRMOD)
+ {
+ RTLdrClose(pEntry->hLdrMod);
+ pEntry->hLdrMod = NIL_RTLDRMOD;
+ pEntry->pNtViRdr = NULL;
+ }
+ else if (pEntry->pNtViRdr)
+ {
+ pEntry->pNtViRdr->Core.pfnDestroy(&pEntry->pNtViRdr->Core);
+ pEntry->pNtViRdr = NULL;
+ }
+
+ if (pEntry->hFile)
+ {
+ NtClose(pEntry->hFile);
+ pEntry->hFile = NULL;
+ }
+
+ pEntry->pszName = NULL;
+ pEntry->fVerified = false;
+ pEntry->fValidBits = false;
+ pEntry->uImageBase = 0;
+}
+
+#ifdef IN_RING3
+
+/**
+ * Flushes the cache.
+ *
+ * This is called from one of two points in the hardened main code, first is
+ * after respawning and the second is when we open the vboxdrv device for
+ * unrestricted access.
+ */
+DECLHIDDEN(void) supR3HardenedWinFlushLoaderCache(void)
+{
+ uint32_t i = g_cSupNtVpLdrCacheEntries;
+ while (i-- > 0)
+ supHardNTLdrCacheDeleteEntry(&g_aSupNtVpLdrCacheEntries[i]);
+ g_cSupNtVpLdrCacheEntries = 0;
+}
+
+
+/**
+ * Searches the cache for a loader image.
+ *
+ * @returns Pointer to the cache entry if found, NULL if not.
+ * @param pszName The name (from g_apszSupNtVpAllowedVmExes or
+ * g_apszSupNtVpAllowedDlls).
+ */
+static PSUPHNTLDRCACHEENTRY supHardNtLdrCacheLookupEntry(const char *pszName)
+{
+ /*
+ * Since the caller is supplying us a pszName from one of the two tables,
+ * we can dispense with string compare and simply compare string pointers.
+ */
+ uint32_t i = g_cSupNtVpLdrCacheEntries;
+ while (i-- > 0)
+ if (g_aSupNtVpLdrCacheEntries[i].pszName == pszName)
+ return &g_aSupNtVpLdrCacheEntries[i];
+ return NULL;
+}
+
+#endif /* IN_RING3 */
+
+static int supHardNtLdrCacheNewEntry(PSUPHNTLDRCACHEENTRY pEntry, const char *pszName, PUNICODE_STRING pUniStrPath,
+ bool fDll, bool f32bitResourceDll, PRTERRINFO pErrInfo)
+{
+ /*
+ * Open the image file.
+ */
+ HANDLE hFile = RTNT_INVALID_HANDLE_VALUE;
+ IO_STATUS_BLOCK Ios = RTNT_IO_STATUS_BLOCK_INITIALIZER;
+
+ OBJECT_ATTRIBUTES ObjAttr;
+ InitializeObjectAttributes(&ObjAttr, pUniStrPath, OBJ_CASE_INSENSITIVE, NULL /*hRootDir*/, NULL /*pSecDesc*/);
+#ifdef IN_RING0
+ ObjAttr.Attributes |= OBJ_KERNEL_HANDLE;
+#endif
+
+ NTSTATUS rcNt = NtCreateFile(&hFile,
+ GENERIC_READ | SYNCHRONIZE,
+ &ObjAttr,
+ &Ios,
+ NULL /* Allocation Size*/,
+ FILE_ATTRIBUTE_NORMAL,
+ FILE_SHARE_READ,
+ FILE_OPEN,
+ FILE_NON_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT,
+ NULL /*EaBuffer*/,
+ 0 /*EaLength*/);
+ if (NT_SUCCESS(rcNt))
+ rcNt = Ios.Status;
+ if (!NT_SUCCESS(rcNt))
+ return supHardNtVpSetInfo1(pErrInfo, VERR_SUP_VP_IMAGE_FILE_OPEN_ERROR,
+ "Error opening image for scanning: %#x (name %ls)", rcNt, pUniStrPath->Buffer);
+
+ /*
+ * Figure out validation flags we'll be using and create the reader
+ * for this image.
+ */
+ uint32_t fFlags = fDll
+ ? SUPHNTVI_F_TRUSTED_INSTALLER_OWNER | SUPHNTVI_F_ALLOW_CAT_FILE_VERIFICATION
+ : SUPHNTVI_F_REQUIRE_BUILD_CERT;
+ if (f32bitResourceDll)
+ fFlags |= SUPHNTVI_F_IGNORE_ARCHITECTURE;
+
+ PSUPHNTVIRDR pNtViRdr;
+ int rc = supHardNtViRdrCreate(hFile, pUniStrPath->Buffer, fFlags, &pNtViRdr);
+ if (RT_FAILURE(rc))
+ {
+ NtClose(hFile);
+ return rc;
+ }
+
+ /*
+ * Finally, open the image with the loader
+ */
+ RTLDRMOD hLdrMod;
+ RTLDRARCH enmArch = fFlags & SUPHNTVI_F_RC_IMAGE ? RTLDRARCH_X86_32 : RTLDRARCH_HOST;
+ if (fFlags & SUPHNTVI_F_IGNORE_ARCHITECTURE)
+ enmArch = RTLDRARCH_WHATEVER;
+ rc = RTLdrOpenWithReader(&pNtViRdr->Core, RTLDR_O_FOR_VALIDATION, enmArch, &hLdrMod, pErrInfo);
+ if (RT_FAILURE(rc))
+ return supHardNtVpAddInfo1(pErrInfo, rc, "RTLdrOpenWithReader failed: %Rrc (Image='%ls').",
+ rc, pUniStrPath->Buffer);
+
+ /*
+ * Fill in the cache entry.
+ */
+ pEntry->pszName = pszName;
+ pEntry->hLdrMod = hLdrMod;
+ pEntry->pNtViRdr = pNtViRdr;
+ pEntry->hFile = hFile;
+ pEntry->pbBits = NULL;
+ pEntry->fVerified = false;
+ pEntry->fValidBits = false;
+ pEntry->uImageBase = ~(uintptr_t)0;
+
+#ifdef IN_SUP_HARDENED_R3
+ /*
+ * Log the image timestamp when in the hardened exe.
+ */
+ uint64_t uTimestamp = 0;
+ rc = RTLdrQueryProp(hLdrMod, RTLDRPROP_TIMESTAMP_SECONDS, &uTimestamp, sizeof(uint64_t));
+ SUP_DPRINTF(("%s: timestamp %#llx (rc=%Rrc)\n", pszName, uTimestamp, rc));
+#endif
+
+ return VINF_SUCCESS;
+}
+
+#ifdef IN_RING3
+/**
+ * Opens a loader cache entry.
+ *
+ * Currently this is only used by the import code for getting NTDLL.
+ *
+ * @returns VBox status code.
+ * @param pszName The DLL name. Must be one from the
+ * g_apszSupNtVpAllowedDlls array.
+ * @param ppEntry Where to return the entry we've opened/found.
+ * @param pErrInfo Optional buffer where to return additional error
+ * information.
+ */
+DECLHIDDEN(int) supHardNtLdrCacheOpen(const char *pszName, PSUPHNTLDRCACHEENTRY *ppEntry, PRTERRINFO pErrInfo)
+{
+ /*
+ * Locate the dll.
+ */
+ uint32_t i = 0;
+ while ( i < RT_ELEMENTS(g_apszSupNtVpAllowedDlls)
+ && strcmp(pszName, g_apszSupNtVpAllowedDlls[i]))
+ i++;
+ if (i >= RT_ELEMENTS(g_apszSupNtVpAllowedDlls))
+ return VERR_FILE_NOT_FOUND;
+ pszName = g_apszSupNtVpAllowedDlls[i];
+
+ /*
+ * Try the cache.
+ */
+ *ppEntry = supHardNtLdrCacheLookupEntry(pszName);
+ if (*ppEntry)
+ return VINF_SUCCESS;
+
+ /*
+ * Not in the cache, so open it.
+ * Note! We cannot assume that g_System32NtPath has been initialized at this point.
+ */
+ if (g_cSupNtVpLdrCacheEntries >= RT_ELEMENTS(g_aSupNtVpLdrCacheEntries))
+ return VERR_INTERNAL_ERROR_3;
+
+ static WCHAR s_wszSystem32[] = L"\\SystemRoot\\System32\\";
+ WCHAR wszPath[64];
+ memcpy(wszPath, s_wszSystem32, sizeof(s_wszSystem32));
+ RTUtf16CatAscii(wszPath, sizeof(wszPath), pszName);
+
+ UNICODE_STRING UniStr;
+ UniStr.Buffer = wszPath;
+ UniStr.Length = (USHORT)(RTUtf16Len(wszPath) * sizeof(WCHAR));
+ UniStr.MaximumLength = UniStr.Length + sizeof(WCHAR);
+
+ int rc = supHardNtLdrCacheNewEntry(&g_aSupNtVpLdrCacheEntries[g_cSupNtVpLdrCacheEntries], pszName, &UniStr,
+ true /*fDll*/, false /*f32bitResourceDll*/, pErrInfo);
+ if (RT_SUCCESS(rc))
+ {
+ *ppEntry = &g_aSupNtVpLdrCacheEntries[g_cSupNtVpLdrCacheEntries];
+ g_cSupNtVpLdrCacheEntries++;
+ return VINF_SUCCESS;
+ }
+ return rc;
+}
+#endif /* IN_RING3 */
+
+
+/**
+ * Opens all the images with the IPRT loader, setting both, hFile, pNtViRdr and
+ * hLdrMod for each image.
+ *
+ * @returns VBox status code.
+ * @param pThis The process scanning state structure.
+ */
+static int supHardNtVpOpenImages(PSUPHNTVPSTATE pThis)
+{
+ unsigned i = pThis->cImages;
+ while (i-- > 0)
+ {
+ PSUPHNTVPIMAGE pImage = &pThis->aImages[i];
+
+#ifdef IN_RING3
+ /*
+ * Try the cache first.
+ */
+ pImage->pCacheEntry = supHardNtLdrCacheLookupEntry(pImage->pszName);
+ if (pImage->pCacheEntry)
+ continue;
+
+ /*
+ * Not in the cache, so load it into the cache.
+ */
+ if (g_cSupNtVpLdrCacheEntries >= RT_ELEMENTS(g_aSupNtVpLdrCacheEntries))
+ return supHardNtVpSetInfo2(pThis, VERR_INTERNAL_ERROR_3, "Loader cache overflow.");
+ pImage->pCacheEntry = &g_aSupNtVpLdrCacheEntries[g_cSupNtVpLdrCacheEntries];
+#else
+ /*
+ * In ring-0 we don't have a cache at the moment (resource reasons), so
+ * we have a static cache entry in each image structure that we use instead.
+ */
+ pImage->pCacheEntry = &pImage->CacheEntry;
+#endif
+
+ int rc = supHardNtLdrCacheNewEntry(pImage->pCacheEntry, pImage->pszName, &pImage->Name.UniStr,
+ pImage->fDll, pImage->f32bitResourceDll, pThis->pErrInfo);
+ if (RT_FAILURE(rc))
+ return rc;
+#ifdef IN_RING3
+ g_cSupNtVpLdrCacheEntries++;
+#endif
+ }
+
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * Check the integrity of the executable of the process.
+ *
+ * @returns VBox status code.
+ * @param pThis The process scanning state structure. Details
+ * about images are added to this. The hProcess
+ * member holds the handle to the process that is
+ * to be verified.
+ */
+static int supHardNtVpCheckExe(PSUPHNTVPSTATE pThis)
+{
+ /*
+ * Make sure there is exactly one executable image.
+ */
+ unsigned cExecs = 0;
+ unsigned iExe = ~0U;
+ unsigned i = pThis->cImages;
+ while (i-- > 0)
+ {
+ if (!pThis->aImages[i].fDll)
+ {
+ cExecs++;
+ iExe = i;
+ }
+ }
+ if (cExecs == 0)
+ return supHardNtVpSetInfo2(pThis, VERR_SUP_VP_NO_FOUND_NO_EXE_MAPPING,
+ "No executable mapping found in the virtual address space.");
+ if (cExecs != 1)
+ return supHardNtVpSetInfo2(pThis, VERR_SUP_VP_FOUND_MORE_THAN_ONE_EXE_MAPPING,
+ "Found more than one executable mapping in the virtual address space.");
+ PSUPHNTVPIMAGE pImage = &pThis->aImages[iExe];
+
+ /*
+ * Check that it matches the executable image of the process.
+ */
+ int rc;
+ ULONG cbUniStr = sizeof(UNICODE_STRING) + RTPATH_MAX * sizeof(RTUTF16);
+ PUNICODE_STRING pUniStr = (PUNICODE_STRING)RTMemAllocZ(cbUniStr);
+ if (!pUniStr)
+ return supHardNtVpSetInfo2(pThis, VERR_SUP_VP_NO_MEMORY,
+ "Error allocating %zu bytes for process name.", cbUniStr);
+ ULONG cbIgn = 0;
+ NTSTATUS rcNt = NtQueryInformationProcess(pThis->hProcess, ProcessImageFileName, pUniStr, cbUniStr - sizeof(WCHAR), &cbIgn);
+ if (NT_SUCCESS(rcNt))
+ {
+ pUniStr->Buffer[pUniStr->Length / sizeof(WCHAR)] = '\0';
+ if (supHardNtVpArePathsEqual(pUniStr, &pImage->Name.UniStr))
+ rc = VINF_SUCCESS;
+ else
+ rc = supHardNtVpSetInfo2(pThis, VERR_SUP_VP_EXE_VS_PROC_NAME_MISMATCH,
+ "Process image name does not match the exectuable we found: %ls vs %ls.",
+ pUniStr->Buffer, pImage->Name.UniStr.Buffer);
+ }
+ else
+ rc = supHardNtVpSetInfo2(pThis, VERR_SUP_VP_NT_QI_PROCESS_NM_ERROR,
+ "NtQueryInformationProcess/ProcessImageFileName failed: %#x", rcNt);
+ RTMemFree(pUniStr);
+ if (RT_FAILURE(rc))
+ return rc;
+
+ /*
+ * Validate the signing of the executable image.
+ * This will load the fDllCharecteristics and fImageCharecteristics members we use below.
+ */
+ rc = supHardNtVpVerifyImage(pThis, pImage);
+ if (RT_FAILURE(rc))
+ return rc;
+
+ /*
+ * Check linking requirements.
+ * This query is only available using the current process pseudo handle on
+ * older windows versions. The cut-off seems to be Vista.
+ */
+ SECTION_IMAGE_INFORMATION ImageInfo;
+ rcNt = NtQueryInformationProcess(pThis->hProcess, ProcessImageInformation, &ImageInfo, sizeof(ImageInfo), NULL);
+ if (!NT_SUCCESS(rcNt))
+ {
+ if ( rcNt == STATUS_INVALID_PARAMETER
+ && g_uNtVerCombined < SUP_NT_VER_VISTA
+ && pThis->hProcess != NtCurrentProcess() )
+ return VINF_SUCCESS;
+ return supHardNtVpSetInfo2(pThis, VERR_SUP_VP_NT_QI_PROCESS_IMG_INFO_ERROR,
+ "NtQueryInformationProcess/ProcessImageInformation failed: %#x hProcess=%#x",
+ rcNt, pThis->hProcess);
+ }
+ if ( !(ImageInfo.DllCharacteristics & IMAGE_DLLCHARACTERISTICS_FORCE_INTEGRITY))
+ return supHardNtVpSetInfo2(pThis, VERR_SUP_VP_EXE_MISSING_FORCE_INTEGRITY,
+ "EXE DllCharacteristics=%#x, expected IMAGE_DLLCHARACTERISTICS_FORCE_INTEGRITY to be set.",
+ ImageInfo.DllCharacteristics);
+ if (!(ImageInfo.DllCharacteristics & IMAGE_DLLCHARACTERISTICS_DYNAMIC_BASE))
+ return supHardNtVpSetInfo2(pThis, VERR_SUP_VP_EXE_MISSING_DYNAMIC_BASE,
+ "EXE DllCharacteristics=%#x, expected IMAGE_DLLCHARACTERISTICS_DYNAMIC_BASE to be set.",
+ ImageInfo.DllCharacteristics);
+ if (!(ImageInfo.DllCharacteristics & IMAGE_DLLCHARACTERISTICS_NX_COMPAT))
+ return supHardNtVpSetInfo2(pThis, VERR_SUP_VP_EXE_MISSING_NX_COMPAT,
+ "EXE DllCharacteristics=%#x, expected IMAGE_DLLCHARACTERISTICS_NX_COMPAT to be set.",
+ ImageInfo.DllCharacteristics);
+
+ if (pImage->fDllCharecteristics != ImageInfo.DllCharacteristics)
+ return supHardNtVpSetInfo2(pThis, VERR_SUP_VP_DLL_CHARECTERISTICS_MISMATCH,
+ "EXE Info.DllCharacteristics=%#x fDllCharecteristics=%#x.",
+ ImageInfo.DllCharacteristics, pImage->fDllCharecteristics);
+
+ if (pImage->fImageCharecteristics != ImageInfo.ImageCharacteristics)
+ return supHardNtVpSetInfo2(pThis, VERR_SUP_VP_DLL_CHARECTERISTICS_MISMATCH,
+ "EXE Info.ImageCharacteristics=%#x fImageCharecteristics=%#x.",
+ ImageInfo.ImageCharacteristics, pImage->fImageCharecteristics);
+
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * Check the integrity of the DLLs found in the process.
+ *
+ * @returns VBox status code.
+ * @param pThis The process scanning state structure. Details
+ * about images are added to this. The hProcess
+ * member holds the handle to the process that is
+ * to be verified.
+ */
+static int supHardNtVpCheckDlls(PSUPHNTVPSTATE pThis)
+{
+ /*
+ * Check for duplicate entries (paranoia).
+ */
+ uint32_t i = pThis->cImages;
+ while (i-- > 1)
+ {
+ const char *pszName = pThis->aImages[i].pszName;
+ uint32_t j = i;
+ while (j-- > 0)
+ if (pThis->aImages[j].pszName == pszName)
+ return supHardNtVpSetInfo2(pThis, VERR_SUP_VP_DUPLICATE_DLL_MAPPING,
+ "Duplicate image entries for %s: %ls and %ls",
+ pszName, pThis->aImages[i].Name.UniStr.Buffer, pThis->aImages[j].Name.UniStr.Buffer);
+ }
+
+ /*
+ * Check that both ntdll and kernel32 are present.
+ * ASSUMES the entries in g_apszSupNtVpAllowedDlls are all lower case.
+ */
+ uint32_t iNtDll = UINT32_MAX;
+ uint32_t iKernel32 = UINT32_MAX;
+ i = pThis->cImages;
+ while (i-- > 0)
+ if (suplibHardenedStrCmp(pThis->aImages[i].pszName, "ntdll.dll") == 0)
+ iNtDll = i;
+ else if (suplibHardenedStrCmp(pThis->aImages[i].pszName, "kernel32.dll") == 0)
+ iKernel32 = i;
+ if (iNtDll == UINT32_MAX)
+ return supHardNtVpSetInfo2(pThis, VERR_SUP_VP_NO_NTDLL_MAPPING,
+ "The process has no NTDLL.DLL.");
+ if (iKernel32 == UINT32_MAX && ( pThis->enmKind == SUPHARDNTVPKIND_SELF_PURIFICATION
+ || pThis->enmKind == SUPHARDNTVPKIND_SELF_PURIFICATION_LIMITED))
+ return supHardNtVpSetInfo2(pThis, VERR_SUP_VP_NO_KERNEL32_MAPPING,
+ "The process has no KERNEL32.DLL.");
+ else if (iKernel32 != UINT32_MAX && pThis->enmKind == SUPHARDNTVPKIND_CHILD_PURIFICATION)
+ return supHardNtVpSetInfo2(pThis, VERR_SUP_VP_KERNEL32_ALREADY_MAPPED,
+ "The process already has KERNEL32.DLL loaded.");
+
+ /*
+ * Verify that the DLLs are correctly signed (by MS).
+ */
+ i = pThis->cImages;
+ while (i-- > 0)
+ {
+ int rc = supHardNtVpVerifyImage(pThis, &pThis->aImages[i]);
+ if (RT_FAILURE(rc))
+ return rc;
+ }
+
+ return VINF_SUCCESS;
+}
+
+
+#ifdef IN_RING3
+/**
+ * Verifies that we don't have any inheritable handles around, other than a few
+ * ones for file and event objects.
+ *
+ * When finding an inheritable handle of a different type, it will change it to
+ * non-inhertiable. This must NOT be called in the final process prior to
+ * opening the device!
+ *
+ * @returns VBox status code
+ * @param pThis The process scanning state structure.
+ */
+static int supHardNtVpCheckHandles(PSUPHNTVPSTATE pThis)
+{
+ SUP_DPRINTF(("supHardNtVpCheckHandles:\n"));
+
+ /*
+ * Take a snapshot of all the handles in the system.
+ * (Because the current process handle snapshot was added in Windows 8,
+ * so we cannot use that yet.)
+ */
+ uint32_t cbBuf = _256K;
+ uint8_t *pbBuf = (uint8_t *)RTMemAlloc(cbBuf);
+ ULONG cbNeeded = cbBuf;
+ NTSTATUS rcNt = NtQuerySystemInformation(SystemExtendedHandleInformation, pbBuf, cbBuf, &cbNeeded);
+ if (!NT_SUCCESS(rcNt))
+ {
+ while ( rcNt == STATUS_INFO_LENGTH_MISMATCH
+ && cbNeeded > cbBuf
+ && cbBuf <= _32M)
+ {
+ cbBuf = RT_ALIGN_32(cbNeeded + _4K, _64K);
+ RTMemFree(pbBuf);
+ pbBuf = (uint8_t *)RTMemAlloc(cbBuf);
+ if (!pbBuf)
+ return supHardNtVpSetInfo2(pThis, VERR_SUP_VP_NO_MEMORY, "Failed to allocate %zu bytes querying handles.", cbBuf);
+ rcNt = NtQuerySystemInformation(SystemExtendedHandleInformation, pbBuf, cbBuf, &cbNeeded);
+ }
+ if (!NT_SUCCESS(rcNt))
+ {
+ RTMemFree(pbBuf);
+ return supHardNtVpSetInfo2(pThis, VERR_SUP_VP_NO_MEMORY, "Failed to allocate %zu bytes querying handles.", cbBuf);
+ }
+ }
+
+ /*
+ * Examine the snapshot for handles for this process.
+ */
+ int rcRet = VINF_SUCCESS;
+ HANDLE const idProcess = RTNtCurrentTeb()->ClientId.UniqueProcess;
+ SYSTEM_HANDLE_INFORMATION_EX const *pInfo = (SYSTEM_HANDLE_INFORMATION_EX const *)pbBuf;
+ ULONG_PTR i = pInfo->NumberOfHandles;
+ AssertRelease(RT_UOFFSETOF_DYN(SYSTEM_HANDLE_INFORMATION_EX, Handles[i]) == cbNeeded);
+ while (i-- > 0)
+ {
+ SYSTEM_HANDLE_ENTRY_INFO_EX const *pHandleInfo = &pInfo->Handles[i];
+ if ( (pHandleInfo->HandleAttributes & OBJ_INHERIT)
+ && pHandleInfo->UniqueProcessId == idProcess)
+ {
+ ULONG cbNeeded2 = 0;
+ rcNt = NtQueryObject(pHandleInfo->HandleValue, ObjectTypeInformation,
+ pThis->abMemory, sizeof(pThis->abMemory), &cbNeeded2);
+ if (NT_SUCCESS(rcNt))
+ {
+ POBJECT_TYPE_INFORMATION pTypeInfo = (POBJECT_TYPE_INFORMATION)pThis->abMemory;
+ if ( pTypeInfo->TypeName.Length == sizeof(L"File") - sizeof(wchar_t)
+ && memcmp(pTypeInfo->TypeName.Buffer, L"File", sizeof(L"File") - sizeof(wchar_t)) == 0)
+ SUP_DPRINTF(("supHardNtVpCheckHandles: Inheritable file handle: %p\n", pHandleInfo->HandleValue));
+ else if ( pTypeInfo->TypeName.Length == sizeof(L"Event") - sizeof(wchar_t)
+ && memcmp(pTypeInfo->TypeName.Buffer, L"Event", sizeof(L"Event") - sizeof(wchar_t)) == 0)
+ SUP_DPRINTF(("supHardNtVpCheckHandles: Inheritable event handle: %p\n", pHandleInfo->HandleValue));
+ else
+ {
+ OBJECT_HANDLE_FLAG_INFORMATION SetInfo;
+ SetInfo.Inherit = FALSE;
+ SetInfo.ProtectFromClose = FALSE;
+ rcNt = NtSetInformationObject(pHandleInfo->HandleValue, ObjectHandleFlagInformation,
+ &SetInfo, sizeof(SetInfo));
+ if (NT_SUCCESS(rcNt))
+ {
+ SUP_DPRINTF(("supHardNtVpCheckHandles: Marked %ls handle non-inheritable: %p\n",
+ pTypeInfo->TypeName.Buffer, pHandleInfo->HandleValue));
+ pThis->cFixes++;
+ }
+ else
+ {
+ rcRet = supHardNtVpSetInfo2(pThis, VERR_SUP_VP_SET_HANDLE_NOINHERIT,
+ "NtSetInformationObject(%p,,,) -> %#x", pHandleInfo->HandleValue, rcNt);
+ break;
+ }
+ }
+ }
+ else
+ {
+ rcRet = supHardNtVpSetInfo2(pThis, VERR_SUP_VP_QUERY_HANDLE_TYPE,
+ "NtQueryObject(%p,,,,) -> %#x", pHandleInfo->HandleValue, rcNt);
+ break;
+ }
+
+ }
+ }
+ RTMemFree(pbBuf);
+ return rcRet;
+}
+#endif /* IN_RING3 */
+
+
+/**
+ * Verifies the given process.
+ *
+ * The following requirements are checked:
+ * - The process only has one thread, the calling thread.
+ * - The process has no debugger attached.
+ * - The executable image of the process is verified to be signed with
+ * certificate known to this code at build time.
+ * - The executable image is one of a predefined set.
+ * - The process has only a very limited set of system DLLs loaded.
+ * - The system DLLs signatures check out fine.
+ * - The only executable memory in the process belongs to the system DLLs and
+ * the executable image.
+ *
+ * @returns VBox status code.
+ * @param hProcess The process to verify.
+ * @param hThread A thread in the process (the caller).
+ * @param enmKind The kind of process verification to perform.
+ * @param fFlags Valid combination of SUPHARDNTVP_F_XXX flags.
+ * @param pErrInfo Pointer to error info structure. Optional.
+ * @param pcFixes Where to return the number of fixes made during
+ * purification. Optional.
+ */
+DECLHIDDEN(int) supHardenedWinVerifyProcess(HANDLE hProcess, HANDLE hThread, SUPHARDNTVPKIND enmKind, uint32_t fFlags,
+ uint32_t *pcFixes, PRTERRINFO pErrInfo)
+{
+ if (pcFixes)
+ *pcFixes = 0;
+
+ /*
+ * Some basic checks regarding threads and debuggers. We don't need
+ * allocate any state memory for these.
+ */
+ int rc = VINF_SUCCESS;
+ if ( enmKind != SUPHARDNTVPKIND_CHILD_PURIFICATION
+ && enmKind != SUPHARDNTVPKIND_SELF_PURIFICATION_LIMITED)
+ rc = supHardNtVpThread(hProcess, hThread, pErrInfo);
+ if (RT_SUCCESS(rc))
+ rc = supHardNtVpDebugger(hProcess, pErrInfo);
+ if (RT_SUCCESS(rc))
+ {
+ /*
+ * Allocate and initialize memory for the state.
+ */
+ PSUPHNTVPSTATE pThis = (PSUPHNTVPSTATE)RTMemAllocZ(sizeof(*pThis));
+ if (pThis)
+ {
+ pThis->enmKind = enmKind;
+ pThis->fFlags = fFlags;
+ pThis->rcResult = VINF_SUCCESS;
+ pThis->hProcess = hProcess;
+ pThis->pErrInfo = pErrInfo;
+
+ /*
+ * Perform the verification.
+ */
+ rc = supHardNtVpScanVirtualMemory(pThis, hProcess);
+ if (RT_SUCCESS(rc))
+ rc = supHardNtVpOpenImages(pThis);
+ if (RT_SUCCESS(rc))
+ rc = supHardNtVpCheckExe(pThis);
+ if (RT_SUCCESS(rc))
+ rc = supHardNtVpCheckDlls(pThis);
+#ifdef IN_RING3
+ if (enmKind == SUPHARDNTVPKIND_SELF_PURIFICATION_LIMITED)
+ rc = supHardNtVpCheckHandles(pThis);
+#endif
+
+ if (pcFixes)
+ *pcFixes = pThis->cFixes;
+
+ /*
+ * Clean up the state.
+ */
+#ifdef IN_RING0
+ for (uint32_t i = 0; i < pThis->cImages; i++)
+ supHardNTLdrCacheDeleteEntry(&pThis->aImages[i].CacheEntry);
+#endif
+ RTMemFree(pThis);
+ }
+ else
+ rc = supHardNtVpSetInfo1(pErrInfo, VERR_SUP_VP_NO_MEMORY_STATE,
+ "Failed to allocate %zu bytes for state structures.", sizeof(*pThis));
+ }
+ return rc;
+}
+
diff --git a/src/VBox/HostDrivers/Support/win/SUPLib-win.cpp b/src/VBox/HostDrivers/Support/win/SUPLib-win.cpp
new file mode 100644
index 00000000..dfa590a9
--- /dev/null
+++ b/src/VBox/HostDrivers/Support/win/SUPLib-win.cpp
@@ -0,0 +1,1002 @@
+/* $Id: SUPLib-win.cpp $ */
+/** @file
+ * VirtualBox Support Library - Windows NT specific parts.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#define LOG_GROUP LOG_GROUP_SUP
+#ifdef IN_SUP_HARDENED_R3
+# undef DEBUG /* Warning: disables RT_STRICT */
+# undef LOG_DISABLED
+# define LOG_DISABLED
+ /** @todo RTLOGREL_DISABLED */
+# include <iprt/log.h>
+# undef LogRelIt
+# define LogRelIt(pvInst, fFlags, iGroup, fmtargs) do { } while (0)
+#endif
+
+#define USE_NT_DEVICE_IO_CONTROL_FILE
+#include <iprt/nt/nt-and-windows.h>
+
+#include <VBox/sup.h>
+#include <VBox/types.h>
+#include <VBox/err.h>
+#include <VBox/param.h>
+#include <VBox/log.h>
+#include <iprt/assert.h>
+#ifndef IN_SUP_HARDENED_R3
+# include <iprt/env.h>
+# include <iprt/x86.h>
+# include <iprt/ldr.h>
+#endif
+#include <iprt/path.h>
+#include <iprt/string.h>
+#include "../SUPLibInternal.h"
+#include "../SUPDrvIOC.h"
+#ifdef VBOX_WITH_HARDENING
+# include "win/SUPHardenedVerify-win.h"
+#endif
+
+
+/*********************************************************************************************************************************
+* Defined Constants And Macros *
+*********************************************************************************************************************************/
+/** The support service name. */
+#define SERVICE_NAME "VBoxSup"
+
+
+/*********************************************************************************************************************************
+* Internal Functions *
+*********************************************************************************************************************************/
+#ifndef IN_SUP_HARDENED_R3
+static int suplibOsCreateService(void);
+//unused: static int suplibOsUpdateService(void);
+static int suplibOsDeleteService(void);
+static int suplibOsStartService(void);
+static int suplibOsStopService(void);
+#endif
+#ifdef USE_NT_DEVICE_IO_CONTROL_FILE
+static int suplibConvertNtStatus(NTSTATUS rcNt);
+#else
+static int suplibConvertWin32Err(int);
+#endif
+
+
+/*********************************************************************************************************************************
+* Global Variables *
+*********************************************************************************************************************************/
+static bool g_fHardenedVerifyInited = false;
+
+
+DECLHIDDEN(int) suplibOsHardenedVerifyInit(void)
+{
+ if (!g_fHardenedVerifyInited)
+ {
+#if defined(VBOX_WITH_HARDENING) && !defined(IN_SUP_HARDENED_R3) && !defined(IN_SUP_R3_STATIC)
+ supR3HardenedWinInitVersion(false /*fEarly*/);
+ int rc = supHardenedWinInitImageVerifier(NULL);
+ if (RT_FAILURE(rc))
+ return rc;
+ supR3HardenedWinResolveVerifyTrustApiAndHookThreadCreation(NULL);
+#endif
+ g_fHardenedVerifyInited = true;
+ }
+ return VINF_SUCCESS;
+}
+
+
+DECLHIDDEN(int) suplibOsHardenedVerifyTerm(void)
+{
+ /** @todo free resources... */
+ return VINF_SUCCESS;
+}
+
+
+DECLHIDDEN(int) suplibOsInit(PSUPLIBDATA pThis, bool fPreInited, uint32_t fFlags, SUPINITOP *penmWhat, PRTERRINFO pErrInfo)
+{
+ /*
+ * Make sure the image verifier is fully initialized.
+ */
+ int rc = suplibOsHardenedVerifyInit();
+ if (RT_FAILURE(rc))
+ return RTErrInfoSetF(pErrInfo, rc, "suplibOsHardenedVerifyInit failed: %Rrc", rc);
+
+ /*
+ * Done if of pre-inited.
+ */
+ if (fPreInited)
+ {
+#if defined(VBOX_WITH_HARDENING) && !defined(IN_SUP_HARDENED_R3)
+# ifdef IN_SUP_R3_STATIC
+ return VERR_NOT_SUPPORTED;
+# else
+ return VINF_SUCCESS;
+# endif
+#else
+ return VINF_SUCCESS;
+#endif
+ }
+
+#if !defined(IN_SUP_HARDENED_R3)
+ /*
+ * Driverless?
+ */
+ if (fFlags & SUPR3INIT_F_DRIVERLESS)
+ {
+ pThis->fDriverless = true;
+ return VINF_SUCCESS;
+ }
+#endif
+
+ /*
+ * Try open the device.
+ */
+#ifndef IN_SUP_HARDENED_R3
+ uint32_t cTry = 0;
+#endif
+ HANDLE hDevice;
+ for (;;)
+ {
+ IO_STATUS_BLOCK Ios = RTNT_IO_STATUS_BLOCK_INITIALIZER;
+
+ static const WCHAR s_wszName[] = L"\\Device\\VBoxDrvU";
+ UNICODE_STRING NtName;
+ NtName.Buffer = (PWSTR)s_wszName;
+ NtName.Length = sizeof(s_wszName) - sizeof(WCHAR) * (fFlags & SUPR3INIT_F_UNRESTRICTED ? 2 : 1);
+ NtName.MaximumLength = NtName.Length;
+
+ OBJECT_ATTRIBUTES ObjAttr;
+ InitializeObjectAttributes(&ObjAttr, &NtName, OBJ_CASE_INSENSITIVE, NULL /*hRootDir*/, NULL /*pSecDesc*/);
+
+ hDevice = RTNT_INVALID_HANDLE_VALUE;
+
+ NTSTATUS rcNt = NtCreateFile(&hDevice,
+ GENERIC_READ | GENERIC_WRITE, /* No SYNCHRONIZE. */
+ &ObjAttr,
+ &Ios,
+ NULL /* Allocation Size*/,
+ FILE_ATTRIBUTE_NORMAL,
+ FILE_SHARE_READ | FILE_SHARE_WRITE,
+ FILE_OPEN,
+ FILE_NON_DIRECTORY_FILE, /* No FILE_SYNCHRONOUS_IO_NONALERT! */
+ NULL /*EaBuffer*/,
+ 0 /*EaLength*/);
+ if (NT_SUCCESS(rcNt))
+ rcNt = Ios.Status;
+ if (NT_SUCCESS(rcNt))
+ {
+ /*
+ * We're good.
+ */
+ pThis->hDevice = hDevice;
+ pThis->fUnrestricted = RT_BOOL(fFlags & SUPR3INIT_F_UNRESTRICTED);
+ return VINF_SUCCESS;
+ }
+
+#ifndef IN_SUP_HARDENED_R3
+ /*
+ * Failed to open, try starting the service and reopen the device
+ * exactly once.
+ */
+ if (cTry == 0 && !NT_SUCCESS(rcNt))
+ {
+ cTry++;
+ suplibOsStartService();
+ continue;
+ }
+#endif
+
+ /*
+ * Translate the error code.
+ */
+ switch (rcNt)
+ {
+ /** @todo someone must test what is actually returned. */
+ case STATUS_DEVICE_DOES_NOT_EXIST:
+ case STATUS_DEVICE_NOT_CONNECTED:
+ //case ERROR_BAD_DEVICE:
+ case STATUS_DEVICE_REMOVED:
+ //case ERROR_DEVICE_NOT_AVAILABLE:
+ rc = VERR_VM_DRIVER_LOAD_ERROR;
+ break;
+ case STATUS_OBJECT_PATH_NOT_FOUND:
+ case STATUS_NO_SUCH_DEVICE:
+ case STATUS_NO_SUCH_FILE:
+ case STATUS_OBJECT_NAME_NOT_FOUND:
+ rc = VERR_VM_DRIVER_NOT_INSTALLED;
+ break;
+ case STATUS_ACCESS_DENIED:
+ case STATUS_SHARING_VIOLATION:
+ rc = VERR_VM_DRIVER_NOT_ACCESSIBLE;
+ break;
+ case STATUS_UNSUCCESSFUL:
+ rc = VERR_SUPLIB_NT_PROCESS_UNTRUSTED_0;
+ break;
+ case STATUS_TRUST_FAILURE:
+ rc = VERR_SUPLIB_NT_PROCESS_UNTRUSTED_1;
+ break;
+ case STATUS_TOO_LATE:
+ rc = VERR_SUPDRV_HARDENING_EVIL_HANDLE;
+ break;
+ default:
+ if (SUP_NT_STATUS_IS_VBOX(rcNt)) /* See VBoxDrvNtErr2NtStatus. */
+ rc = SUP_NT_STATUS_TO_VBOX(rcNt);
+ else
+ rc = VERR_VM_DRIVER_OPEN_ERROR;
+ break;
+ }
+
+#ifdef IN_SUP_HARDENED_R3
+ /*
+ * Get more details from VBoxDrvErrorInfo if present.
+ */
+ if (pErrInfo && pErrInfo->cbMsg > 32)
+ {
+ /* Prefix. */
+ size_t cchPrefix;
+ if (RTErrIsKnown(rc))
+ cchPrefix = RTStrPrintf(pErrInfo->pszMsg, pErrInfo->cbMsg / 2, "Integrity error (%#x/%Rrc): ", rcNt, rc);
+ else
+ cchPrefix = RTStrPrintf(pErrInfo->pszMsg, pErrInfo->cbMsg / 2, "Integrity error (%#x/%d): ", rcNt, rc);
+
+ /* Get error info. */
+ supR3HardenedWinReadErrorInfoDevice(pErrInfo->pszMsg + cchPrefix, pErrInfo->cbMsg - cchPrefix, "");
+ if (pErrInfo->pszMsg[cchPrefix] != '\0')
+ {
+ pErrInfo->fFlags |= RTERRINFO_FLAGS_SET;
+ pErrInfo->rc = rc;
+ *penmWhat = kSupInitOp_Integrity;
+ }
+ else
+ pErrInfo->pszMsg[0] = '\0';
+ }
+
+#else
+ RT_NOREF1(penmWhat);
+
+ /*
+ * Do fallback.
+ */
+ if ( (fFlags & SUPR3INIT_F_DRIVERLESS_MASK)
+ && rcNt != -1 /** @todo */ )
+ {
+ LogRel(("Failed to open '%.*ls' rc=%Rrc rcNt=%#x - Switching to driverless mode.\n",
+ NtName.Length / sizeof(WCHAR), NtName.Buffer, rc, rcNt));
+ pThis->fDriverless = true;
+ return VINF_SUCCESS;
+ }
+#endif
+ return rc;
+ }
+}
+
+
+#ifndef IN_SUP_HARDENED_R3
+
+DECLHIDDEN(int) suplibOsInstall(void)
+{
+ int rc = suplibOsCreateService();
+ if (RT_SUCCESS(rc))
+ {
+ int rc2 = suplibOsStartService();
+ if (rc2 != VINF_SUCCESS)
+ rc = rc2;
+ }
+ return rc;
+}
+
+
+DECLHIDDEN(int) suplibOsUninstall(void)
+{
+ int rc = suplibOsStopService();
+ if (RT_SUCCESS(rc))
+ rc = suplibOsDeleteService();
+ return rc;
+}
+
+
+/**
+ * Creates the service.
+ *
+ * @returns VBox status code.
+ * @retval VWRN_ALREADY_EXISTS if it already exists.
+ */
+static int suplibOsCreateService(void)
+{
+ /*
+ * Assume it didn't exist, so we'll create the service.
+ */
+ int rc;
+ SC_HANDLE hSMgrCreate = OpenSCManager(NULL, NULL, SERVICE_CHANGE_CONFIG);
+ DWORD dwErr = GetLastError();
+ AssertMsg(hSMgrCreate, ("OpenSCManager(,,create) failed dwErr=%d\n", dwErr));
+ if (hSMgrCreate != NULL)
+ {
+ char szDriver[RTPATH_MAX];
+ rc = RTPathExecDir(szDriver, sizeof(szDriver) - sizeof("\\VBoxSup.sys"));
+ if (RT_SUCCESS(rc))
+ {
+ strcat(szDriver, "\\VBoxSup.sys");
+ SC_HANDLE hService = CreateService(hSMgrCreate,
+ SERVICE_NAME,
+ "VBox Support Driver",
+ SERVICE_QUERY_STATUS,
+ SERVICE_KERNEL_DRIVER,
+ SERVICE_DEMAND_START,
+ SERVICE_ERROR_NORMAL,
+ szDriver,
+ NULL, NULL, NULL, NULL, NULL);
+ dwErr = GetLastError();
+ if (hService)
+ {
+ CloseServiceHandle(hService);
+ rc = VINF_SUCCESS;
+ }
+ else if (dwErr == ERROR_SERVICE_EXISTS)
+ rc = VWRN_ALREADY_EXISTS;
+ else
+ {
+ AssertMsgFailed(("CreateService failed! dwErr=%Rwa szDriver=%s\n", dwErr, szDriver));
+ rc = RTErrConvertFromWin32(dwErr);
+ }
+ }
+ CloseServiceHandle(hSMgrCreate);
+ }
+ else
+ rc = RTErrConvertFromWin32(GetLastError());
+ return rc;
+}
+
+
+/**
+ * Stops a possibly running service.
+ *
+ * @returns VBox status code.
+ */
+static int suplibOsStopService(void)
+{
+ /*
+ * Assume it didn't exist, so we'll create the service.
+ */
+ int rc;
+ SC_HANDLE hSMgr = OpenSCManager(NULL, NULL, SERVICE_STOP | SERVICE_QUERY_STATUS);
+ DWORD dwErr = GetLastError();
+ AssertMsg(hSMgr, ("OpenSCManager(,,delete) failed dwErr=%d\n", dwErr));
+ if (hSMgr)
+ {
+ SC_HANDLE hService = OpenService(hSMgr, SERVICE_NAME, SERVICE_STOP | SERVICE_QUERY_STATUS);
+ if (hService)
+ {
+ /*
+ * Stop the service.
+ */
+ SERVICE_STATUS Status;
+ QueryServiceStatus(hService, &Status);
+ if (Status.dwCurrentState == SERVICE_STOPPED)
+ rc = 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
+ {
+ AssertMsgFailed(("Failed to stop service. status=%d\n", Status.dwCurrentState));
+ rc = VERR_GENERAL_FAILURE;
+ }
+ }
+ else
+ {
+ dwErr = GetLastError();
+ if ( Status.dwCurrentState == SERVICE_STOP_PENDING
+ && dwErr == ERROR_SERVICE_CANNOT_ACCEPT_CTRL)
+ rc = VERR_RESOURCE_BUSY; /* better than VERR_GENERAL_FAILURE */
+ else
+ {
+ AssertMsgFailed(("ControlService failed with dwErr=%Rwa. status=%d\n", dwErr, Status.dwCurrentState));
+ rc = RTErrConvertFromWin32(dwErr);
+ }
+ }
+ CloseServiceHandle(hService);
+ }
+ else
+ {
+ dwErr = GetLastError();
+ if (GetLastError() == ERROR_SERVICE_DOES_NOT_EXIST)
+ rc = VINF_SUCCESS;
+ else
+ {
+ AssertMsgFailed(("OpenService failed dwErr=%Rwa\n", dwErr));
+ rc = RTErrConvertFromWin32(dwErr);
+ }
+ }
+ CloseServiceHandle(hSMgr);
+ }
+ else
+ rc = RTErrConvertFromWin32(dwErr);
+ return rc;
+}
+
+
+/**
+ * Deletes the service.
+ *
+ * @returns VBox status code.
+ */
+int suplibOsDeleteService(void)
+{
+ int rcRet = VINF_SUCCESS;
+ SC_HANDLE hSMgr = OpenSCManager(NULL, NULL, SERVICE_CHANGE_CONFIG);
+ DWORD dwErr = GetLastError();
+ AssertMsg(hSMgr, ("OpenSCManager(,,delete) failed rc=%d\n", dwErr));
+ if (hSMgr)
+ {
+ /*
+ * Old service name.
+ */
+ SC_HANDLE hService = OpenService(hSMgr, "VBoxDrv", DELETE);
+ if (hService)
+ {
+ if (!DeleteService(hService))
+ {
+ dwErr = GetLastError();
+ AssertMsgFailed(("DeleteService failed for VBoxDrv dwErr=%Rwa\n", dwErr));
+ rcRet = RTErrConvertFromWin32(dwErr);
+ }
+ CloseServiceHandle(hService);
+ }
+ else
+ {
+ dwErr = GetLastError();
+ if (dwErr != ERROR_SERVICE_DOES_NOT_EXIST)
+ {
+ AssertMsgFailed(("OpenService failed for VBoxDrv dwErr=%Rwa\n", dwErr));
+ rcRet = RTErrConvertFromWin32(dwErr);
+ }
+ }
+
+ /*
+ * The new service.
+ */
+ hService = OpenService(hSMgr, SERVICE_NAME, DELETE);
+ if (hService)
+ {
+ if (!DeleteService(hService))
+ {
+ dwErr = GetLastError();
+ AssertMsgFailed(("DeleteService for " SERVICE_NAME " failed dwErr=%Rwa\n", dwErr));
+ rcRet = RTErrConvertFromWin32(dwErr);
+ }
+ CloseServiceHandle(hService);
+ }
+ else
+ {
+ dwErr = GetLastError();
+ if (dwErr != ERROR_SERVICE_DOES_NOT_EXIST)
+ {
+ AssertMsgFailed(("OpenService failed for " SERVICE_NAME " dwErr=%Rwa\n", dwErr));
+ rcRet = RTErrConvertFromWin32(dwErr);
+ }
+ }
+ CloseServiceHandle(hSMgr);
+ }
+ else
+ rcRet = RTErrConvertFromWin32(dwErr);
+ return rcRet;
+}
+
+#if 0
+/**
+ * Creates the service.
+ *
+ * @returns 0 on success.
+ * @returns -1 on failure.
+ */
+static int suplibOsUpdateService(void)
+{
+ /*
+ * Assume it didn't exist, so we'll create the service.
+ */
+ SC_HANDLE hSMgr = OpenSCManager(NULL, NULL, SERVICE_CHANGE_CONFIG);
+ DWORD LastError = GetLastError(); NOREF(LastError);
+ AssertMsg(hSMgr, ("OpenSCManager(,,delete) failed LastError=%Rwa\n", LastError));
+ if (hSMgr)
+ {
+ SC_HANDLE hService = OpenService(hSMgr, SERVICE_NAME, SERVICE_CHANGE_CONFIG);
+ if (hService)
+ {
+ char szDriver[RTPATH_MAX];
+ int rc = RTPathExecDir(szDriver, sizeof(szDriver) - sizeof("\\VBoxSup.sys"));
+ if (RT_SUCCESS(rc))
+ {
+ strcat(szDriver, "\\VBoxSup.sys");
+
+ SC_LOCK hLock = LockServiceDatabase(hSMgr);
+ if (ChangeServiceConfig(hService,
+ SERVICE_KERNEL_DRIVER,
+ SERVICE_DEMAND_START,
+ SERVICE_ERROR_NORMAL,
+ szDriver,
+ NULL, NULL, NULL, NULL, NULL, NULL))
+ {
+
+ UnlockServiceDatabase(hLock);
+ CloseServiceHandle(hService);
+ CloseServiceHandle(hSMgr);
+ return 0;
+ }
+ else
+ {
+ DWORD LastError = GetLastError(); NOREF(LastError);
+ AssertMsgFailed(("ChangeServiceConfig failed LastError=%Rwa\n", LastError));
+ }
+ }
+ UnlockServiceDatabase(hLock);
+ CloseServiceHandle(hService);
+ }
+ else
+ {
+ DWORD LastError = GetLastError(); NOREF(LastError);
+ AssertMsgFailed(("OpenService failed LastError=%Rwa\n", LastError));
+ }
+ CloseServiceHandle(hSMgr);
+ }
+ return -1;
+}
+#endif
+
+
+/**
+ * Attempts to start the service, creating it if necessary.
+ *
+ * @returns VBox status code.
+ */
+static int suplibOsStartService(void)
+{
+ /*
+ * Check if the driver service is there.
+ */
+ SC_HANDLE hSMgr = OpenSCManager(NULL, NULL, SERVICE_QUERY_STATUS | SERVICE_START);
+ if (hSMgr == NULL)
+ {
+ DWORD dwErr = GetLastError();
+ AssertMsgFailed(("couldn't open service manager in SERVICE_QUERY_CONFIG | SERVICE_QUERY_STATUS mode! (dwErr=%d)\n", dwErr));
+ return RTErrConvertFromWin32(dwErr);
+ }
+
+ /*
+ * Try open our service to check it's status.
+ */
+ SC_HANDLE hService = OpenService(hSMgr, SERVICE_NAME, SERVICE_QUERY_STATUS | SERVICE_START);
+ if (!hService)
+ {
+ /*
+ * Create the service.
+ */
+ int rc = suplibOsCreateService();
+ if (RT_FAILURE(rc))
+ return rc;
+
+ /*
+ * Try open the service.
+ */
+ hService = OpenService(hSMgr, SERVICE_NAME, SERVICE_QUERY_STATUS | SERVICE_START);
+ }
+
+ /*
+ * Check if open and on demand create succeeded.
+ */
+ int rc;
+ if (hService)
+ {
+
+ /*
+ * Query service status to see if we need to start it or not.
+ */
+ SERVICE_STATUS Status;
+ BOOL fRc = QueryServiceStatus(hService, &Status);
+ Assert(fRc); NOREF(fRc);
+ if (Status.dwCurrentState == SERVICE_RUNNING)
+ rc = VINF_ALREADY_INITIALIZED;
+ else
+ {
+ if (Status.dwCurrentState == SERVICE_START_PENDING)
+ rc = VINF_SUCCESS;
+ else
+ {
+ /*
+ * Start it.
+ */
+ if (StartService(hService, 0, NULL))
+ rc = VINF_SUCCESS;
+ else
+ {
+ DWORD dwErr = GetLastError();
+ AssertMsg(fRc, ("StartService failed with dwErr=%Rwa\n", dwErr));
+ rc = RTErrConvertFromWin32(dwErr);
+ }
+ }
+
+ /*
+ * Wait for the service to finish starting.
+ * We'll wait for 10 seconds then we'll give up.
+ */
+ QueryServiceStatus(hService, &Status);
+ if (Status.dwCurrentState == SERVICE_START_PENDING)
+ {
+ int iWait;
+ for (iWait = 100; iWait > 0 && Status.dwCurrentState == SERVICE_START_PENDING; iWait--)
+ {
+ Sleep(100);
+ QueryServiceStatus(hService, &Status);
+ }
+ DWORD dwErr = GetLastError(); NOREF(dwErr);
+ AssertMsg(Status.dwCurrentState != SERVICE_RUNNING,
+ ("Failed to start. dwErr=%Rwa iWait=%d status=%d\n", dwErr, iWait, Status.dwCurrentState));
+ }
+
+ if (Status.dwCurrentState == SERVICE_RUNNING)
+ rc = VINF_SUCCESS;
+ else if (RT_SUCCESS_NP(rc))
+ rc = VERR_GENERAL_FAILURE;
+ }
+
+ /*
+ * Close open handles.
+ */
+ CloseServiceHandle(hService);
+ }
+ else
+ {
+ DWORD dwErr = GetLastError();
+ AssertMsgFailed(("OpenService failed! LastError=%Rwa\n", dwErr));
+ rc = RTErrConvertFromWin32(dwErr);
+ }
+ if (!CloseServiceHandle(hSMgr))
+ AssertFailed();
+
+ return rc;
+}
+
+#endif /* !IN_SUP_HARDENED_R3 */
+
+DECLHIDDEN(int) suplibOsTerm(PSUPLIBDATA pThis)
+{
+ /*
+ * Check if we're inited at all.
+ */
+ if (pThis->hDevice != NULL)
+ {
+ NTSTATUS rcNt = NtClose((HANDLE)pThis->hDevice);
+ Assert(NT_SUCCESS(rcNt)); RT_NOREF(rcNt);
+ pThis->hDevice = NIL_RTFILE; /* yes, that's right */
+ }
+
+ return VINF_SUCCESS;
+}
+
+#ifndef IN_SUP_HARDENED_R3
+
+DECLHIDDEN(int) suplibOsIOCtl(PSUPLIBDATA pThis, uintptr_t uFunction, void *pvReq, size_t cbReq)
+{
+ RT_NOREF1(cbReq);
+
+ /*
+ * Issue the device I/O control.
+ */
+ PSUPREQHDR pHdr = (PSUPREQHDR)pvReq;
+ Assert(cbReq == RT_MAX(pHdr->cbIn, pHdr->cbOut));
+# ifdef USE_NT_DEVICE_IO_CONTROL_FILE
+ IO_STATUS_BLOCK Ios = RTNT_IO_STATUS_BLOCK_INITIALIZER;
+ NTSTATUS rcNt = NtDeviceIoControlFile((HANDLE)pThis->hDevice, NULL /*hEvent*/, NULL /*pfnApc*/, NULL /*pvApcCtx*/, &Ios,
+ (ULONG)uFunction,
+ pvReq /*pvInput */, pHdr->cbIn /* cbInput */,
+ pvReq /*pvOutput*/, pHdr->cbOut /* cbOutput */);
+ if (NT_SUCCESS(rcNt))
+ {
+ if (NT_SUCCESS(Ios.Status))
+ return VINF_SUCCESS;
+ rcNt = Ios.Status;
+ }
+ return suplibConvertNtStatus(rcNt);
+
+# else
+ DWORD cbReturned = (ULONG)pHdr->cbOut;
+ if (DeviceIoControl((HANDLE)pThis->hDevice, uFunction, pvReq, pHdr->cbIn, pvReq, cbReturned, &cbReturned, NULL))
+ return 0;
+ return suplibConvertWin32Err(GetLastError());
+# endif
+}
+
+
+DECLHIDDEN(int) suplibOsIOCtlFast(PSUPLIBDATA pThis, uintptr_t uFunction, uintptr_t idCpu)
+{
+ /*
+ * Issue device I/O control.
+ */
+# ifdef USE_NT_DEVICE_IO_CONTROL_FILE
+ IO_STATUS_BLOCK Ios = RTNT_IO_STATUS_BLOCK_INITIALIZER;
+ NTSTATUS rcNt = NtDeviceIoControlFile((HANDLE)pThis->hDevice, NULL /*hEvent*/, NULL /*pfnApc*/, NULL /*pvApcCtx*/, &Ios,
+ (ULONG)uFunction,
+ NULL /*pvInput */, 0 /* cbInput */,
+ (PVOID)idCpu /*pvOutput*/, 0 /* cbOutput */);
+ if (NT_SUCCESS(rcNt))
+ {
+ if (NT_SUCCESS(Ios.Status))
+ return VINF_SUCCESS;
+ rcNt = Ios.Status;
+ }
+ return suplibConvertNtStatus(rcNt);
+# else
+ DWORD cbReturned = 0;
+ if (DeviceIoControl((HANDLE)pThis->hDevice, uFunction, NULL, 0, (LPVOID)idCpu, 0, &cbReturned, NULL))
+ return VINF_SUCCESS;
+ return suplibConvertWin32Err(GetLastError());
+# endif
+}
+
+
+DECLHIDDEN(int) suplibOsPageAlloc(PSUPLIBDATA pThis, size_t cPages, uint32_t fFlags, void **ppvPages)
+{
+ RT_NOREF(pThis, fFlags);
+
+ /*
+ * Do some one-time init here wrt large pages.
+ *
+ * Large pages requires the SeLockMemoryPrivilege, which by default (Win10,
+ * Win11) isn't even enabled and must be gpedit'ed to be adjustable here.
+ */
+ if (!(cPages & 511) && (fFlags & SUP_PAGE_ALLOC_F_LARGE_PAGES))
+ {
+ static int volatile s_fCanDoLargePages = -1;
+ int fCanDoLargePages = s_fCanDoLargePages;
+ if (s_fCanDoLargePages != -1)
+ { /* likely */ }
+ else if (RTEnvExistsUtf8("VBOX_DO_NOT_USE_LARGE_PAGES")) /** @todo add flag for this instead? */
+ s_fCanDoLargePages = fCanDoLargePages = 0;
+ else
+ {
+ HANDLE hToken = NULL;
+ if (OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY | TOKEN_ADJUST_PRIVILEGES, &hToken))
+ {
+ union
+ {
+ TOKEN_PRIVILEGES s;
+ uint8_t abPadding[RT_UOFFSETOF(TOKEN_PRIVILEGES, Privileges) + sizeof(LUID_AND_ATTRIBUTES)];
+ } Privileges;
+ RT_ZERO(Privileges);
+ Privileges.s.PrivilegeCount = 1;
+ Privileges.s.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
+ if (LookupPrivilegeValueW(NULL, L"SeLockMemoryPrivilege", &Privileges.s.Privileges[0].Luid))
+ AdjustTokenPrivileges(hToken, FALSE, &Privileges.s, 0, NULL, NULL);
+ else
+ AssertFailed();
+ CloseHandle(hToken);
+ }
+ else
+ AssertFailed();
+ s_fCanDoLargePages = fCanDoLargePages = -2;
+ }
+
+ /*
+ * Try allocate with large pages.
+ */
+ if (fCanDoLargePages != 0)
+ {
+ *ppvPages = VirtualAlloc(NULL, (size_t)cPages << PAGE_SHIFT, MEM_COMMIT | MEM_LARGE_PAGES, PAGE_EXECUTE_READWRITE);
+ if (*ppvPages)
+ {
+ if (fCanDoLargePages == -2)
+ {
+ s_fCanDoLargePages = 1;
+ LogRel(("SUPLib: MEM_LARGE_PAGES works!\n"));
+ }
+ LogRel2(("SUPLib: MEM_LARGE_PAGES for %p LB %p\n", *ppvPages, (size_t)cPages << PAGE_SHIFT));
+ return VINF_SUCCESS;
+ }
+
+ /* This can happen if the above AdjustTokenPrivileges failed (non-admin
+ user), or if the privilege isn't present in the token (need gpedit). */
+ if (GetLastError() == ERROR_PRIVILEGE_NOT_HELD)
+ {
+ LogRel(("SUPLib: MEM_LARGE_PAGES privilege not held.\n"));
+ s_fCanDoLargePages = 0;
+ }
+ else
+ LogRel2(("SUPLib: MEM_LARGE_PAGES allocation failed with odd status: %u\n", GetLastError()));
+ }
+ }
+
+ /*
+ * Do a regular allocation w/o large pages.
+ */
+ *ppvPages = VirtualAlloc(NULL, (size_t)cPages << PAGE_SHIFT, MEM_COMMIT, PAGE_EXECUTE_READWRITE);
+ if (*ppvPages)
+ return VINF_SUCCESS;
+ return RTErrConvertFromWin32(GetLastError());
+}
+
+
+DECLHIDDEN(int) suplibOsPageFree(PSUPLIBDATA pThis, void *pvPages, size_t /* cPages */)
+{
+ NOREF(pThis);
+ if (VirtualFree(pvPages, 0, MEM_RELEASE))
+ return VINF_SUCCESS;
+ return RTErrConvertFromWin32(GetLastError());
+}
+
+
+DECLHIDDEN(bool) suplibOsIsNemSupportedWhenNoVtxOrAmdV(void)
+{
+# if ARCH_BITS == 64
+ /*
+ * Check that we're in a VM.
+ */
+ if (!ASMHasCpuId())
+ return false;
+ if (!RTX86IsValidStdRange(ASMCpuId_EAX(0)))
+ return false;
+ if (!(ASMCpuId_ECX(1) & X86_CPUID_FEATURE_ECX_HVP))
+ return false;
+
+ /*
+ * Try load WinHvPlatform and resolve API for checking.
+ * Note! The two size_t arguments and the ssize_t one are all too big, but who cares.
+ */
+ RTLDRMOD hLdrMod = NIL_RTLDRMOD;
+ int rc = RTLdrLoadSystem("WinHvPlatform.dll", false, &hLdrMod);
+ if (RT_FAILURE(rc))
+ return false;
+
+ bool fRet = false;
+ typedef HRESULT (WINAPI *PFNWHVGETCAPABILITY)(ssize_t, void *, size_t, size_t *);
+ PFNWHVGETCAPABILITY pfnWHvGetCapability = (PFNWHVGETCAPABILITY)RTLdrGetFunction(hLdrMod, "WHvGetCapability");
+ if (pfnWHvGetCapability)
+ {
+ /*
+ * Query the API.
+ */
+ union
+ {
+ BOOL fHypervisorPresent;
+ uint64_t u64Padding;
+ } Caps;
+ RT_ZERO(Caps);
+ size_t cbRetIgn = 0;
+ HRESULT hrc = pfnWHvGetCapability(0 /*WHvCapabilityCodeHypervisorPresent*/, &Caps, sizeof(Caps), &cbRetIgn);
+ if (SUCCEEDED(hrc) && Caps.fHypervisorPresent)
+ fRet = true;
+ }
+
+ RTLdrClose(hLdrMod);
+ return fRet;
+# else
+ return false;
+#endif
+}
+
+
+# ifndef USE_NT_DEVICE_IO_CONTROL_FILE
+/**
+ * Converts a supdrv win32 error code to an IPRT status code.
+ *
+ * @returns corresponding IPRT error code.
+ * @param rc Win32 error code.
+ */
+static int suplibConvertWin32Err(int rc)
+{
+ /* Conversion program (link with ntdll.lib from ddk):
+ #define _WIN32_WINNT 0x0501
+ #include <iprt/win/windows.h>
+ #include <ntstatus.h>
+ #include <winternl.h>
+ #include <stdio.h>
+
+ int main()
+ {
+ #define CONVERT(a) printf(#a " %#x -> %d\n", a, RtlNtStatusToDosError((a)))
+ CONVERT(STATUS_SUCCESS);
+ CONVERT(STATUS_NOT_SUPPORTED);
+ CONVERT(STATUS_INVALID_PARAMETER);
+ CONVERT(STATUS_UNKNOWN_REVISION);
+ CONVERT(STATUS_INVALID_HANDLE);
+ CONVERT(STATUS_INVALID_ADDRESS);
+ CONVERT(STATUS_NOT_LOCKED);
+ CONVERT(STATUS_IMAGE_ALREADY_LOADED);
+ CONVERT(STATUS_ACCESS_DENIED);
+ CONVERT(STATUS_REVISION_MISMATCH);
+
+ return 0;
+ }
+ */
+
+ switch (rc)
+ {
+ //case 0: return STATUS_SUCCESS;
+ case 0: return VINF_SUCCESS;
+ case ERROR_NOT_SUPPORTED: return VERR_GENERAL_FAILURE;
+ case ERROR_INVALID_PARAMETER: return VERR_INVALID_PARAMETER;
+ case ERROR_UNKNOWN_REVISION: return VERR_INVALID_MAGIC;
+ case ERROR_INVALID_HANDLE: return VERR_INVALID_HANDLE;
+ case ERROR_UNEXP_NET_ERR: return VERR_INVALID_POINTER;
+ case ERROR_NOT_LOCKED: return VERR_LOCK_FAILED;
+ case ERROR_SERVICE_ALREADY_RUNNING: return VERR_ALREADY_LOADED;
+ case ERROR_ACCESS_DENIED: return VERR_PERMISSION_DENIED;
+ case ERROR_REVISION_MISMATCH: return VERR_VERSION_MISMATCH;
+ }
+
+ /* fall back on the default conversion. */
+ return RTErrConvertFromWin32(rc);
+}
+# else
+/**
+ * Reverse of VBoxDrvNtErr2NtStatus
+ * returns VBox status code.
+ * @param rcNt NT status code.
+ */
+static int suplibConvertNtStatus(NTSTATUS rcNt)
+{
+ switch (rcNt)
+ {
+ case STATUS_SUCCESS: return VINF_SUCCESS;
+ case STATUS_NOT_SUPPORTED: return VERR_GENERAL_FAILURE;
+ case STATUS_INVALID_PARAMETER: return VERR_INVALID_PARAMETER;
+ case STATUS_UNKNOWN_REVISION: return VERR_INVALID_MAGIC;
+ case STATUS_INVALID_HANDLE: return VERR_INVALID_HANDLE;
+ case STATUS_INVALID_ADDRESS: return VERR_INVALID_POINTER;
+ case STATUS_NOT_LOCKED: return VERR_LOCK_FAILED;
+ case STATUS_IMAGE_ALREADY_LOADED: return VERR_ALREADY_LOADED;
+ case STATUS_ACCESS_DENIED: return VERR_PERMISSION_DENIED;
+ case STATUS_REVISION_MISMATCH: return VERR_VERSION_MISMATCH;
+ }
+
+ /* See VBoxDrvNtErr2NtStatus. */
+ if (SUP_NT_STATUS_IS_VBOX(rcNt))
+ return SUP_NT_STATUS_TO_VBOX(rcNt);
+
+ /* Fall back on IPRT for the rest. */
+ return RTErrConvertFromNtStatus(rcNt);
+}
+# endif
+
+#endif /* !IN_SUP_HARDENED_R3 */
+
diff --git a/src/VBox/HostDrivers/Support/win/SUPR0IdcClient-win.c b/src/VBox/HostDrivers/Support/win/SUPR0IdcClient-win.c
new file mode 100644
index 00000000..73fe259d
--- /dev/null
+++ b/src/VBox/HostDrivers/Support/win/SUPR0IdcClient-win.c
@@ -0,0 +1,167 @@
+/* $Id: SUPR0IdcClient-win.c $ */
+/** @file
+ * VirtualBox Support Driver - IDC Client Lib, Windows Specific Code.
+ */
+
+/*
+ * Copyright (C) 2008-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include "../SUPR0IdcClientInternal.h"
+#include <iprt/errcore.h>
+
+
+/*********************************************************************************************************************************
+* Defined Constants And Macros *
+*********************************************************************************************************************************/
+/** NT Device name. */
+#define DEVICE_NAME_NT L"\\Device\\VBoxDrv"
+
+
+/**
+ * Internal I/O Control call worker.
+ *
+ * @returns VBox status code.
+ * @param pDeviceObject The device object to call.
+ * @param pFileObject The file object for the connection.
+ * @param uReq The request.
+ * @param pReq The request packet.
+ */
+static int supR0IdcNtCallInternal(PDEVICE_OBJECT pDeviceObject, PFILE_OBJECT pFileObject, uint32_t uReq, PSUPDRVIDCREQHDR pReq)
+{
+ int rc;
+ IO_STATUS_BLOCK IoStatusBlock;
+ KEVENT Event;
+ PIRP pIrp;
+ NTSTATUS rcNt;
+
+ /*
+ * Build the request.
+ */
+ KeInitializeEvent(&Event, NotificationEvent, FALSE);
+ pIrp = IoBuildDeviceIoControlRequest(uReq, /* IoControlCode */
+ pDeviceObject,
+ pReq, /* InputBuffer */
+ pReq->cb, /* InputBufferLength */
+ pReq, /* OutputBuffer */
+ pReq->cb, /* OutputBufferLength */
+ TRUE, /* InternalDeviceIoControl (=> IRP_MJ_INTERNAL_DEVICE_CONTROL) */
+ &Event, /* Event */
+ &IoStatusBlock); /* IoStatusBlock */
+ if (pIrp)
+ {
+ IoGetNextIrpStackLocation(pIrp)->FileObject = pFileObject;
+
+ /*
+ * Call the driver, wait for an async request to complete (should never happen).
+ */
+ rcNt = IoCallDriver(pDeviceObject, pIrp);
+ if (rcNt == STATUS_PENDING)
+ {
+ rcNt = KeWaitForSingleObject(&Event, /* Object */
+ Executive, /* WaitReason */
+ KernelMode, /* WaitMode */
+ FALSE, /* Alertable */
+ NULL); /* TimeOut */
+ rcNt = IoStatusBlock.Status;
+ }
+ if (NT_SUCCESS(rcNt))
+ rc = pReq->rc;
+ else
+ rc = RTErrConvertFromNtStatus(rcNt);
+ }
+ else
+ rc = VERR_NO_MEMORY;
+ return rc;
+}
+
+
+int VBOXCALL supR0IdcNativeOpen(PSUPDRVIDCHANDLE pHandle, PSUPDRVIDCREQCONNECT pReq)
+{
+ PDEVICE_OBJECT pDeviceObject = NULL;
+ PFILE_OBJECT pFileObject = NULL;
+ UNICODE_STRING wszDeviceName;
+ NTSTATUS rcNt;
+ int rc;
+
+ /*
+ * Get the device object pointer.
+ */
+ RtlInitUnicodeString(&wszDeviceName, DEVICE_NAME_NT);
+ rcNt = IoGetDeviceObjectPointer(&wszDeviceName, FILE_ALL_ACCESS, &pFileObject, &pDeviceObject);
+ if (NT_SUCCESS(rcNt))
+ {
+ /*
+ * Make the connection call.
+ */
+ rc = supR0IdcNtCallInternal(pDeviceObject, pFileObject, SUPDRV_IDC_REQ_CONNECT, &pReq->Hdr);
+ if (RT_SUCCESS(rc))
+ {
+ pHandle->s.pDeviceObject = pDeviceObject;
+ pHandle->s.pFileObject = pFileObject;
+ return rc;
+ }
+
+ /* only the file object. */
+ ObDereferenceObject(pFileObject);
+ }
+ else
+ rc = RTErrConvertFromNtStatus(rcNt);
+
+ pHandle->s.pDeviceObject = NULL;
+ pHandle->s.pFileObject = NULL;
+ return rc;
+}
+
+
+int VBOXCALL supR0IdcNativeClose(PSUPDRVIDCHANDLE pHandle, PSUPDRVIDCREQHDR pReq)
+{
+ PFILE_OBJECT pFileObject = pHandle->s.pFileObject;
+ int rc = supR0IdcNtCallInternal(pHandle->s.pDeviceObject, pFileObject, SUPDRV_IDC_REQ_DISCONNECT, pReq);
+ if (RT_SUCCESS(rc))
+ {
+ pHandle->s.pDeviceObject = NULL;
+ pHandle->s.pFileObject = NULL;
+ ObDereferenceObject(pFileObject);
+ }
+
+ return rc;
+}
+
+
+int VBOXCALL supR0IdcNativeCall(PSUPDRVIDCHANDLE pHandle, uint32_t uReq, PSUPDRVIDCREQHDR pReq)
+{
+ return supR0IdcNtCallInternal(pHandle->s.pDeviceObject, pHandle->s.pFileObject, uReq, pReq);
+}
+
diff --git a/src/VBox/HostDrivers/Support/win/SUPR3HardenedMain-win.cpp b/src/VBox/HostDrivers/Support/win/SUPR3HardenedMain-win.cpp
new file mode 100644
index 00000000..a7dccfd9
--- /dev/null
+++ b/src/VBox/HostDrivers/Support/win/SUPR3HardenedMain-win.cpp
@@ -0,0 +1,7031 @@
+/* $Id: SUPR3HardenedMain-win.cpp $ */
+/** @file
+ * VirtualBox Support Library - Hardened main(), windows bits.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include <iprt/nt/nt-and-windows.h>
+#include <AccCtrl.h>
+#include <AclApi.h>
+#ifndef PROCESS_SET_LIMITED_INFORMATION
+# define PROCESS_SET_LIMITED_INFORMATION 0x2000
+#endif
+#ifndef LOAD_LIBRARY_SEARCH_APPLICATION_DIR
+# define LOAD_LIBRARY_SEARCH_DLL_LOAD_DIR UINT32_C(0x100)
+# define LOAD_LIBRARY_SEARCH_APPLICATION_DIR UINT32_C(0x200)
+# define LOAD_LIBRARY_SEARCH_USER_DIRS UINT32_C(0x400)
+# define LOAD_LIBRARY_SEARCH_SYSTEM32 UINT32_C(0x800)
+#endif
+
+#include <VBox/sup.h>
+#include <VBox/err.h>
+#include <VBox/dis.h>
+#include <iprt/ctype.h>
+#include <iprt/string.h>
+#include <iprt/initterm.h>
+#include <iprt/param.h>
+#include <iprt/path.h>
+#include <iprt/thread.h>
+#include <iprt/utf16.h>
+#include <iprt/zero.h>
+
+#include "SUPLibInternal.h"
+#include "win/SUPHardenedVerify-win.h"
+#include "../SUPDrvIOC.h"
+
+#ifndef IMAGE_SCN_TYPE_NOLOAD
+# define IMAGE_SCN_TYPE_NOLOAD 0x00000002
+#endif
+
+
+/*********************************************************************************************************************************
+* Defined Constants And Macros *
+*********************************************************************************************************************************/
+/** The first argument of a respawed stub when respawned for the first time.
+ * This just needs to be unique enough to avoid most confusion with real
+ * executable names, there are other checks in place to make sure we've respanwed. */
+#define SUPR3_RESPAWN_1_ARG0 "60eaff78-4bdd-042d-2e72-669728efd737-suplib-2ndchild"
+
+/** The first argument of a respawed stub when respawned for the second time.
+ * This just needs to be unique enough to avoid most confusion with real
+ * executable names, there are other checks in place to make sure we've respanwed. */
+#define SUPR3_RESPAWN_2_ARG0 "60eaff78-4bdd-042d-2e72-669728efd737-suplib-3rdchild"
+
+/** Unconditional assertion. */
+#define SUPR3HARDENED_ASSERT(a_Expr) \
+ do { \
+ if (!(a_Expr)) \
+ supR3HardenedFatal("%s: %s\n", __FUNCTION__, #a_Expr); \
+ } while (0)
+
+/** Unconditional assertion of NT_SUCCESS. */
+#define SUPR3HARDENED_ASSERT_NT_SUCCESS(a_Expr) \
+ do { \
+ NTSTATUS rcNtAssert = (a_Expr); \
+ if (!NT_SUCCESS(rcNtAssert)) \
+ supR3HardenedFatal("%s: %s -> %#x\n", __FUNCTION__, #a_Expr, rcNtAssert); \
+ } while (0)
+
+/** Unconditional assertion of a WIN32 API returning non-FALSE. */
+#define SUPR3HARDENED_ASSERT_WIN32_SUCCESS(a_Expr) \
+ do { \
+ BOOL fRcAssert = (a_Expr); \
+ if (fRcAssert == FALSE) \
+ supR3HardenedFatal("%s: %s -> %#x\n", __FUNCTION__, #a_Expr, RtlGetLastWin32Error()); \
+ } while (0)
+
+
+/*********************************************************************************************************************************
+* Structures and Typedefs *
+*********************************************************************************************************************************/
+/**
+ * Security descriptor cleanup structure.
+ */
+typedef struct MYSECURITYCLEANUP
+{
+ union
+ {
+ SID Sid;
+ uint8_t abPadding[SECURITY_MAX_SID_SIZE];
+ } Everyone, Owner, User, Login;
+ union
+ {
+ ACL AclHdr;
+ uint8_t abPadding[1024];
+ } Acl;
+ PSECURITY_DESCRIPTOR pSecDesc;
+} MYSECURITYCLEANUP;
+/** Pointer to security cleanup structure. */
+typedef MYSECURITYCLEANUP *PMYSECURITYCLEANUP;
+
+
+/**
+ * Image verifier cache entry.
+ */
+typedef struct VERIFIERCACHEENTRY
+{
+ /** Pointer to the next entry with the same hash value. */
+ struct VERIFIERCACHEENTRY * volatile pNext;
+ /** Next entry in the WinVerifyTrust todo list. */
+ struct VERIFIERCACHEENTRY * volatile pNextTodoWvt;
+
+ /** The file handle. */
+ HANDLE hFile;
+ /** If fIndexNumber is set, this is an file system internal file identifier. */
+ LARGE_INTEGER IndexNumber;
+ /** The path hash value. */
+ uint32_t uHash;
+ /** The verification result. */
+ int rc;
+ /** Used for shutting up load and error messages after a while so they don't
+ * flood the log file and fill up the disk. */
+ uint32_t volatile cHits;
+ /** The validation flags (for WinVerifyTrust retry). */
+ uint32_t fFlags;
+ /** Whether IndexNumber is valid */
+ bool fIndexNumberValid;
+ /** Whether verified by WinVerifyTrust. */
+ bool volatile fWinVerifyTrust;
+ /** cwcPath * sizeof(RTUTF16). */
+ uint16_t cbPath;
+ /** The full path of this entry (variable size). */
+ RTUTF16 wszPath[1];
+} VERIFIERCACHEENTRY;
+/** Pointer to an image verifier path entry. */
+typedef VERIFIERCACHEENTRY *PVERIFIERCACHEENTRY;
+
+
+/**
+ * Name of an import DLL that we need to check out.
+ */
+typedef struct VERIFIERCACHEIMPORT
+{
+ /** Pointer to the next DLL in the list. */
+ struct VERIFIERCACHEIMPORT * volatile pNext;
+ /** The length of pwszAltSearchDir if available. */
+ uint32_t cwcAltSearchDir;
+ /** This points the directory containing the DLL needing it, this will be
+ * NULL for a System32 DLL. */
+ PWCHAR pwszAltSearchDir;
+ /** The name of the import DLL (variable length). */
+ char szName[1];
+} VERIFIERCACHEIMPORT;
+/** Pointer to a import DLL that needs checking out. */
+typedef VERIFIERCACHEIMPORT *PVERIFIERCACHEIMPORT;
+
+
+/**
+ * Child requests.
+ */
+typedef enum SUPR3WINCHILDREQ
+{
+ /** Perform child purification and close full access handles (must be zero). */
+ kSupR3WinChildReq_PurifyChildAndCloseHandles = 0,
+ /** Close the events, we're good on our own from here on. */
+ kSupR3WinChildReq_CloseEvents,
+ /** Reporting error. */
+ kSupR3WinChildReq_Error,
+ /** End of valid requests. */
+ kSupR3WinChildReq_End
+} SUPR3WINCHILDREQ;
+
+/**
+ * Child process parameters.
+ */
+typedef struct SUPR3WINPROCPARAMS
+{
+ /** The event semaphore the child will be waiting on. */
+ HANDLE hEvtChild;
+ /** The event semaphore the parent will be waiting on. */
+ HANDLE hEvtParent;
+
+ /** The address of the NTDLL. This is only valid during the very early
+ * initialization as we abuse for thread creation protection. */
+ uintptr_t uNtDllAddr;
+
+ /** The requested operation (set by the child). */
+ SUPR3WINCHILDREQ enmRequest;
+ /** The last status. */
+ int32_t rc;
+ /** The init operation the error relates to if message, kSupInitOp_Invalid if
+ * not message. */
+ SUPINITOP enmWhat;
+ /** Where if message. */
+ char szWhere[80];
+ /** Error message / path name string space. */
+ char szErrorMsg[16384+1024];
+} SUPR3WINPROCPARAMS;
+
+
+/**
+ * Child process data structure for use during child process init setup and
+ * purification.
+ */
+typedef struct SUPR3HARDNTCHILD
+{
+ /** Process handle. */
+ HANDLE hProcess;
+ /** Primary thread handle. */
+ HANDLE hThread;
+ /** Handle to the parent process, if we're the middle (stub) process. */
+ HANDLE hParent;
+ /** The event semaphore the child will be waiting on. */
+ HANDLE hEvtChild;
+ /** The event semaphore the parent will be waiting on. */
+ HANDLE hEvtParent;
+ /** The address of NTDLL in the child. */
+ uintptr_t uNtDllAddr;
+ /** The address of NTDLL in this process. */
+ uintptr_t uNtDllParentAddr;
+ /** Which respawn number this is (1 = stub, 2 = VM). */
+ int iWhich;
+ /** The basic process info. */
+ PROCESS_BASIC_INFORMATION BasicInfo;
+ /** The probable size of the PEB. */
+ size_t cbPeb;
+ /** The pristine process environment block. */
+ PEB Peb;
+ /** The child process parameters. */
+ SUPR3WINPROCPARAMS ProcParams;
+} SUPR3HARDNTCHILD;
+/** Pointer to a child process data structure. */
+typedef SUPR3HARDNTCHILD *PSUPR3HARDNTCHILD;
+
+
+/*********************************************************************************************************************************
+* Global Variables *
+*********************************************************************************************************************************/
+/** Process parameters. Specified by parent if VM process, see
+ * supR3HardenedVmProcessInit. */
+static SUPR3WINPROCPARAMS g_ProcParams = { NULL, NULL, 0, (SUPR3WINCHILDREQ)0, 0 };
+/** Set if supR3HardenedEarlyProcessInit was invoked. */
+bool g_fSupEarlyProcessInit = false;
+/** Set if the stub device has been opened (stub process only). */
+bool g_fSupStubOpened = false;
+
+/** @name Global variables initialized by suplibHardenedWindowsMain.
+ * @{ */
+/** Combined windows NT version number. See SUP_MAKE_NT_VER_COMBINED. */
+uint32_t g_uNtVerCombined = 0;
+/** Count calls to the special main function for linking santity checks. */
+static uint32_t volatile g_cSuplibHardenedWindowsMainCalls;
+/** The UTF-16 windows path to the executable. */
+RTUTF16 g_wszSupLibHardenedExePath[1024];
+/** The NT path of the executable. */
+SUPSYSROOTDIRBUF g_SupLibHardenedExeNtPath;
+/** The NT path of the application binary directory. */
+SUPSYSROOTDIRBUF g_SupLibHardenedAppBinNtPath;
+/** The offset into g_SupLibHardenedExeNtPath of the executable name (WCHAR,
+ * not byte). This also gives the length of the exectuable directory path,
+ * including a trailing slash. */
+static uint32_t g_offSupLibHardenedExeNtName;
+/** Set if we need to use the LOAD_LIBRARY_SEARCH_USER_DIRS option. */
+bool g_fSupLibHardenedDllSearchUserDirs = false;
+/** @} */
+
+/** @name Hook related variables.
+ * @{ */
+/** Pointer to the bit of assembly code that will perform the original
+ * NtCreateSection operation. */
+static NTSTATUS (NTAPI *g_pfnNtCreateSectionReal)(PHANDLE, ACCESS_MASK, POBJECT_ATTRIBUTES,
+ PLARGE_INTEGER, ULONG, ULONG, HANDLE);
+/** Pointer to the NtCreateSection function in NtDll (for patching purposes). */
+static uint8_t *g_pbNtCreateSection;
+/** The patched NtCreateSection bytes (for restoring). */
+static uint8_t g_abNtCreateSectionPatch[16];
+/** Pointer to the bit of assembly code that will perform the original
+ * LdrLoadDll operation. */
+static NTSTATUS (NTAPI *g_pfnLdrLoadDllReal)(PWSTR, PULONG, PUNICODE_STRING, PHANDLE);
+/** Pointer to the LdrLoadDll function in NtDll (for patching purposes). */
+static uint8_t *g_pbLdrLoadDll;
+/** The patched LdrLoadDll bytes (for restoring). */
+static uint8_t g_abLdrLoadDllPatch[16];
+
+#ifndef VBOX_WITHOUT_HARDENDED_XCPT_LOGGING
+/** Pointer to the bit of assembly code that will perform the original
+ * KiUserExceptionDispatcher operation. */
+static VOID (NTAPI *g_pfnKiUserExceptionDispatcherReal)(void);
+/** Pointer to the KiUserExceptionDispatcher function in NtDll (for patching purposes). */
+static uint8_t *g_pbKiUserExceptionDispatcher;
+/** The patched KiUserExceptionDispatcher bytes (for restoring). */
+static uint8_t g_abKiUserExceptionDispatcherPatch[16];
+#endif
+
+/** Pointer to the bit of assembly code that will perform the original
+ * KiUserApcDispatcher operation. */
+static VOID (NTAPI *g_pfnKiUserApcDispatcherReal)(void);
+/** Pointer to the KiUserApcDispatcher function in NtDll (for patching purposes). */
+static uint8_t *g_pbKiUserApcDispatcher;
+/** The patched KiUserApcDispatcher bytes (for restoring). */
+static uint8_t g_abKiUserApcDispatcherPatch[16];
+
+/** Pointer to the LdrInitializeThunk function in NtDll for
+ * supR3HardenedMonitor_KiUserApcDispatcher_C() to use for APC vetting. */
+static uintptr_t g_pfnLdrInitializeThunk;
+
+/** The hash table of verifier cache . */
+static PVERIFIERCACHEENTRY volatile g_apVerifierCache[128];
+/** Queue of cached images which needs WinVerifyTrust to check them. */
+static PVERIFIERCACHEENTRY volatile g_pVerifierCacheTodoWvt = NULL;
+/** Queue of cached images which needs their imports checked. */
+static PVERIFIERCACHEIMPORT volatile g_pVerifierCacheTodoImports = NULL;
+
+/** The windows path to dir \\SystemRoot\\System32 directory (technically
+ * this whatever \\KnownDlls\\KnownDllPath points to). */
+SUPSYSROOTDIRBUF g_System32WinPath;
+/** @} */
+
+/** Positive if the DLL notification callback has been registered, counts
+ * registration attempts as negative. */
+static int g_cDllNotificationRegistered = 0;
+/** The registration cookie of the DLL notification callback. */
+static PVOID g_pvDllNotificationCookie = NULL;
+
+/** Static error info structure used during init. */
+static RTERRINFOSTATIC g_ErrInfoStatic;
+
+/** In the assembly file. */
+extern "C" uint8_t g_abSupHardReadWriteExecPage[PAGE_SIZE];
+
+/** Whether we've patched our own LdrInitializeThunk or not. We do this to
+ * disable thread creation. */
+static bool g_fSupInitThunkSelfPatched;
+/** The backup of our own LdrInitializeThunk code, for enabling and disabling
+ * thread creation in this process. */
+static uint8_t g_abLdrInitThunkSelfBackup[16];
+
+/** Mask of adversaries that we've detected (SUPHARDNT_ADVERSARY_XXX). */
+static uint32_t g_fSupAdversaries = 0;
+/** @name SUPHARDNT_ADVERSARY_XXX - Adversaries
+ * @{ */
+/** Symantec endpoint protection or similar including SysPlant.sys. */
+#define SUPHARDNT_ADVERSARY_SYMANTEC_SYSPLANT RT_BIT_32(0)
+/** Symantec Norton 360. */
+#define SUPHARDNT_ADVERSARY_SYMANTEC_N360 RT_BIT_32(1)
+/** Avast! */
+#define SUPHARDNT_ADVERSARY_AVAST RT_BIT_32(2)
+/** TrendMicro OfficeScan and probably others. */
+#define SUPHARDNT_ADVERSARY_TRENDMICRO RT_BIT_32(3)
+/** TrendMicro potentially buggy sakfile.sys. */
+#define SUPHARDNT_ADVERSARY_TRENDMICRO_SAKFILE RT_BIT_32(4)
+/** McAfee. */
+#define SUPHARDNT_ADVERSARY_MCAFEE RT_BIT_32(5)
+/** Kaspersky or OEMs of it. */
+#define SUPHARDNT_ADVERSARY_KASPERSKY RT_BIT_32(6)
+/** Malwarebytes Anti-Malware (MBAM). */
+#define SUPHARDNT_ADVERSARY_MBAM RT_BIT_32(7)
+/** AVG Internet Security. */
+#define SUPHARDNT_ADVERSARY_AVG RT_BIT_32(8)
+/** Panda Security. */
+#define SUPHARDNT_ADVERSARY_PANDA RT_BIT_32(9)
+/** Microsoft Security Essentials. */
+#define SUPHARDNT_ADVERSARY_MSE RT_BIT_32(10)
+/** Comodo. */
+#define SUPHARDNT_ADVERSARY_COMODO RT_BIT_32(11)
+/** Check Point's Zone Alarm (may include Kaspersky). */
+#define SUPHARDNT_ADVERSARY_ZONE_ALARM RT_BIT_32(12)
+/** Digital guardian, old problematic version. */
+#define SUPHARDNT_ADVERSARY_DIGITAL_GUARDIAN_OLD RT_BIT_32(13)
+/** Digital guardian, new version. */
+#define SUPHARDNT_ADVERSARY_DIGITAL_GUARDIAN_NEW RT_BIT_32(14)
+/** Cylance protect or something (from googling, no available sample copy). */
+#define SUPHARDNT_ADVERSARY_CYLANCE RT_BIT_32(15)
+/** BeyondTrust / PowerBroker / something (googling, no available sample copy). */
+#define SUPHARDNT_ADVERSARY_BEYONDTRUST RT_BIT_32(16)
+/** Avecto / Defendpoint / Privilege Guard (details from support guy, hoping to get sample copy). */
+#define SUPHARDNT_ADVERSARY_AVECTO RT_BIT_32(17)
+/** Sophos Endpoint Defense. */
+#define SUPHARDNT_ADVERSARY_SOPHOS RT_BIT_32(18)
+/** VMware horizon view agent. */
+#define SUPHARDNT_ADVERSARY_HORIZON_VIEW_AGENT RT_BIT_32(19)
+/** Unknown adversary detected while waiting on child. */
+#define SUPHARDNT_ADVERSARY_UNKNOWN RT_BIT_32(31)
+/** @} */
+
+
+/*********************************************************************************************************************************
+* Internal Functions *
+*********************************************************************************************************************************/
+static NTSTATUS supR3HardenedScreenImage(HANDLE hFile, bool fImage, bool fIgnoreArch, PULONG pfAccess, PULONG pfProtect,
+ bool *pfCallRealApi, const char *pszCaller, bool fAvoidWinVerifyTrust,
+ bool *pfQuiet) RT_NOTHROW_PROTO;
+static void supR3HardenedWinRegisterDllNotificationCallback(void);
+static void supR3HardenedWinReInstallHooks(bool fFirst) RT_NOTHROW_PROTO;
+DECLASM(void) supR3HardenedEarlyProcessInitThunk(void);
+DECLASM(void) supR3HardenedMonitor_KiUserApcDispatcher(void);
+#ifndef VBOX_WITHOUT_HARDENDED_XCPT_LOGGING
+DECLASM(void) supR3HardenedMonitor_KiUserExceptionDispatcher(void);
+#endif
+extern "C" void __stdcall suplibHardenedWindowsMain(void);
+
+
+#if 0 /* unused */
+
+/**
+ * Simple wide char search routine.
+ *
+ * @returns Pointer to the first location of @a wcNeedle in @a pwszHaystack.
+ * NULL if not found.
+ * @param pwszHaystack Pointer to the string that should be searched.
+ * @param wcNeedle The character to search for.
+ */
+static PRTUTF16 suplibHardenedWStrChr(PCRTUTF16 pwszHaystack, RTUTF16 wcNeedle)
+{
+ for (;;)
+ {
+ RTUTF16 wcCur = *pwszHaystack;
+ if (wcCur == wcNeedle)
+ return (PRTUTF16)pwszHaystack;
+ if (wcCur == '\0')
+ return NULL;
+ pwszHaystack++;
+ }
+}
+
+
+/**
+ * Simple wide char string length routine.
+ *
+ * @returns The number of characters in the given string. (Excludes the
+ * terminator.)
+ * @param pwsz The string.
+ */
+static size_t suplibHardenedWStrLen(PCRTUTF16 pwsz)
+{
+ PCRTUTF16 pwszCur = pwsz;
+ while (*pwszCur != '\0')
+ pwszCur++;
+ return pwszCur - pwsz;
+}
+
+#endif /* unused */
+
+
+/**
+ * Our version of GetTickCount.
+ * @returns Millisecond timestamp.
+ */
+static uint64_t supR3HardenedWinGetMilliTS(void)
+{
+ PKUSER_SHARED_DATA pUserSharedData = (PKUSER_SHARED_DATA)(uintptr_t)0x7ffe0000;
+
+ /* use interrupt time */
+ LARGE_INTEGER Time;
+ do
+ {
+ Time.HighPart = pUserSharedData->InterruptTime.High1Time;
+ Time.LowPart = pUserSharedData->InterruptTime.LowPart;
+ } while (pUserSharedData->InterruptTime.High2Time != Time.HighPart);
+
+ return (uint64_t)Time.QuadPart / 10000;
+}
+
+
+/**
+ * Called when there is some /GS (or maybe /RTCsu) related stack problem.
+ *
+ * We don't want the CRT version living in gshandle.obj, as it uses a lot of
+ * kernel32 imports, we want to report this error ourselves.
+ */
+extern "C" __declspec(noreturn guard(nosspro) guard(nossepi))
+void __cdecl __report_rangecheckfailure(void)
+{
+ supR3HardenedFatal("__report_rangecheckfailure called from %p", ASMReturnAddress());
+}
+
+
+/**
+ * Called when there is some /GS problem has been detected.
+ *
+ * We don't want the CRT version living in gshandle.obj, as it uses a lot of
+ * kernel32 imports, we want to report this error ourselves.
+ */
+extern "C" __declspec(noreturn guard(nosspro) guard(nossepi))
+#ifdef RT_ARCH_X86
+void __cdecl __report_gsfailure(void)
+#else
+void __report_gsfailure(uintptr_t uCookie)
+#endif
+{
+#ifdef RT_ARCH_X86
+ supR3HardenedFatal("__report_gsfailure called from %p", ASMReturnAddress());
+#else
+ supR3HardenedFatal("__report_gsfailure called from %p, cookie=%p", ASMReturnAddress(), uCookie);
+#endif
+}
+
+
+/**
+ * Wrapper around LoadLibraryEx that deals with the UTF-8 to UTF-16 conversion
+ * and supplies the right flags.
+ *
+ * @returns Module handle on success, NULL on failure.
+ * @param pszName The full path to the DLL.
+ * @param fSystem32Only Whether to only look for imports in the system32
+ * directory. If set to false, the application
+ * directory is also searched.
+ * @param fMainFlags The main flags (giving the location), if the DLL
+ * being loaded is loaded from the app bin
+ * directory and import other DLLs from there. Pass
+ * 0 (= SUPSECMAIN_FLAGS_LOC_APP_BIN) if not
+ * applicable. Ignored if @a fSystem32Only is set.
+ *
+ * This is only needed to load VBoxRT.dll when
+ * executing a testcase from the testcase/ subdir.
+ */
+DECLHIDDEN(void *) supR3HardenedWinLoadLibrary(const char *pszName, bool fSystem32Only, uint32_t fMainFlags)
+{
+ WCHAR wszPath[RTPATH_MAX];
+ PRTUTF16 pwszPath = wszPath;
+ int rc = RTStrToUtf16Ex(pszName, RTSTR_MAX, &pwszPath, RT_ELEMENTS(wszPath), NULL);
+ if (RT_SUCCESS(rc))
+ {
+ while (*pwszPath)
+ {
+ if (*pwszPath == '/')
+ *pwszPath = '\\';
+ pwszPath++;
+ }
+
+ DWORD fFlags = 0;
+ if (g_uNtVerCombined >= SUP_MAKE_NT_VER_SIMPLE(6, 0))
+ {
+ fFlags |= LOAD_LIBRARY_SEARCH_SYSTEM32;
+ if (!fSystem32Only)
+ {
+ fFlags |= LOAD_LIBRARY_SEARCH_APPLICATION_DIR;
+ if (g_fSupLibHardenedDllSearchUserDirs)
+ fFlags |= LOAD_LIBRARY_SEARCH_USER_DIRS;
+ if ((fMainFlags & SUPSECMAIN_FLAGS_LOC_MASK) != SUPSECMAIN_FLAGS_LOC_APP_BIN)
+ fFlags |= LOAD_LIBRARY_SEARCH_DLL_LOAD_DIR;
+ }
+ }
+
+ void *pvRet = (void *)LoadLibraryExW(wszPath, NULL /*hFile*/, fFlags);
+
+ /* Vista, W7, W2K8R might not work without KB2533623, so retry with no flags. */
+ if ( !pvRet
+ && fFlags
+ && g_uNtVerCombined < SUP_MAKE_NT_VER_SIMPLE(6, 2)
+ && RtlGetLastWin32Error() == ERROR_INVALID_PARAMETER)
+ pvRet = (void *)LoadLibraryExW(wszPath, NULL /*hFile*/, 0);
+
+ return pvRet;
+ }
+ supR3HardenedFatal("RTStrToUtf16Ex failed on '%s': %Rrc", pszName, rc);
+ /* not reached */
+}
+
+
+/**
+ * Gets the internal index number of the file.
+ *
+ * @returns True if we got an index number, false if not.
+ * @param hFile The file in question.
+ * @param pIndexNumber where to return the index number.
+ */
+static bool supR3HardenedWinVerifyCacheGetIndexNumber(HANDLE hFile, PLARGE_INTEGER pIndexNumber) RT_NOTHROW_DEF
+{
+ IO_STATUS_BLOCK Ios = RTNT_IO_STATUS_BLOCK_INITIALIZER;
+ NTSTATUS rcNt = NtQueryInformationFile(hFile, &Ios, pIndexNumber, sizeof(*pIndexNumber), FileInternalInformation);
+ if (NT_SUCCESS(rcNt))
+ rcNt = Ios.Status;
+#ifdef DEBUG_bird
+ if (!NT_SUCCESS(rcNt))
+ __debugbreak();
+#endif
+ return NT_SUCCESS(rcNt) && pIndexNumber->QuadPart != 0;
+}
+
+
+/**
+ * Calculates the hash value for the given UTF-16 path string.
+ *
+ * @returns Hash value.
+ * @param pUniStr String to hash.
+ */
+static uint32_t supR3HardenedWinVerifyCacheHashPath(PCUNICODE_STRING pUniStr) RT_NOTHROW_DEF
+{
+ uint32_t uHash = 0;
+ unsigned cwcLeft = pUniStr->Length / sizeof(WCHAR);
+ PRTUTF16 pwc = pUniStr->Buffer;
+
+ while (cwcLeft-- > 0)
+ {
+ RTUTF16 wc = *pwc++;
+ if (wc < 0x80)
+ wc = wc != '/' ? RT_C_TO_LOWER(wc) : '\\';
+ uHash = wc + (uHash << 6) + (uHash << 16) - uHash;
+ }
+ return uHash;
+}
+
+
+/**
+ * Calculates the hash value for a directory + filename combo as if they were
+ * one single string.
+ *
+ * @returns Hash value.
+ * @param pawcDir The directory name.
+ * @param cwcDir The length of the directory name. RTSTR_MAX if
+ * not available.
+ * @param pszName The import name (UTF-8).
+ */
+static uint32_t supR3HardenedWinVerifyCacheHashDirAndFile(PCRTUTF16 pawcDir, uint32_t cwcDir, const char *pszName) RT_NOTHROW_DEF
+{
+ uint32_t uHash = 0;
+ while (cwcDir-- > 0)
+ {
+ RTUTF16 wc = *pawcDir++;
+ if (wc < 0x80)
+ wc = wc != '/' ? RT_C_TO_LOWER(wc) : '\\';
+ uHash = wc + (uHash << 6) + (uHash << 16) - uHash;
+ }
+
+ unsigned char ch = '\\';
+ uHash = ch + (uHash << 6) + (uHash << 16) - uHash;
+
+ while ((ch = *pszName++) != '\0')
+ {
+ ch = RT_C_TO_LOWER(ch);
+ uHash = ch + (uHash << 6) + (uHash << 16) - uHash;
+ }
+
+ return uHash;
+}
+
+
+/**
+ * Verify string cache compare function.
+ *
+ * @returns true if the strings match, false if not.
+ * @param pawcLeft The left hand string.
+ * @param pawcRight The right hand string.
+ * @param cwcToCompare The number of chars to compare.
+ */
+static bool supR3HardenedWinVerifyCacheIsMatch(PCRTUTF16 pawcLeft, PCRTUTF16 pawcRight, uint32_t cwcToCompare) RT_NOTHROW_DEF
+{
+ /* Try a quick memory compare first. */
+ if (memcmp(pawcLeft, pawcRight, cwcToCompare * sizeof(RTUTF16)) == 0)
+ return true;
+
+ /* Slow char by char compare. */
+ while (cwcToCompare-- > 0)
+ {
+ RTUTF16 wcLeft = *pawcLeft++;
+ RTUTF16 wcRight = *pawcRight++;
+ if (wcLeft != wcRight)
+ {
+ wcLeft = wcLeft != '/' ? RT_C_TO_LOWER(wcLeft) : '\\';
+ wcRight = wcRight != '/' ? RT_C_TO_LOWER(wcRight) : '\\';
+ if (wcLeft != wcRight)
+ return false;
+ }
+ }
+
+ return true;
+}
+
+
+
+/**
+ * Inserts the given verifier result into the cache.
+ *
+ * @param pUniStr The full path of the image.
+ * @param hFile The file handle - must either be entered into
+ * the cache or closed.
+ * @param rc The verifier result.
+ * @param fWinVerifyTrust Whether verified by WinVerifyTrust or not.
+ * @param fFlags The image verification flags.
+ */
+static void supR3HardenedWinVerifyCacheInsert(PCUNICODE_STRING pUniStr, HANDLE hFile, int rc,
+ bool fWinVerifyTrust, uint32_t fFlags) RT_NOTHROW_DEF
+{
+ /*
+ * Allocate and initalize a new entry.
+ */
+ PVERIFIERCACHEENTRY pEntry = (PVERIFIERCACHEENTRY)RTMemAllocZ(sizeof(VERIFIERCACHEENTRY) + pUniStr->Length);
+ if (pEntry)
+ {
+ pEntry->pNext = NULL;
+ pEntry->pNextTodoWvt = NULL;
+ pEntry->hFile = hFile;
+ pEntry->uHash = supR3HardenedWinVerifyCacheHashPath(pUniStr);
+ pEntry->rc = rc;
+ pEntry->fFlags = fFlags;
+ pEntry->cHits = 0;
+ pEntry->fWinVerifyTrust = fWinVerifyTrust;
+ pEntry->cbPath = pUniStr->Length;
+ memcpy(pEntry->wszPath, pUniStr->Buffer, pUniStr->Length);
+ pEntry->wszPath[pUniStr->Length / sizeof(WCHAR)] = '\0';
+ pEntry->fIndexNumberValid = supR3HardenedWinVerifyCacheGetIndexNumber(hFile, &pEntry->IndexNumber);
+
+ /*
+ * Try insert it, careful with concurrent code as well as potential duplicates.
+ */
+ uint32_t iHashTab = pEntry->uHash % RT_ELEMENTS(g_apVerifierCache);
+ VERIFIERCACHEENTRY * volatile *ppEntry = &g_apVerifierCache[iHashTab];
+ for (;;)
+ {
+ if (ASMAtomicCmpXchgPtr(ppEntry, pEntry, NULL))
+ {
+ if (!fWinVerifyTrust)
+ do
+ pEntry->pNextTodoWvt = g_pVerifierCacheTodoWvt;
+ while (!ASMAtomicCmpXchgPtr(&g_pVerifierCacheTodoWvt, pEntry, pEntry->pNextTodoWvt));
+
+ SUP_DPRINTF(("supR3HardenedWinVerifyCacheInsert: %ls\n", pUniStr->Buffer));
+ return;
+ }
+
+ PVERIFIERCACHEENTRY pOther = *ppEntry;
+ if (!pOther)
+ continue;
+ if ( pOther->uHash == pEntry->uHash
+ && pOther->cbPath == pEntry->cbPath
+ && supR3HardenedWinVerifyCacheIsMatch(pOther->wszPath, pEntry->wszPath, pEntry->cbPath / sizeof(RTUTF16)))
+ break;
+ ppEntry = &pOther->pNext;
+ }
+
+ /* Duplicate entry (may happen due to races). */
+ RTMemFree(pEntry);
+ }
+ NtClose(hFile);
+}
+
+
+/**
+ * Looks up an entry in the verifier hash table.
+ *
+ * @return Pointer to the entry on if found, NULL if not.
+ * @param pUniStr The full path of the image.
+ * @param hFile The file handle.
+ */
+static PVERIFIERCACHEENTRY supR3HardenedWinVerifyCacheLookup(PCUNICODE_STRING pUniStr, HANDLE hFile) RT_NOTHROW_DEF
+{
+ PRTUTF16 const pwszPath = pUniStr->Buffer;
+ uint16_t const cbPath = pUniStr->Length;
+ uint32_t uHash = supR3HardenedWinVerifyCacheHashPath(pUniStr);
+ uint32_t iHashTab = uHash % RT_ELEMENTS(g_apVerifierCache);
+ PVERIFIERCACHEENTRY pCur = g_apVerifierCache[iHashTab];
+ while (pCur)
+ {
+ if ( pCur->uHash == uHash
+ && pCur->cbPath == cbPath
+ && supR3HardenedWinVerifyCacheIsMatch(pCur->wszPath, pwszPath, cbPath / sizeof(RTUTF16)))
+ {
+
+ if (!pCur->fIndexNumberValid)
+ return pCur;
+ LARGE_INTEGER IndexNumber;
+ bool fIndexNumberValid = supR3HardenedWinVerifyCacheGetIndexNumber(hFile, &IndexNumber);
+ if ( fIndexNumberValid
+ && IndexNumber.QuadPart == pCur->IndexNumber.QuadPart)
+ return pCur;
+#ifdef DEBUG_bird
+ __debugbreak();
+#endif
+ }
+ pCur = pCur->pNext;
+ }
+ return NULL;
+}
+
+
+/**
+ * Looks up an import DLL in the verifier hash table.
+ *
+ * @return Pointer to the entry on if found, NULL if not.
+ * @param pawcDir The directory name.
+ * @param cwcDir The length of the directory name.
+ * @param pszName The import name (UTF-8).
+ */
+static PVERIFIERCACHEENTRY supR3HardenedWinVerifyCacheLookupImport(PCRTUTF16 pawcDir, uint32_t cwcDir, const char *pszName)
+{
+ uint32_t uHash = supR3HardenedWinVerifyCacheHashDirAndFile(pawcDir, cwcDir, pszName);
+ uint32_t iHashTab = uHash % RT_ELEMENTS(g_apVerifierCache);
+ uint32_t const cbPath = (uint32_t)((cwcDir + 1 + strlen(pszName)) * sizeof(RTUTF16));
+ PVERIFIERCACHEENTRY pCur = g_apVerifierCache[iHashTab];
+ while (pCur)
+ {
+ if ( pCur->uHash == uHash
+ && pCur->cbPath == cbPath)
+ {
+ if (supR3HardenedWinVerifyCacheIsMatch(pCur->wszPath, pawcDir, cwcDir))
+ {
+ if (pCur->wszPath[cwcDir] == '\\' || pCur->wszPath[cwcDir] == '/')
+ {
+ if (RTUtf16ICmpAscii(&pCur->wszPath[cwcDir + 1], pszName))
+ {
+ return pCur;
+ }
+ }
+ }
+ }
+
+ pCur = pCur->pNext;
+ }
+ return NULL;
+}
+
+
+/**
+ * Schedules the import DLLs for verification and entry into the cache.
+ *
+ * @param hLdrMod The loader module which imports should be
+ * scheduled for verification.
+ * @param pwszName The full NT path of the module.
+ */
+DECLHIDDEN(void) supR3HardenedWinVerifyCacheScheduleImports(RTLDRMOD hLdrMod, PCRTUTF16 pwszName)
+{
+ /*
+ * Any imports?
+ */
+ uint32_t cImports;
+ int rc = RTLdrQueryPropEx(hLdrMod, RTLDRPROP_IMPORT_COUNT, NULL /*pvBits*/, &cImports, sizeof(cImports), NULL);
+ if (RT_SUCCESS(rc))
+ {
+ if (cImports)
+ {
+ /*
+ * Figure out the DLL directory from pwszName.
+ */
+ PCRTUTF16 pawcDir = pwszName;
+ uint32_t cwcDir = 0;
+ uint32_t i = 0;
+ RTUTF16 wc;
+ while ((wc = pawcDir[i++]) != '\0')
+ if ((wc == '\\' || wc == '/' || wc == ':') && cwcDir + 2 != i)
+ cwcDir = i - 1;
+ if ( g_System32NtPath.UniStr.Length / sizeof(WCHAR) == cwcDir
+ && supR3HardenedWinVerifyCacheIsMatch(pawcDir, g_System32NtPath.UniStr.Buffer, cwcDir))
+ pawcDir = NULL;
+
+ /*
+ * Enumerate the imports.
+ */
+ for (i = 0; i < cImports; i++)
+ {
+ union
+ {
+ char szName[256];
+ uint32_t iImport;
+ } uBuf;
+ uBuf.iImport = i;
+ rc = RTLdrQueryPropEx(hLdrMod, RTLDRPROP_IMPORT_MODULE, NULL /*pvBits*/, &uBuf, sizeof(uBuf), NULL);
+ if (RT_SUCCESS(rc))
+ {
+ /*
+ * Skip kernel32, ntdll and API set stuff.
+ */
+ RTStrToLower(uBuf.szName);
+ if ( RTStrCmp(uBuf.szName, "kernel32.dll") == 0
+ || RTStrCmp(uBuf.szName, "kernelbase.dll") == 0
+ || RTStrCmp(uBuf.szName, "ntdll.dll") == 0
+ || RTStrNCmp(uBuf.szName, RT_STR_TUPLE("api-ms-win-")) == 0
+ || RTStrNCmp(uBuf.szName, RT_STR_TUPLE("ext-ms-win-")) == 0
+ )
+ {
+ continue;
+ }
+
+ /*
+ * Skip to the next one if it's already in the cache.
+ */
+ if (supR3HardenedWinVerifyCacheLookupImport(g_System32NtPath.UniStr.Buffer,
+ g_System32NtPath.UniStr.Length / sizeof(WCHAR),
+ uBuf.szName) != NULL)
+ {
+ SUP_DPRINTF(("supR3HardenedWinVerifyCacheScheduleImports: '%s' cached for system32\n", uBuf.szName));
+ continue;
+ }
+ if (supR3HardenedWinVerifyCacheLookupImport(g_SupLibHardenedAppBinNtPath.UniStr.Buffer,
+ g_SupLibHardenedAppBinNtPath.UniStr.Length / sizeof(CHAR),
+ uBuf.szName) != NULL)
+ {
+ SUP_DPRINTF(("supR3HardenedWinVerifyCacheScheduleImports: '%s' cached for appdir\n", uBuf.szName));
+ continue;
+ }
+ if (pawcDir && supR3HardenedWinVerifyCacheLookupImport(pawcDir, cwcDir, uBuf.szName) != NULL)
+ {
+ SUP_DPRINTF(("supR3HardenedWinVerifyCacheScheduleImports: '%s' cached for dll dir\n", uBuf.szName));
+ continue;
+ }
+
+ /* We could skip already scheduled modules, but that'll require serialization and extra work... */
+
+ /*
+ * Add it to the todo list.
+ */
+ SUP_DPRINTF(("supR3HardenedWinVerifyCacheScheduleImports: Import todo: #%u '%s'.\n", i, uBuf.szName));
+ uint32_t cbName = (uint32_t)strlen(uBuf.szName) + 1;
+ uint32_t cbNameAligned = RT_ALIGN_32(cbName, sizeof(RTUTF16));
+ uint32_t cbNeeded = RT_UOFFSETOF_DYN(VERIFIERCACHEIMPORT, szName[cbNameAligned])
+ + (pawcDir ? (cwcDir + 1) * sizeof(RTUTF16) : 0);
+ PVERIFIERCACHEIMPORT pImport = (PVERIFIERCACHEIMPORT)RTMemAllocZ(cbNeeded);
+ if (pImport)
+ {
+ /* Init it. */
+ memcpy(pImport->szName, uBuf.szName, cbName);
+ if (!pawcDir)
+ {
+ pImport->cwcAltSearchDir = 0;
+ pImport->pwszAltSearchDir = NULL;
+ }
+ else
+ {
+ pImport->cwcAltSearchDir = cwcDir;
+ pImport->pwszAltSearchDir = (PRTUTF16)&pImport->szName[cbNameAligned];
+ memcpy(pImport->pwszAltSearchDir, pawcDir, cwcDir * sizeof(RTUTF16));
+ pImport->pwszAltSearchDir[cwcDir] = '\0';
+ }
+
+ /* Insert it. */
+ do
+ pImport->pNext = g_pVerifierCacheTodoImports;
+ while (!ASMAtomicCmpXchgPtr(&g_pVerifierCacheTodoImports, pImport, pImport->pNext));
+ }
+ }
+ else
+ SUP_DPRINTF(("RTLDRPROP_IMPORT_MODULE failed with rc=%Rrc i=%#x on '%ls'\n", rc, i, pwszName));
+ }
+ }
+ else
+ SUP_DPRINTF(("'%ls' has no imports\n", pwszName));
+ }
+ else
+ SUP_DPRINTF(("RTLDRPROP_IMPORT_COUNT failed with rc=%Rrc on '%ls'\n", rc, pwszName));
+}
+
+
+/**
+ * Processes the list of import todos.
+ */
+static void supR3HardenedWinVerifyCacheProcessImportTodos(void)
+{
+ /*
+ * Work until we've got nothing more todo.
+ */
+ for (;;)
+ {
+ PVERIFIERCACHEIMPORT pTodo = ASMAtomicXchgPtrT(&g_pVerifierCacheTodoImports, NULL, PVERIFIERCACHEIMPORT);
+ if (!pTodo)
+ break;
+ do
+ {
+ PVERIFIERCACHEIMPORT pCur = pTodo;
+ pTodo = pTodo->pNext;
+
+ /*
+ * Not in the cached already?
+ */
+ if ( !supR3HardenedWinVerifyCacheLookupImport(g_System32NtPath.UniStr.Buffer,
+ g_System32NtPath.UniStr.Length / sizeof(WCHAR),
+ pCur->szName)
+ && !supR3HardenedWinVerifyCacheLookupImport(g_SupLibHardenedAppBinNtPath.UniStr.Buffer,
+ g_SupLibHardenedAppBinNtPath.UniStr.Length / sizeof(WCHAR),
+ pCur->szName)
+ && ( pCur->cwcAltSearchDir == 0
+ || !supR3HardenedWinVerifyCacheLookupImport(pCur->pwszAltSearchDir, pCur->cwcAltSearchDir, pCur->szName)) )
+ {
+ /*
+ * Try locate the imported DLL and open it.
+ */
+ SUP_DPRINTF(("supR3HardenedWinVerifyCacheProcessImportTodos: Processing '%s'...\n", pCur->szName));
+
+ NTSTATUS rcNt;
+ NTSTATUS rcNtRedir = 0x22222222;
+ HANDLE hFile = INVALID_HANDLE_VALUE;
+ RTUTF16 wszPath[260 + 260]; /* Assumes we've limited the import name length to 256. */
+ AssertCompile(sizeof(wszPath) > sizeof(g_System32NtPath));
+
+ /*
+ * Check for DLL isolation / redirection / mapping.
+ */
+ size_t cwcName = 260;
+ PRTUTF16 pwszName = &wszPath[0];
+ int rc = RTStrToUtf16Ex(pCur->szName, RTSTR_MAX, &pwszName, cwcName, &cwcName);
+ if (RT_SUCCESS(rc))
+ {
+ UNICODE_STRING UniStrName;
+ UniStrName.Buffer = wszPath;
+ UniStrName.Length = (USHORT)cwcName * sizeof(WCHAR);
+ UniStrName.MaximumLength = UniStrName.Length + sizeof(WCHAR);
+
+ UNICODE_STRING UniStrStatic;
+ UniStrStatic.Buffer = &wszPath[cwcName + 1];
+ UniStrStatic.Length = 0;
+ UniStrStatic.MaximumLength = (USHORT)(sizeof(wszPath) - cwcName * sizeof(WCHAR) - sizeof(WCHAR));
+
+ static UNICODE_STRING const s_DefaultSuffix = RTNT_CONSTANT_UNISTR(L".dll");
+ UNICODE_STRING UniStrDynamic = { 0, 0, NULL };
+ PUNICODE_STRING pUniStrResult = NULL;
+
+ rcNtRedir = RtlDosApplyFileIsolationRedirection_Ustr(1 /*fFlags*/,
+ &UniStrName,
+ (PUNICODE_STRING)&s_DefaultSuffix,
+ &UniStrStatic,
+ &UniStrDynamic,
+ &pUniStrResult,
+ NULL /*pNewFlags*/,
+ NULL /*pcbFilename*/,
+ NULL /*pcbNeeded*/);
+ if (NT_SUCCESS(rcNtRedir))
+ {
+ IO_STATUS_BLOCK Ios = RTNT_IO_STATUS_BLOCK_INITIALIZER;
+ OBJECT_ATTRIBUTES ObjAttr;
+ InitializeObjectAttributes(&ObjAttr, pUniStrResult,
+ OBJ_CASE_INSENSITIVE, NULL /*hRootDir*/, NULL /*pSecDesc*/);
+ rcNt = NtCreateFile(&hFile,
+ FILE_READ_DATA | READ_CONTROL | SYNCHRONIZE,
+ &ObjAttr,
+ &Ios,
+ NULL /* Allocation Size*/,
+ FILE_ATTRIBUTE_NORMAL,
+ FILE_SHARE_READ,
+ FILE_OPEN,
+ FILE_NON_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT,
+ NULL /*EaBuffer*/,
+ 0 /*EaLength*/);
+ if (NT_SUCCESS(rcNt))
+ rcNt = Ios.Status;
+ if (NT_SUCCESS(rcNt))
+ {
+ /* For accurate logging. */
+ size_t cwcCopy = RT_MIN(pUniStrResult->Length / sizeof(RTUTF16), RT_ELEMENTS(wszPath) - 1);
+ memcpy(wszPath, pUniStrResult->Buffer, cwcCopy * sizeof(RTUTF16));
+ wszPath[cwcCopy] = '\0';
+ }
+ else
+ hFile = INVALID_HANDLE_VALUE;
+ RtlFreeUnicodeString(&UniStrDynamic);
+ }
+ }
+ else
+ SUP_DPRINTF(("supR3HardenedWinVerifyCacheProcessImportTodos: RTStrToUtf16Ex #1 failed: %Rrc\n", rc));
+
+ /*
+ * If not something that gets remapped, do the half normal searching we need.
+ */
+ if (hFile == INVALID_HANDLE_VALUE)
+ {
+ struct
+ {
+ PRTUTF16 pawcDir;
+ uint32_t cwcDir;
+ } Tmp, aDirs[] =
+ {
+ { g_System32NtPath.UniStr.Buffer, g_System32NtPath.UniStr.Length / sizeof(WCHAR) },
+ { g_SupLibHardenedExeNtPath.UniStr.Buffer, g_SupLibHardenedAppBinNtPath.UniStr.Length / sizeof(WCHAR) },
+ { pCur->pwszAltSearchDir, pCur->cwcAltSearchDir },
+ };
+
+ /* Search System32 first, unless it's a 'V*' or 'm*' name, the latter for msvcrt. */
+ if ( pCur->szName[0] == 'v'
+ || pCur->szName[0] == 'V'
+ || pCur->szName[0] == 'm'
+ || pCur->szName[0] == 'M')
+ {
+ Tmp = aDirs[0];
+ aDirs[0] = aDirs[1];
+ aDirs[1] = Tmp;
+ }
+
+ for (uint32_t i = 0; i < RT_ELEMENTS(aDirs); i++)
+ {
+ if (aDirs[i].pawcDir && aDirs[i].cwcDir && aDirs[i].cwcDir < RT_ELEMENTS(wszPath) / 3 * 2)
+ {
+ memcpy(wszPath, aDirs[i].pawcDir, aDirs[i].cwcDir * sizeof(RTUTF16));
+ uint32_t cwc = aDirs[i].cwcDir;
+ wszPath[cwc++] = '\\';
+ cwcName = RT_ELEMENTS(wszPath) - cwc;
+ pwszName = &wszPath[cwc];
+ rc = RTStrToUtf16Ex(pCur->szName, RTSTR_MAX, &pwszName, cwcName, &cwcName);
+ if (RT_SUCCESS(rc))
+ {
+ IO_STATUS_BLOCK Ios = RTNT_IO_STATUS_BLOCK_INITIALIZER;
+ UNICODE_STRING NtName;
+ NtName.Buffer = wszPath;
+ NtName.Length = (USHORT)((cwc + cwcName) * sizeof(WCHAR));
+ NtName.MaximumLength = NtName.Length + sizeof(WCHAR);
+ OBJECT_ATTRIBUTES ObjAttr;
+ InitializeObjectAttributes(&ObjAttr, &NtName, OBJ_CASE_INSENSITIVE, NULL /*hRootDir*/, NULL /*pSecDesc*/);
+
+ rcNt = NtCreateFile(&hFile,
+ FILE_READ_DATA | READ_CONTROL | SYNCHRONIZE,
+ &ObjAttr,
+ &Ios,
+ NULL /* Allocation Size*/,
+ FILE_ATTRIBUTE_NORMAL,
+ FILE_SHARE_READ,
+ FILE_OPEN,
+ FILE_NON_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT,
+ NULL /*EaBuffer*/,
+ 0 /*EaLength*/);
+ if (NT_SUCCESS(rcNt))
+ rcNt = Ios.Status;
+ if (NT_SUCCESS(rcNt))
+ break;
+ hFile = INVALID_HANDLE_VALUE;
+ }
+ else
+ SUP_DPRINTF(("supR3HardenedWinVerifyCacheProcessImportTodos: RTStrToUtf16Ex #2 failed: %Rrc\n", rc));
+ }
+ }
+ }
+
+ /*
+ * If we successfully opened it, verify it and cache the result.
+ */
+ if (hFile != INVALID_HANDLE_VALUE)
+ {
+ SUP_DPRINTF(("supR3HardenedWinVerifyCacheProcessImportTodos: '%s' -> '%ls' [rcNtRedir=%#x]\n",
+ pCur->szName, wszPath, rcNtRedir));
+
+ ULONG fAccess = 0;
+ ULONG fProtect = 0;
+ bool fCallRealApi = false;
+ rcNt = supR3HardenedScreenImage(hFile, true /*fImage*/, false /*fIgnoreArch*/, &fAccess, &fProtect,
+ &fCallRealApi, "Imports", false /*fAvoidWinVerifyTrust*/, NULL /*pfQuiet*/);
+ NtClose(hFile);
+ }
+ else
+ SUP_DPRINTF(("supR3HardenedWinVerifyCacheProcessImportTodos: Failed to locate '%s'\n", pCur->szName));
+ }
+ else
+ SUP_DPRINTF(("supR3HardenedWinVerifyCacheProcessImportTodos: '%s' is in the cache.\n", pCur->szName));
+
+ RTMemFree(pCur);
+ } while (pTodo);
+ }
+}
+
+
+/**
+ * Processes the list of WinVerifyTrust todos.
+ */
+static void supR3HardenedWinVerifyCacheProcessWvtTodos(void)
+{
+ PVERIFIERCACHEENTRY pReschedule = NULL;
+ PVERIFIERCACHEENTRY volatile *ppReschedLastNext = &pReschedule;
+
+ /*
+ * Work until we've got nothing more todo.
+ */
+ for (;;)
+ {
+ if (!supHardenedWinIsWinVerifyTrustCallable())
+ break;
+ PVERIFIERCACHEENTRY pTodo = ASMAtomicXchgPtrT(&g_pVerifierCacheTodoWvt, NULL, PVERIFIERCACHEENTRY);
+ if (!pTodo)
+ break;
+ do
+ {
+ PVERIFIERCACHEENTRY pCur = pTodo;
+ pTodo = pTodo->pNextTodoWvt;
+ pCur->pNextTodoWvt = NULL;
+
+ if ( !pCur->fWinVerifyTrust
+ && RT_SUCCESS(pCur->rc))
+ {
+ bool fWinVerifyTrust = false;
+ int rc = supHardenedWinVerifyImageTrust(pCur->hFile, pCur->wszPath, pCur->fFlags, pCur->rc,
+ &fWinVerifyTrust, NULL /* pErrInfo*/);
+ if (RT_FAILURE(rc) || fWinVerifyTrust)
+ {
+ SUP_DPRINTF(("supR3HardenedWinVerifyCacheProcessWvtTodos: %d (was %d) fWinVerifyTrust=%d for '%ls'\n",
+ rc, pCur->rc, fWinVerifyTrust, pCur->wszPath));
+ pCur->fWinVerifyTrust = true;
+ pCur->rc = rc;
+ }
+ else
+ {
+ /* Retry it at a later time. */
+ SUP_DPRINTF(("supR3HardenedWinVerifyCacheProcessWvtTodos: %d (was %d) fWinVerifyTrust=%d for '%ls' [rescheduled]\n",
+ rc, pCur->rc, fWinVerifyTrust, pCur->wszPath));
+ *ppReschedLastNext = pCur;
+ ppReschedLastNext = &pCur->pNextTodoWvt;
+ }
+ }
+ /* else: already processed. */
+ } while (pTodo);
+ }
+
+ /*
+ * Anything to reschedule.
+ */
+ if (pReschedule)
+ {
+ do
+ *ppReschedLastNext = g_pVerifierCacheTodoWvt;
+ while (!ASMAtomicCmpXchgPtr(&g_pVerifierCacheTodoWvt, pReschedule, *ppReschedLastNext));
+ }
+}
+
+
+/**
+ * Translates VBox status code (from supHardenedWinVerifyImageTrust) to an NT
+ * status.
+ *
+ * @returns NT status.
+ * @param rc VBox status code.
+ */
+static NTSTATUS supR3HardenedScreenImageCalcStatus(int rc) RT_NOTHROW_DEF
+{
+ /* This seems to be what LdrLoadDll returns when loading a 32-bit DLL into
+ a 64-bit process. At least here on windows 10 (2015-11-xx).
+
+ NtCreateSection probably returns something different, possibly a warning,
+ we currently don't distinguish between the too, so we stick with the
+ LdrLoadDll one as it's definitely an error.*/
+ if (rc == VERR_LDR_ARCH_MISMATCH)
+ return STATUS_INVALID_IMAGE_FORMAT;
+
+ return STATUS_TRUST_FAILURE;
+}
+
+
+/**
+ * Screens an image file or file mapped with execute access.
+ *
+ * @returns NT status code.
+ * @param hFile The file handle.
+ * @param fImage Set if image file mapping being made
+ * (NtCreateSection thing).
+ * @param fIgnoreArch Using the DONT_RESOLVE_DLL_REFERENCES flag,
+ * which also implies that DLL init / term code
+ * isn't called, so the architecture should be
+ * ignored.
+ * @param pfAccess Pointer to the NtCreateSection access flags,
+ * so we can modify them if necessary.
+ * @param pfProtect Pointer to the NtCreateSection protection
+ * flags, so we can modify them if necessary.
+ * @param pfCallRealApi Whether it's ok to go on to the real API.
+ * @param pszCaller Who is calling (for debugging / logging).
+ * @param fAvoidWinVerifyTrust Whether we should avoid WinVerifyTrust.
+ * @param pfQuiet Where to return whether to be quiet about
+ * this image in the log (i.e. we've seen it
+ * lots of times already). Optional.
+ */
+static NTSTATUS
+supR3HardenedScreenImage(HANDLE hFile, bool fImage, bool fIgnoreArch, PULONG pfAccess, PULONG pfProtect,
+ bool *pfCallRealApi, const char *pszCaller, bool fAvoidWinVerifyTrust, bool *pfQuiet) RT_NOTHROW_DEF
+{
+ *pfCallRealApi = false;
+ if (pfQuiet)
+ *pfQuiet = false;
+
+ /*
+ * Query the name of the file, making sure to zero terminator the
+ * string. (2nd half of buffer is used for error info, see below.)
+ */
+ union
+ {
+ UNICODE_STRING UniStr;
+ uint8_t abBuffer[sizeof(UNICODE_STRING) + 2048 * sizeof(WCHAR)];
+ } uBuf;
+ RT_ZERO(uBuf);
+ ULONG cbNameBuf;
+ NTSTATUS rcNt = NtQueryObject(hFile, ObjectNameInformation, &uBuf, sizeof(uBuf) - sizeof(WCHAR) - 128, &cbNameBuf);
+ if (!NT_SUCCESS(rcNt))
+ {
+ supR3HardenedError(VINF_SUCCESS, false,
+ "supR3HardenedScreenImage/%s: NtQueryObject -> %#x (fImage=%d fProtect=%#x fAccess=%#x)\n",
+ pszCaller, fImage, *pfProtect, *pfAccess);
+ return rcNt;
+ }
+
+ if (!RTNtPathFindPossible8dot3Name(uBuf.UniStr.Buffer))
+ cbNameBuf += sizeof(WCHAR);
+ else
+ {
+ uBuf.UniStr.MaximumLength = sizeof(uBuf) - 128;
+ RTNtPathExpand8dot3Path(&uBuf.UniStr, true /*fPathOnly*/);
+ cbNameBuf = (uintptr_t)uBuf.UniStr.Buffer + uBuf.UniStr.Length + sizeof(WCHAR) - (uintptr_t)&uBuf.abBuffer[0];
+ }
+
+ /*
+ * Check the cache.
+ */
+ PVERIFIERCACHEENTRY pCacheHit = supR3HardenedWinVerifyCacheLookup(&uBuf.UniStr, hFile);
+ if (pCacheHit)
+ {
+ /* Do hit accounting and figure whether we need to be quiet or not. */
+ uint32_t cHits = ASMAtomicIncU32(&pCacheHit->cHits);
+ bool const fQuiet = cHits >= 8 && !RT_IS_POWER_OF_TWO(cHits);
+ if (pfQuiet)
+ *pfQuiet = fQuiet;
+
+ /* If we haven't done the WinVerifyTrust thing, do it if we can. */
+ if ( !pCacheHit->fWinVerifyTrust
+ && RT_SUCCESS(pCacheHit->rc)
+ && supHardenedWinIsWinVerifyTrustCallable() )
+ {
+ if (!fAvoidWinVerifyTrust)
+ {
+ SUP_DPRINTF(("supR3HardenedScreenImage/%s: cache hit (%Rrc) on %ls [redoing WinVerifyTrust]\n",
+ pszCaller, pCacheHit->rc, pCacheHit->wszPath));
+
+ bool fWinVerifyTrust = false;
+ int rc = supHardenedWinVerifyImageTrust(pCacheHit->hFile, pCacheHit->wszPath, pCacheHit->fFlags, pCacheHit->rc,
+ &fWinVerifyTrust, NULL /* pErrInfo*/);
+ if (RT_FAILURE(rc) || fWinVerifyTrust)
+ {
+ SUP_DPRINTF(("supR3HardenedScreenImage/%s: %d (was %d) fWinVerifyTrust=%d for '%ls'\n",
+ pszCaller, rc, pCacheHit->rc, fWinVerifyTrust, pCacheHit->wszPath));
+ pCacheHit->fWinVerifyTrust = true;
+ pCacheHit->rc = rc;
+ }
+ else
+ SUP_DPRINTF(("supR3HardenedScreenImage/%s: WinVerifyTrust not available, rescheduling %ls\n",
+ pszCaller, pCacheHit->wszPath));
+ }
+ else
+ SUP_DPRINTF(("supR3HardenedScreenImage/%s: cache hit (%Rrc) on %ls [avoiding WinVerifyTrust]\n",
+ pszCaller, pCacheHit->rc, pCacheHit->wszPath));
+ }
+ else if (!fQuiet || !pCacheHit->fWinVerifyTrust)
+ SUP_DPRINTF(("supR3HardenedScreenImage/%s: cache hit (%Rrc) on %ls%s\n",
+ pszCaller, pCacheHit->rc, pCacheHit->wszPath, pCacheHit->fWinVerifyTrust ? "" : " [lacks WinVerifyTrust]"));
+
+ /* Return the cached value. */
+ if (RT_SUCCESS(pCacheHit->rc))
+ {
+ *pfCallRealApi = true;
+ return STATUS_SUCCESS;
+ }
+
+ if (!fQuiet)
+ supR3HardenedError(VINF_SUCCESS, false,
+ "supR3HardenedScreenImage/%s: cached rc=%Rrc fImage=%d fProtect=%#x fAccess=%#x cHits=%u %ls\n",
+ pszCaller, pCacheHit->rc, fImage, *pfProtect, *pfAccess, cHits, uBuf.UniStr.Buffer);
+ return supR3HardenedScreenImageCalcStatus(pCacheHit->rc);
+ }
+
+ /*
+ * On XP the loader might hand us handles with just FILE_EXECUTE and
+ * SYNCHRONIZE, the means reading will fail later on. Also, we need
+ * READ_CONTROL access to check the file ownership later on, and non
+ * of the OS versions seems be giving us that. So, in effect we
+ * more or less always reopen the file here.
+ */
+ HANDLE hMyFile = NULL;
+ rcNt = NtDuplicateObject(NtCurrentProcess(), hFile, NtCurrentProcess(),
+ &hMyFile,
+ FILE_READ_DATA | READ_CONTROL | SYNCHRONIZE,
+ 0 /* Handle attributes*/, 0 /* Options */);
+ if (!NT_SUCCESS(rcNt))
+ {
+ if (rcNt == STATUS_ACCESS_DENIED)
+ {
+ IO_STATUS_BLOCK Ios = RTNT_IO_STATUS_BLOCK_INITIALIZER;
+ OBJECT_ATTRIBUTES ObjAttr;
+ InitializeObjectAttributes(&ObjAttr, &uBuf.UniStr, OBJ_CASE_INSENSITIVE, NULL /*hRootDir*/, NULL /*pSecDesc*/);
+
+ rcNt = NtCreateFile(&hMyFile,
+ FILE_READ_DATA | READ_CONTROL | SYNCHRONIZE,
+ &ObjAttr,
+ &Ios,
+ NULL /* Allocation Size*/,
+ FILE_ATTRIBUTE_NORMAL,
+ FILE_SHARE_READ,
+ FILE_OPEN,
+ FILE_NON_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT,
+ NULL /*EaBuffer*/,
+ 0 /*EaLength*/);
+ if (NT_SUCCESS(rcNt))
+ rcNt = Ios.Status;
+ if (!NT_SUCCESS(rcNt))
+ {
+ supR3HardenedError(VINF_SUCCESS, false,
+ "supR3HardenedScreenImage/%s: Failed to duplicate and open the file: rcNt=%#x hFile=%p %ls\n",
+ pszCaller, rcNt, hFile, uBuf.UniStr.Buffer);
+ return rcNt;
+ }
+
+ /* Check that we've got the same file. */
+ LARGE_INTEGER idMyFile, idInFile;
+ bool fMyValid = supR3HardenedWinVerifyCacheGetIndexNumber(hMyFile, &idMyFile);
+ bool fInValid = supR3HardenedWinVerifyCacheGetIndexNumber(hFile, &idInFile);
+ if ( fMyValid
+ && ( fMyValid != fInValid
+ || idMyFile.QuadPart != idInFile.QuadPart))
+ {
+ supR3HardenedError(VINF_SUCCESS, false,
+ "supR3HardenedScreenImage/%s: Re-opened has different ID that input: %#llx vx %#llx (%ls)\n",
+ pszCaller, rcNt, idMyFile.QuadPart, idInFile.QuadPart, uBuf.UniStr.Buffer);
+ NtClose(hMyFile);
+ return STATUS_TRUST_FAILURE;
+ }
+ }
+ else
+ {
+ SUP_DPRINTF(("supR3HardenedScreenImage/%s: NtDuplicateObject -> %#x\n", pszCaller, rcNt));
+#ifdef DEBUG
+
+ supR3HardenedError(VINF_SUCCESS, false,
+ "supR3HardenedScreenImage/%s: NtDuplicateObject(,%#x,) failed: %#x\n", pszCaller, hFile, rcNt);
+#endif
+ hMyFile = hFile;
+ }
+ }
+
+ /*
+ * Special Kludge for Windows XP and W2K3 and their stupid attempts
+ * at mapping a hidden XML file called c:\Windows\WindowsShell.Manifest
+ * with executable access. The image bit isn't set, fortunately.
+ */
+ if ( !fImage
+ && uBuf.UniStr.Length > g_System32NtPath.UniStr.Length - sizeof(L"System32") + sizeof(WCHAR)
+ && memcmp(uBuf.UniStr.Buffer, g_System32NtPath.UniStr.Buffer,
+ g_System32NtPath.UniStr.Length - sizeof(L"System32") + sizeof(WCHAR)) == 0)
+ {
+ PRTUTF16 pwszName = &uBuf.UniStr.Buffer[(g_System32NtPath.UniStr.Length - sizeof(L"System32") + sizeof(WCHAR)) / sizeof(WCHAR)];
+ if (RTUtf16ICmpAscii(pwszName, "WindowsShell.Manifest") == 0)
+ {
+ /*
+ * Drop all executable access to the mapping and let it continue.
+ */
+ SUP_DPRINTF(("supR3HardenedScreenImage/%s: Applying the drop-exec-kludge for '%ls'\n", pszCaller, uBuf.UniStr.Buffer));
+ if (*pfAccess & SECTION_MAP_EXECUTE)
+ *pfAccess = (*pfAccess & ~SECTION_MAP_EXECUTE) | SECTION_MAP_READ;
+ if (*pfProtect & PAGE_EXECUTE)
+ *pfProtect = (*pfProtect & ~PAGE_EXECUTE) | PAGE_READONLY;
+ *pfProtect = (*pfProtect & ~UINT32_C(0xf0)) | ((*pfProtect & UINT32_C(0xe0)) >> 4);
+ if (hMyFile != hFile)
+ NtClose(hMyFile);
+ *pfCallRealApi = true;
+ return STATUS_SUCCESS;
+ }
+ }
+
+#ifndef VBOX_PERMIT_EVEN_MORE
+ /*
+ * Check the path. We don't allow DLLs to be loaded from just anywhere:
+ * 1. System32 - normal code or cat signing, owner TrustedInstaller.
+ * 2. WinSxS - normal code or cat signing, owner TrustedInstaller.
+ * 3. VirtualBox - kernel code signing and integrity checks.
+ * 4. AppPatchDir - normal code or cat signing, owner TrustedInstaller.
+ * 5. Program Files - normal code or cat signing, owner TrustedInstaller.
+ * 6. Common Files - normal code or cat signing, owner TrustedInstaller.
+ * 7. x86 variations of 4 & 5 - ditto.
+ */
+ uint32_t fFlags = 0;
+ if (supHardViUniStrPathStartsWithUniStr(&uBuf.UniStr, &g_System32NtPath.UniStr, true /*fCheckSlash*/))
+ fFlags |= SUPHNTVI_F_ALLOW_CAT_FILE_VERIFICATION | SUPHNTVI_F_TRUSTED_INSTALLER_OWNER;
+ else if (supHardViUniStrPathStartsWithUniStr(&uBuf.UniStr, &g_WinSxSNtPath.UniStr, true /*fCheckSlash*/))
+ fFlags |= SUPHNTVI_F_ALLOW_CAT_FILE_VERIFICATION | SUPHNTVI_F_TRUSTED_INSTALLER_OWNER;
+ else if (supHardViUniStrPathStartsWithUniStr(&uBuf.UniStr, &g_SupLibHardenedAppBinNtPath.UniStr, true /*fCheckSlash*/))
+ fFlags |= SUPHNTVI_F_REQUIRE_KERNEL_CODE_SIGNING | SUPHNTVI_F_REQUIRE_SIGNATURE_ENFORCEMENT;
+# ifdef VBOX_PERMIT_MORE
+ else if (supHardViIsAppPatchDir(uBuf.UniStr.Buffer, uBuf.UniStr.Length / sizeof(WCHAR)))
+ fFlags |= SUPHNTVI_F_ALLOW_CAT_FILE_VERIFICATION | SUPHNTVI_F_TRUSTED_INSTALLER_OWNER;
+ else if (supHardViUniStrPathStartsWithUniStr(&uBuf.UniStr, &g_ProgramFilesNtPath.UniStr, true /*fCheckSlash*/))
+ fFlags |= SUPHNTVI_F_ALLOW_CAT_FILE_VERIFICATION | SUPHNTVI_F_TRUSTED_INSTALLER_OWNER;
+ else if (supHardViUniStrPathStartsWithUniStr(&uBuf.UniStr, &g_CommonFilesNtPath.UniStr, true /*fCheckSlash*/))
+ fFlags |= SUPHNTVI_F_ALLOW_CAT_FILE_VERIFICATION | SUPHNTVI_F_TRUSTED_INSTALLER_OWNER;
+# ifdef RT_ARCH_AMD64
+ else if (supHardViUniStrPathStartsWithUniStr(&uBuf.UniStr, &g_ProgramFilesX86NtPath.UniStr, true /*fCheckSlash*/))
+ fFlags |= SUPHNTVI_F_ALLOW_CAT_FILE_VERIFICATION | SUPHNTVI_F_TRUSTED_INSTALLER_OWNER;
+ else if (supHardViUniStrPathStartsWithUniStr(&uBuf.UniStr, &g_CommonFilesX86NtPath.UniStr, true /*fCheckSlash*/))
+ fFlags |= SUPHNTVI_F_ALLOW_CAT_FILE_VERIFICATION | SUPHNTVI_F_TRUSTED_INSTALLER_OWNER;
+# endif
+# endif
+# ifdef VBOX_PERMIT_VISUAL_STUDIO_PROFILING
+ /* Hack to allow profiling our code with Visual Studio. */
+ else if ( uBuf.UniStr.Length > sizeof(L"\\SamplingRuntime.dll")
+ && memcmp(uBuf.UniStr.Buffer + (uBuf.UniStr.Length - sizeof(L"\\SamplingRuntime.dll") + sizeof(WCHAR)) / sizeof(WCHAR),
+ L"\\SamplingRuntime.dll", sizeof(L"\\SamplingRuntime.dll") - sizeof(WCHAR)) == 0 )
+ {
+ if (hMyFile != hFile)
+ NtClose(hMyFile);
+ *pfCallRealApi = true;
+ return STATUS_SUCCESS;
+ }
+# endif
+ else
+ {
+ supR3HardenedError(VINF_SUCCESS, false,
+ "supR3HardenedScreenImage/%s: Not a trusted location: '%ls' (fImage=%d fProtect=%#x fAccess=%#x)\n",
+ pszCaller, uBuf.UniStr.Buffer, fImage, *pfAccess, *pfProtect);
+ if (hMyFile != hFile)
+ NtClose(hMyFile);
+ return STATUS_TRUST_FAILURE;
+ }
+
+#else /* VBOX_PERMIT_EVEN_MORE */
+ /*
+ * Require trusted installer + some kind of signature on everything, except
+ * for the VBox bits where we require kernel code signing and special
+ * integrity checks.
+ */
+ uint32_t fFlags = 0;
+ if (supHardViUniStrPathStartsWithUniStr(&uBuf.UniStr, &g_SupLibHardenedAppBinNtPath.UniStr, true /*fCheckSlash*/))
+ fFlags |= SUPHNTVI_F_REQUIRE_KERNEL_CODE_SIGNING | SUPHNTVI_F_REQUIRE_SIGNATURE_ENFORCEMENT;
+ else
+ fFlags |= SUPHNTVI_F_ALLOW_CAT_FILE_VERIFICATION | SUPHNTVI_F_TRUSTED_INSTALLER_OWNER;
+#endif /* VBOX_PERMIT_EVEN_MORE */
+
+ /*
+ * Do the verification. For better error message we borrow what's
+ * left of the path buffer for an RTERRINFO buffer.
+ */
+ if (fIgnoreArch)
+ fFlags |= SUPHNTVI_F_IGNORE_ARCHITECTURE;
+ RTERRINFO ErrInfo;
+ RTErrInfoInit(&ErrInfo, (char *)&uBuf.abBuffer[cbNameBuf], sizeof(uBuf) - cbNameBuf);
+
+ int rc;
+ bool fWinVerifyTrust = false;
+ rc = supHardenedWinVerifyImageByHandle(hMyFile, uBuf.UniStr.Buffer, fFlags, fAvoidWinVerifyTrust, &fWinVerifyTrust, &ErrInfo);
+ if (RT_FAILURE(rc))
+ {
+ supR3HardenedError(VINF_SUCCESS, false,
+ "supR3HardenedScreenImage/%s: rc=%Rrc fImage=%d fProtect=%#x fAccess=%#x %ls: %s\n",
+ pszCaller, rc, fImage, *pfAccess, *pfProtect, uBuf.UniStr.Buffer, ErrInfo.pszMsg);
+ if (hMyFile != hFile)
+ supR3HardenedWinVerifyCacheInsert(&uBuf.UniStr, hMyFile, rc, fWinVerifyTrust, fFlags);
+ return supR3HardenedScreenImageCalcStatus(rc);
+ }
+
+ /*
+ * Insert into the cache.
+ */
+ if (hMyFile != hFile)
+ supR3HardenedWinVerifyCacheInsert(&uBuf.UniStr, hMyFile, rc, fWinVerifyTrust, fFlags);
+
+ *pfCallRealApi = true;
+ return STATUS_SUCCESS;
+}
+
+
+/**
+ * Preloads a file into the verify cache if possible.
+ *
+ * This is used to avoid known cyclic LoadLibrary issues with WinVerifyTrust.
+ *
+ * @param pwszName The name of the DLL to verify.
+ */
+DECLHIDDEN(void) supR3HardenedWinVerifyCachePreload(PCRTUTF16 pwszName)
+{
+ HANDLE hFile = RTNT_INVALID_HANDLE_VALUE;
+ IO_STATUS_BLOCK Ios = RTNT_IO_STATUS_BLOCK_INITIALIZER;
+
+ UNICODE_STRING UniStr;
+ UniStr.Buffer = (PWCHAR)pwszName;
+ UniStr.Length = (USHORT)(RTUtf16Len(pwszName) * sizeof(WCHAR));
+ UniStr.MaximumLength = UniStr.Length + sizeof(WCHAR);
+
+ OBJECT_ATTRIBUTES ObjAttr;
+ InitializeObjectAttributes(&ObjAttr, &UniStr, OBJ_CASE_INSENSITIVE, NULL /*hRootDir*/, NULL /*pSecDesc*/);
+
+ NTSTATUS rcNt = NtCreateFile(&hFile,
+ FILE_READ_DATA | READ_CONTROL | SYNCHRONIZE,
+ &ObjAttr,
+ &Ios,
+ NULL /* Allocation Size*/,
+ FILE_ATTRIBUTE_NORMAL,
+ FILE_SHARE_READ,
+ FILE_OPEN,
+ FILE_NON_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT,
+ NULL /*EaBuffer*/,
+ 0 /*EaLength*/);
+ if (NT_SUCCESS(rcNt))
+ rcNt = Ios.Status;
+ if (!NT_SUCCESS(rcNt))
+ {
+ SUP_DPRINTF(("supR3HardenedWinVerifyCachePreload: Error %#x opening '%ls'.\n", rcNt, pwszName));
+ return;
+ }
+
+ ULONG fAccess = 0;
+ ULONG fProtect = 0;
+ bool fCallRealApi;
+ //SUP_DPRINTF(("supR3HardenedWinVerifyCachePreload: scanning %ls\n", pwszName));
+ supR3HardenedScreenImage(hFile, false, false /*fIgnoreArch*/, &fAccess, &fProtect, &fCallRealApi, "preload",
+ false /*fAvoidWinVerifyTrust*/, NULL /*pfQuiet*/);
+ //SUP_DPRINTF(("supR3HardenedWinVerifyCachePreload: done %ls\n", pwszName));
+
+ NtClose(hFile);
+}
+
+
+
+/**
+ * Hook that monitors NtCreateSection calls.
+ *
+ * @returns NT status code.
+ * @param phSection Where to return the section handle.
+ * @param fAccess The desired access.
+ * @param pObjAttribs The object attributes (optional).
+ * @param pcbSection The section size (optional).
+ * @param fProtect The max section protection.
+ * @param fAttribs The section attributes.
+ * @param hFile The file to create a section from (optional).
+ */
+__declspec(guard(ignore)) /* don't barf when calling g_pfnNtCreateSectionReal */
+static NTSTATUS NTAPI
+supR3HardenedMonitor_NtCreateSection(PHANDLE phSection, ACCESS_MASK fAccess, POBJECT_ATTRIBUTES pObjAttribs,
+ PLARGE_INTEGER pcbSection, ULONG fProtect, ULONG fAttribs, HANDLE hFile)
+{
+ bool fNeedUncChecking = false;
+ if ( hFile != NULL
+ && hFile != INVALID_HANDLE_VALUE)
+ {
+ bool const fImage = RT_BOOL(fAttribs & (SEC_IMAGE | SEC_PROTECTED_IMAGE));
+ bool const fExecMap = RT_BOOL(fAccess & SECTION_MAP_EXECUTE);
+ bool const fExecProt = RT_BOOL(fProtect & (PAGE_EXECUTE | PAGE_EXECUTE_READ | PAGE_EXECUTE_WRITECOPY
+ | PAGE_EXECUTE_READWRITE));
+ if (fImage || fExecMap || fExecProt)
+ {
+ fNeedUncChecking = true;
+ DWORD dwSavedLastError = RtlGetLastWin32Error();
+
+ bool fCallRealApi;
+ //SUP_DPRINTF(("supR3HardenedMonitor_NtCreateSection: 1\n"));
+ NTSTATUS rcNt = supR3HardenedScreenImage(hFile, fImage, true /*fIgnoreArch*/, &fAccess, &fProtect, &fCallRealApi,
+ "NtCreateSection", true /*fAvoidWinVerifyTrust*/, NULL /*pfQuiet*/);
+ //SUP_DPRINTF(("supR3HardenedMonitor_NtCreateSection: 2 rcNt=%#x fCallRealApi=%#x\n", rcNt, fCallRealApi));
+
+ RtlRestoreLastWin32Error(dwSavedLastError);
+
+ if (!NT_SUCCESS(rcNt))
+ return rcNt;
+ Assert(fCallRealApi);
+ if (!fCallRealApi)
+ return STATUS_TRUST_FAILURE;
+
+ }
+ }
+
+ /*
+ * Call checked out OK, call the original.
+ */
+ NTSTATUS rcNtReal = g_pfnNtCreateSectionReal(phSection, fAccess, pObjAttribs, pcbSection, fProtect, fAttribs, hFile);
+
+ /*
+ * Check that the image that got mapped bear some resemblance to the one that was
+ * requested. Apparently there are ways to trick the NT cache manager to map a
+ * file different from hFile into memory using local UNC accesses.
+ */
+ if ( NT_SUCCESS(rcNtReal)
+ && fNeedUncChecking)
+ {
+ DWORD dwSavedLastError = RtlGetLastWin32Error();
+
+ bool fOkay = false;
+
+ /* To get the name of the file backing the section, we unfortunately have to map it. */
+ SIZE_T cbView = 0;
+ PVOID pvTmpMap = NULL;
+ NTSTATUS rcNt = NtMapViewOfSection(*phSection, NtCurrentProcess(), &pvTmpMap, 0, 0, NULL /*poffSection*/, &cbView,
+ ViewUnmap, MEM_TOP_DOWN, PAGE_EXECUTE);
+ if (NT_SUCCESS(rcNt))
+ {
+ /* Query the name. */
+ union
+ {
+ UNICODE_STRING UniStr;
+ RTUTF16 awcBuf[512];
+ } uBuf;
+ RT_ZERO(uBuf);
+ SIZE_T cbActual = 0;
+ NTSTATUS rcNtQuery = NtQueryVirtualMemory(NtCurrentProcess(), pvTmpMap, MemorySectionName,
+ &uBuf, sizeof(uBuf) - sizeof(RTUTF16), &cbActual);
+
+ /* Unmap the view. */
+ rcNt = NtUnmapViewOfSection(NtCurrentProcess(), pvTmpMap);
+ if (!NT_SUCCESS(rcNt))
+ SUP_DPRINTF(("supR3HardenedMonitor_NtCreateSection: NtUnmapViewOfSection failed on %p (hSection=%p, hFile=%p) with %#x!\n",
+ pvTmpMap, *phSection, hFile, rcNt));
+
+ /* Process the name query result. */
+ if (NT_SUCCESS(rcNtQuery))
+ {
+ static UNICODE_STRING const s_UncPrefix = RTNT_CONSTANT_UNISTR(L"\\Device\\Mup");
+ if (!supHardViUniStrPathStartsWithUniStr(&uBuf.UniStr, &s_UncPrefix, true /*fCheckSlash*/))
+ fOkay = true;
+ else
+ supR3HardenedError(VINF_SUCCESS, false,
+ "supR3HardenedMonitor_NtCreateSection: Image section with UNC path is not trusted: '%.*ls'\n",
+ uBuf.UniStr.Length / sizeof(RTUTF16), uBuf.UniStr.Buffer);
+ }
+ else
+ SUP_DPRINTF(("supR3HardenedMonitor_NtCreateSection: NtQueryVirtualMemory failed on %p (hFile=%p) with %#x -> STATUS_TRUST_FAILURE\n",
+ *phSection, hFile, rcNt));
+ }
+ else
+ SUP_DPRINTF(("supR3HardenedMonitor_NtCreateSection: NtMapViewOfSection failed on %p (hFile=%p) with %#x -> STATUS_TRUST_FAILURE\n",
+ *phSection, hFile, rcNt));
+ if (!fOkay)
+ {
+ NtClose(*phSection);
+ *phSection = INVALID_HANDLE_VALUE;
+ RtlRestoreLastWin32Error(dwSavedLastError);
+ return STATUS_TRUST_FAILURE;
+ }
+
+ RtlRestoreLastWin32Error(dwSavedLastError);
+ }
+ return rcNtReal;
+}
+
+
+/**
+ * Checks if the given name is a valid ApiSet name.
+ *
+ * This is only called on likely looking names.
+ *
+ * @returns true if ApiSet name, false if not.
+ * @param pName The name to check out.
+ */
+static bool supR3HardenedIsApiSetDll(PUNICODE_STRING pName)
+{
+ /*
+ * API added in Windows 8, or so they say.
+ */
+ if (ApiSetQueryApiSetPresence != NULL)
+ {
+ BOOLEAN fPresent = FALSE;
+ NTSTATUS rcNt = ApiSetQueryApiSetPresence(pName, &fPresent);
+ SUP_DPRINTF(("supR3HardenedIsApiSetDll: ApiSetQueryApiSetPresence(%.*ls) -> %#x, fPresent=%d\n",
+ pName->Length / sizeof(WCHAR), pName->Buffer, rcNt, fPresent));
+ return fPresent != 0;
+ }
+
+ /*
+ * Fallback needed for Windows 7. Fortunately, there aren't too many fake DLLs here.
+ */
+ if ( g_uNtVerCombined >= SUP_NT_VER_W70
+ && ( supHardViUtf16PathStartsWithEx(pName->Buffer, pName->Length / sizeof(WCHAR),
+ L"api-ms-win-", 11, false /*fCheckSlash*/)
+ || supHardViUtf16PathStartsWithEx(pName->Buffer, pName->Length / sizeof(WCHAR),
+ L"ext-ms-win-", 11, false /*fCheckSlash*/) ))
+ {
+#define MY_ENTRY(a) { a, sizeof(a) - 1 }
+ static const struct { const char *psz; size_t cch; } s_aKnownSets[] =
+ {
+ MY_ENTRY("api-ms-win-core-console-l1-1-0 "),
+ MY_ENTRY("api-ms-win-core-datetime-l1-1-0"),
+ MY_ENTRY("api-ms-win-core-debug-l1-1-0"),
+ MY_ENTRY("api-ms-win-core-delayload-l1-1-0"),
+ MY_ENTRY("api-ms-win-core-errorhandling-l1-1-0"),
+ MY_ENTRY("api-ms-win-core-fibers-l1-1-0"),
+ MY_ENTRY("api-ms-win-core-file-l1-1-0"),
+ MY_ENTRY("api-ms-win-core-handle-l1-1-0"),
+ MY_ENTRY("api-ms-win-core-heap-l1-1-0"),
+ MY_ENTRY("api-ms-win-core-interlocked-l1-1-0"),
+ MY_ENTRY("api-ms-win-core-io-l1-1-0"),
+ MY_ENTRY("api-ms-win-core-libraryloader-l1-1-0"),
+ MY_ENTRY("api-ms-win-core-localization-l1-1-0"),
+ MY_ENTRY("api-ms-win-core-localregistry-l1-1-0"),
+ MY_ENTRY("api-ms-win-core-memory-l1-1-0"),
+ MY_ENTRY("api-ms-win-core-misc-l1-1-0"),
+ MY_ENTRY("api-ms-win-core-namedpipe-l1-1-0"),
+ MY_ENTRY("api-ms-win-core-processenvironment-l1-1-0"),
+ MY_ENTRY("api-ms-win-core-processthreads-l1-1-0"),
+ MY_ENTRY("api-ms-win-core-profile-l1-1-0"),
+ MY_ENTRY("api-ms-win-core-rtlsupport-l1-1-0"),
+ MY_ENTRY("api-ms-win-core-string-l1-1-0"),
+ MY_ENTRY("api-ms-win-core-synch-l1-1-0"),
+ MY_ENTRY("api-ms-win-core-sysinfo-l1-1-0"),
+ MY_ENTRY("api-ms-win-core-threadpool-l1-1-0"),
+ MY_ENTRY("api-ms-win-core-ums-l1-1-0"),
+ MY_ENTRY("api-ms-win-core-util-l1-1-0"),
+ MY_ENTRY("api-ms-win-core-xstate-l1-1-0"),
+ MY_ENTRY("api-ms-win-security-base-l1-1-0"),
+ MY_ENTRY("api-ms-win-security-lsalookup-l1-1-0"),
+ MY_ENTRY("api-ms-win-security-sddl-l1-1-0"),
+ MY_ENTRY("api-ms-win-service-core-l1-1-0"),
+ MY_ENTRY("api-ms-win-service-management-l1-1-0"),
+ MY_ENTRY("api-ms-win-service-management-l2-1-0"),
+ MY_ENTRY("api-ms-win-service-winsvc-l1-1-0"),
+ };
+#undef MY_ENTRY
+
+ /* drop the dll suffix if present. */
+ PCRTUTF16 pawcName = pName->Buffer;
+ size_t cwcName = pName->Length / sizeof(WCHAR);
+ if ( cwcName > 5
+ && (pawcName[cwcName - 1] == 'l' || pawcName[cwcName - 1] == 'L')
+ && (pawcName[cwcName - 2] == 'l' || pawcName[cwcName - 2] == 'L')
+ && (pawcName[cwcName - 3] == 'd' || pawcName[cwcName - 3] == 'D')
+ && pawcName[cwcName - 4] == '.')
+ cwcName -= 4;
+
+ /* Search the table. */
+ for (size_t i = 0; i < RT_ELEMENTS(s_aKnownSets); i++)
+ if ( cwcName == s_aKnownSets[i].cch
+ && RTUtf16NICmpAscii(pawcName, s_aKnownSets[i].psz, cwcName) == 0)
+ {
+ SUP_DPRINTF(("supR3HardenedIsApiSetDll: '%.*ls' -> true\n", pName->Length / sizeof(WCHAR)));
+ return true;
+ }
+
+ SUP_DPRINTF(("supR3HardenedIsApiSetDll: Warning! '%.*ls' looks like an API set, but it's not in the list!\n",
+ pName->Length / sizeof(WCHAR), pName->Buffer));
+ }
+
+ SUP_DPRINTF(("supR3HardenedIsApiSetDll: '%.*ls' -> false\n", pName->Length / sizeof(WCHAR)));
+ return false;
+}
+
+
+/**
+ * Checks whether the given unicode string contains a path separator and at
+ * least one dash.
+ *
+ * This is used to check for likely ApiSet name. So far, all the pseudo DLL
+ * names include multiple dashes, so we use that as a criteria for recognizing
+ * them. By happy coincident, most regular DLLs doesn't include dashes.
+ *
+ * @returns true if it contains path separator, false if only a name.
+ * @param pPath The path to check.
+ */
+static bool supR3HardenedHasDashButNoPath(PUNICODE_STRING pPath)
+{
+ size_t cDashes = 0;
+ size_t cwcLeft = pPath->Length / sizeof(WCHAR);
+ PCRTUTF16 pwc = pPath->Buffer;
+ while (cwcLeft-- > 0)
+ {
+ RTUTF16 wc = *pwc++;
+ switch (wc)
+ {
+ default:
+ break;
+
+ case '-':
+ cDashes++;
+ break;
+
+ case '\\':
+ case '/':
+ case ':':
+ return false;
+ }
+ }
+ return cDashes > 0;
+}
+
+
+/**
+ * Helper for supR3HardenedMonitor_LdrLoadDll.
+ *
+ * @returns NT status code.
+ * @param pwszPath The path destination buffer.
+ * @param cwcPath The size of the path buffer.
+ * @param pUniStrResult The result string.
+ * @param pOrgName The orignal name (for errors).
+ * @param pcwc Where to return the actual length.
+ */
+static NTSTATUS supR3HardenedCopyRedirectionResult(WCHAR *pwszPath, size_t cwcPath, PUNICODE_STRING pUniStrResult,
+ PUNICODE_STRING pOrgName, UINT *pcwc)
+{
+ UINT cwc;
+ *pcwc = cwc = pUniStrResult->Length / sizeof(WCHAR);
+ if (pUniStrResult->Buffer == pwszPath)
+ pwszPath[cwc] = '\0';
+ else
+ {
+ if (cwc > cwcPath - 1)
+ {
+ supR3HardenedError(VINF_SUCCESS, false,
+ "supR3HardenedMonitor_LdrLoadDll: Name too long: %.*ls -> %.*ls (RtlDosApplyFileIoslationRedirection_Ustr)\n",
+ pOrgName->Length / sizeof(WCHAR), pOrgName->Buffer,
+ pUniStrResult->Length / sizeof(WCHAR), pUniStrResult->Buffer);
+ return STATUS_NAME_TOO_LONG;
+ }
+ memcpy(&pwszPath[0], pUniStrResult->Buffer, pUniStrResult->Length);
+ pwszPath[cwc] = '\0';
+ }
+ return STATUS_SUCCESS;
+}
+
+
+/**
+ * Helper for supR3HardenedMonitor_LdrLoadDll that compares the name part of the
+ * input path against a ASCII name string of a given length.
+ *
+ * @returns true if the name part matches
+ * @param pPath The LdrLoadDll input path.
+ * @param pszName The name to try match it with.
+ * @param cchName The name length.
+ */
+static bool supR3HardenedIsFilenameMatchDll(PUNICODE_STRING pPath, const char *pszName, size_t cchName)
+{
+ if (pPath->Length < cchName * 2)
+ return false;
+ PCRTUTF16 pwszTmp = &pPath->Buffer[pPath->Length / sizeof(RTUTF16) - cchName];
+ if ( pPath->Length != cchName
+ && pwszTmp[-1] != '\\'
+ && pwszTmp[-1] != '/')
+ return false;
+ return RTUtf16ICmpAscii(pwszTmp, pszName) == 0;
+}
+
+
+/**
+ * Hooks that intercepts LdrLoadDll calls.
+ *
+ * Two purposes:
+ * -# Enforce our own search path restrictions.
+ * -# Prevalidate DLLs about to be loaded so we don't upset the loader data
+ * by doing it from within the NtCreateSection hook (WinVerifyTrust
+ * seems to be doing harm there on W7/32).
+ *
+ * @returns
+ * @param pwszSearchPath The search path to use.
+ * @param pfFlags Flags on input. DLL characteristics or something
+ * on return?
+ * @param pName The name of the module.
+ * @param phMod Where the handle of the loaded DLL is to be
+ * returned to the caller.
+ */
+__declspec(guard(ignore)) /* don't barf when calling g_pfnLdrLoadDllReal */
+static NTSTATUS NTAPI
+supR3HardenedMonitor_LdrLoadDll(PWSTR pwszSearchPath, PULONG pfFlags, PUNICODE_STRING pName, PHANDLE phMod)
+{
+ DWORD dwSavedLastError = RtlGetLastWin32Error();
+ PUNICODE_STRING const pOrgName = pName;
+ NTSTATUS rcNt;
+
+ /*
+ * Make sure the DLL notification callback is registered. If we could, we
+ * would've done this during early process init, but due to lack of heap
+ * and uninitialized loader lock, it's not possible that early on.
+ *
+ * The callback protects our NtDll hooks from getting unhooked by
+ * "friendly" fire from the AV crowd.
+ */
+ supR3HardenedWinRegisterDllNotificationCallback();
+
+ /*
+ * Process WinVerifyTrust todo before and after.
+ */
+ supR3HardenedWinVerifyCacheProcessWvtTodos();
+
+ /*
+ * Reject things we don't want to deal with.
+ */
+ if (!pName || pName->Length == 0)
+ {
+ supR3HardenedError(VINF_SUCCESS, false, "supR3HardenedMonitor_LdrLoadDll: name is NULL or have a zero length.\n");
+ SUP_DPRINTF(("supR3HardenedMonitor_LdrLoadDll: returns rcNt=%#x (pName=%p)\n", STATUS_INVALID_PARAMETER, pName));
+ RtlRestoreLastWin32Error(dwSavedLastError);
+ return STATUS_INVALID_PARAMETER;
+ }
+ PCWCHAR const pawcOrgName = pName->Buffer;
+ uint32_t const cwcOrgName = pName->Length / sizeof(WCHAR);
+
+ /*SUP_DPRINTF(("supR3HardenedMonitor_LdrLoadDll: pName=%.*ls *pfFlags=%#x pwszSearchPath=%p:%ls\n",
+ (unsigned)pName->Length / sizeof(WCHAR), pName->Buffer, pfFlags ? *pfFlags : UINT32_MAX, pwszSearchPath,
+ !((uintptr_t)pwszSearchPath & 1) && (uintptr_t)pwszSearchPath >= 0x2000U ? pwszSearchPath : L"<flags>"));*/
+
+ /*
+ * Reject long paths that's close to the 260 limit without looking.
+ */
+ if (cwcOrgName > 256)
+ {
+ supR3HardenedError(VINF_SUCCESS, false, "supR3HardenedMonitor_LdrLoadDll: too long name: %#x bytes\n", pName->Length);
+ SUP_DPRINTF(("supR3HardenedMonitor_LdrLoadDll: returns rcNt=%#x\n", STATUS_NAME_TOO_LONG));
+ RtlRestoreLastWin32Error(dwSavedLastError);
+ return STATUS_NAME_TOO_LONG;
+ }
+
+ /*
+ * Reject all UNC-like paths as we cannot trust non-local files at all.
+ * Note! We may have to relax this to deal with long path specifications and NT pass thrus.
+ */
+ if ( cwcOrgName >= 3
+ && RTPATH_IS_SLASH(pawcOrgName[0])
+ && RTPATH_IS_SLASH(pawcOrgName[1])
+ && !RTPATH_IS_SLASH(pawcOrgName[2]))
+ {
+ supR3HardenedError(VINF_SUCCESS, false, "supR3HardenedMonitor_LdrLoadDll: rejecting UNC name '%.*ls'\n", cwcOrgName, pawcOrgName);
+ SUP_DPRINTF(("supR3HardenedMonitor_LdrLoadDll: returns rcNt=%#x\n", STATUS_REDIRECTOR_NOT_STARTED));
+ RtlRestoreLastWin32Error(dwSavedLastError);
+ return STATUS_REDIRECTOR_NOT_STARTED;
+ }
+
+ /*
+ * Reject PGHook.dll as it creates a thread from its DllMain that breaks
+ * our preconditions respawning the 2nd process, resulting in
+ * VERR_SUP_VP_THREAD_NOT_ALONE. The DLL is being loaded by a user APC
+ * scheduled during kernel32.dll load notification from a kernel driver,
+ * so failing the load attempt should not upset anyone.
+ */
+ if (g_enmSupR3HardenedMainState == SUPR3HARDENEDMAINSTATE_WIN_EARLY_STUB_DEVICE_OPENED)
+ {
+ static const struct { const char *psz; size_t cch; } s_aUnwantedEarlyDlls[] =
+ {
+ { RT_STR_TUPLE("PGHook.dll") },
+ };
+ for (unsigned i = 0; i < RT_ELEMENTS(s_aUnwantedEarlyDlls); i++)
+ if (supR3HardenedIsFilenameMatchDll(pName, s_aUnwantedEarlyDlls[i].psz, s_aUnwantedEarlyDlls[i].cch))
+ {
+ SUP_DPRINTF(("supR3HardenedMonitor_LdrLoadDll: Refusing to load '%.*ls' as it is expected to create undesirable threads that will upset our respawn checks (returning STATUS_TOO_MANY_THREADS)\n",
+ pName->Length / sizeof(RTUTF16), pName->Buffer));
+ return STATUS_TOO_MANY_THREADS;
+ }
+ }
+
+ /*
+ * Resolve the path, copying the result into wszPath
+ */
+ NTSTATUS rcNtResolve = STATUS_SUCCESS;
+ bool fSkipValidation = false;
+ bool fCheckIfLoaded = false;
+ WCHAR wszPath[260];
+ static UNICODE_STRING const s_DefaultSuffix = RTNT_CONSTANT_UNISTR(L".dll");
+ UNICODE_STRING UniStrStatic = { 0, (USHORT)sizeof(wszPath) - sizeof(WCHAR), wszPath };
+ UNICODE_STRING UniStrDynamic = { 0, 0, NULL };
+ PUNICODE_STRING pUniStrResult = NULL;
+ UNICODE_STRING ResolvedName;
+
+ /*
+ * Process the name a little, checking if it needs a DLL suffix and is pathless.
+ */
+ uint32_t offLastSlash = UINT32_MAX;
+ uint32_t offLastDot = UINT32_MAX;
+ for (uint32_t i = 0; i < cwcOrgName; i++)
+ switch (pawcOrgName[i])
+ {
+ case '\\':
+ case '/':
+ offLastSlash = i;
+ offLastDot = UINT32_MAX;
+ break;
+ case '.':
+ offLastDot = i;
+ break;
+ }
+ bool const fNeedDllSuffix = offLastDot == UINT32_MAX;
+ //bool const fTrailingDot = offLastDot == cwcOrgName - 1;
+
+ /*
+ * Absolute path?
+ */
+ if ( ( cwcOrgName >= 4
+ && RT_C_IS_ALPHA(pawcOrgName[0])
+ && pawcOrgName[1] == ':'
+ && RTPATH_IS_SLASH(pawcOrgName[2]) )
+ || ( cwcOrgName >= 1
+ && RTPATH_IS_SLASH(pawcOrgName[0]) )
+ )
+ {
+ rcNtResolve = RtlDosApplyFileIsolationRedirection_Ustr(1 /*fFlags*/,
+ pName,
+ (PUNICODE_STRING)&s_DefaultSuffix,
+ &UniStrStatic,
+ &UniStrDynamic,
+ &pUniStrResult,
+ NULL /*pNewFlags*/,
+ NULL /*pcbFilename*/,
+ NULL /*pcbNeeded*/);
+ if (NT_SUCCESS(rcNtResolve))
+ {
+ UINT cwc;
+ rcNt = supR3HardenedCopyRedirectionResult(wszPath, RT_ELEMENTS(wszPath), pUniStrResult, pName, &cwc);
+ RtlFreeUnicodeString(&UniStrDynamic);
+ if (!NT_SUCCESS(rcNt))
+ {
+ SUP_DPRINTF(("supR3HardenedMonitor_LdrLoadDll: returns rcNt=%#x\n", rcNt));
+ RtlRestoreLastWin32Error(dwSavedLastError);
+ return rcNt;
+ }
+
+ ResolvedName.Buffer = wszPath;
+ ResolvedName.Length = (USHORT)(cwc * sizeof(WCHAR));
+ ResolvedName.MaximumLength = ResolvedName.Length + sizeof(WCHAR);
+
+ SUP_DPRINTF(("supR3HardenedMonitor_LdrLoadDll: '%.*ls' -> '%.*ls' [redir]\n",
+ (unsigned)pName->Length / sizeof(WCHAR), pName->Buffer,
+ ResolvedName.Length / sizeof(WCHAR), ResolvedName.Buffer, rcNt));
+ pName = &ResolvedName;
+ }
+ else
+ {
+ /* Copy the path. */
+ memcpy(wszPath, pawcOrgName, cwcOrgName * sizeof(WCHAR));
+ if (!fNeedDllSuffix)
+ wszPath[cwcOrgName] = '\0';
+ else
+ {
+ if (cwcOrgName + 4 >= RT_ELEMENTS(wszPath))
+ {
+ supR3HardenedError(VINF_SUCCESS, false,
+ "supR3HardenedMonitor_LdrLoadDll: Name too long (abs): %.*ls\n", cwcOrgName, pawcOrgName);
+ SUP_DPRINTF(("supR3HardenedMonitor_LdrLoadDll: returns rcNt=%#x\n", STATUS_NAME_TOO_LONG));
+ RtlRestoreLastWin32Error(dwSavedLastError);
+ return STATUS_NAME_TOO_LONG;
+ }
+ memcpy(&wszPath[cwcOrgName], L".dll", 5 * sizeof(WCHAR));
+ }
+ }
+ }
+ /*
+ * Not an absolute path. Check if it's one of those special API set DLLs
+ * or something we're known to use but should be taken from WinSxS.
+ */
+ else if ( supR3HardenedHasDashButNoPath(pName)
+ && supR3HardenedIsApiSetDll(pName))
+ {
+ memcpy(wszPath, pName->Buffer, pName->Length);
+ wszPath[pName->Length / sizeof(WCHAR)] = '\0';
+ fSkipValidation = true;
+ }
+ /*
+ * Not an absolute path or special API set. There are two alternatives
+ * now, either there is no path at all or there is a relative path. We
+ * will resolve it to an absolute path in either case, failing the call
+ * if we can't.
+ */
+ else
+ {
+ /*
+ * Reject relative paths for now as they might be breakout attempts.
+ */
+ if (offLastSlash != UINT32_MAX)
+ {
+ supR3HardenedError(VINF_SUCCESS, false,
+ "supR3HardenedMonitor_LdrLoadDll: relative name not permitted: %.*ls\n",
+ cwcOrgName, pawcOrgName);
+ SUP_DPRINTF(("supR3HardenedMonitor_LdrLoadDll: returns rcNt=%#x\n", STATUS_OBJECT_NAME_INVALID));
+ RtlRestoreLastWin32Error(dwSavedLastError);
+ return STATUS_OBJECT_NAME_INVALID;
+ }
+
+ /*
+ * Perform dll redirection to WinSxS such. We using an undocumented
+ * API here, which as always is a bit risky... ASSUMES that the API
+ * returns a full DOS path.
+ */
+ UINT cwc;
+ rcNtResolve = RtlDosApplyFileIsolationRedirection_Ustr(1 /*fFlags*/,
+ pName,
+ (PUNICODE_STRING)&s_DefaultSuffix,
+ &UniStrStatic,
+ &UniStrDynamic,
+ &pUniStrResult,
+ NULL /*pNewFlags*/,
+ NULL /*pcbFilename*/,
+ NULL /*pcbNeeded*/);
+ if (NT_SUCCESS(rcNtResolve))
+ {
+ rcNt = supR3HardenedCopyRedirectionResult(wszPath, RT_ELEMENTS(wszPath), pUniStrResult, pName, &cwc);
+ RtlFreeUnicodeString(&UniStrDynamic);
+ if (!NT_SUCCESS(rcNt))
+ {
+ SUP_DPRINTF(("supR3HardenedMonitor_LdrLoadDll: returns rcNt=%#x\n", rcNt));
+ RtlRestoreLastWin32Error(dwSavedLastError);
+ return rcNt;
+ }
+ }
+ else
+ {
+ /*
+ * Search for the DLL. Only System32 is allowed as the target of
+ * a search on the API level, all VBox calls will have full paths.
+ * If the DLL is not in System32, we will resort to check if it's
+ * refering to an already loaded DLL (fCheckIfLoaded).
+ */
+ AssertCompile(sizeof(g_System32WinPath.awcBuffer) <= sizeof(wszPath));
+ cwc = g_System32WinPath.UniStr.Length / sizeof(RTUTF16); Assert(cwc > 2);
+ if (cwc + 1 + cwcOrgName + fNeedDllSuffix * 4 >= RT_ELEMENTS(wszPath))
+ {
+ supR3HardenedError(VINF_SUCCESS, false,
+ "supR3HardenedMonitor_LdrLoadDll: Name too long (system32): %.*ls\n", cwcOrgName, pawcOrgName);
+ SUP_DPRINTF(("supR3HardenedMonitor_LdrLoadDll: returns rcNt=%#x\n", STATUS_NAME_TOO_LONG));
+ RtlRestoreLastWin32Error(dwSavedLastError);
+ return STATUS_NAME_TOO_LONG;
+ }
+ memcpy(wszPath, g_System32WinPath.UniStr.Buffer, cwc * sizeof(RTUTF16));
+ wszPath[cwc++] = '\\';
+ memcpy(&wszPath[cwc], pawcOrgName, cwcOrgName * sizeof(WCHAR));
+ cwc += cwcOrgName;
+ if (!fNeedDllSuffix)
+ wszPath[cwc] = '\0';
+ else
+ {
+ memcpy(&wszPath[cwc], L".dll", 5 * sizeof(WCHAR));
+ cwc += 4;
+ }
+ fCheckIfLoaded = true;
+ }
+
+ ResolvedName.Buffer = wszPath;
+ ResolvedName.Length = (USHORT)(cwc * sizeof(WCHAR));
+ ResolvedName.MaximumLength = ResolvedName.Length + sizeof(WCHAR);
+ pName = &ResolvedName;
+ }
+
+#ifndef IN_SUP_R3_STATIC
+ /*
+ * Reject blacklisted DLLs based on input name.
+ */
+ for (unsigned i = 0; g_aSupNtViBlacklistedDlls[i].psz != NULL; i++)
+ if (supR3HardenedIsFilenameMatchDll(pName, g_aSupNtViBlacklistedDlls[i].psz, g_aSupNtViBlacklistedDlls[i].cch))
+ {
+ SUP_DPRINTF(("supR3HardenedMonitor_LdrLoadDll: Refusing to load blacklisted DLL: '%.*ls'\n",
+ pName->Length / sizeof(RTUTF16), pName->Buffer));
+ RtlRestoreLastWin32Error(dwSavedLastError);
+ return STATUS_TOO_MANY_THREADS;
+ }
+#endif
+
+ bool fQuiet = false;
+ if (!fSkipValidation)
+ {
+ /*
+ * Try open the file. If this fails, never mind, just pass it on to
+ * the real API as we've replaced any searchable name with a full name
+ * and the real API can come up with a fitting status code for it.
+ */
+ HANDLE hRootDir;
+ UNICODE_STRING NtPathUniStr;
+ int rc = RTNtPathFromWinUtf16Ex(&NtPathUniStr, &hRootDir, wszPath, RTSTR_MAX);
+ if (RT_FAILURE(rc))
+ {
+ supR3HardenedError(rc, false,
+ "supR3HardenedMonitor_LdrLoadDll: RTNtPathFromWinUtf16Ex failed on '%ls': %Rrc\n", wszPath, rc);
+ SUP_DPRINTF(("supR3HardenedMonitor_LdrLoadDll: returns rcNt=%#x\n", STATUS_OBJECT_NAME_INVALID));
+ RtlRestoreLastWin32Error(dwSavedLastError);
+ return STATUS_OBJECT_NAME_INVALID;
+ }
+
+ HANDLE hFile = RTNT_INVALID_HANDLE_VALUE;
+ IO_STATUS_BLOCK Ios = RTNT_IO_STATUS_BLOCK_INITIALIZER;
+ OBJECT_ATTRIBUTES ObjAttr;
+ InitializeObjectAttributes(&ObjAttr, &NtPathUniStr, OBJ_CASE_INSENSITIVE, hRootDir, NULL /*pSecDesc*/);
+
+ rcNt = NtCreateFile(&hFile,
+ FILE_READ_DATA | READ_CONTROL | SYNCHRONIZE,
+ &ObjAttr,
+ &Ios,
+ NULL /* Allocation Size*/,
+ FILE_ATTRIBUTE_NORMAL,
+ FILE_SHARE_READ,
+ FILE_OPEN,
+ FILE_NON_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT,
+ NULL /*EaBuffer*/,
+ 0 /*EaLength*/);
+ if (NT_SUCCESS(rcNt))
+ rcNt = Ios.Status;
+ if (NT_SUCCESS(rcNt))
+ {
+ ULONG fAccess = 0;
+ ULONG fProtect = 0;
+ bool fCallRealApi = false;
+ rcNt = supR3HardenedScreenImage(hFile, true /*fImage*/, RT_VALID_PTR(pfFlags) && (*pfFlags & 0x2) /*fIgnoreArch*/,
+ &fAccess, &fProtect, &fCallRealApi,
+ "LdrLoadDll", false /*fAvoidWinVerifyTrust*/, &fQuiet);
+ NtClose(hFile);
+ if (!NT_SUCCESS(rcNt))
+ {
+ if (!fQuiet)
+ {
+ if (pOrgName != pName)
+ supR3HardenedError(VINF_SUCCESS, false, "supR3HardenedMonitor_LdrLoadDll: rejecting '%ls': rcNt=%#x\n",
+ wszPath, rcNt);
+ else
+ supR3HardenedError(VINF_SUCCESS, false, "supR3HardenedMonitor_LdrLoadDll: rejecting '%ls' (%.*ls): rcNt=%#x\n",
+ wszPath, pOrgName->Length / sizeof(WCHAR), pOrgName->Buffer, rcNt);
+ SUP_DPRINTF(("supR3HardenedMonitor_LdrLoadDll: returns rcNt=%#x '%ls'\n", rcNt, wszPath));
+ }
+ RtlRestoreLastWin32Error(dwSavedLastError);
+ return rcNt;
+ }
+
+ supR3HardenedWinVerifyCacheProcessImportTodos();
+ }
+ else
+ {
+ DWORD dwErr = RtlGetLastWin32Error();
+
+ /*
+ * Deal with special case where the caller (first case was MS LifeCam)
+ * is using LoadLibrary instead of GetModuleHandle to find a loaded DLL.
+ */
+ NTSTATUS rcNtGetDll = STATUS_SUCCESS;
+ if ( fCheckIfLoaded
+ && ( rcNt == STATUS_OBJECT_NAME_NOT_FOUND
+ || rcNt == STATUS_OBJECT_PATH_NOT_FOUND))
+ {
+ rcNtGetDll = LdrGetDllHandle(NULL /*DllPath*/, NULL /*pfFlags*/, pOrgName, phMod);
+ if (NT_SUCCESS(rcNtGetDll))
+ {
+ RTNtPathFree(&NtPathUniStr, &hRootDir);
+ RtlRestoreLastWin32Error(dwSavedLastError);
+ return rcNtGetDll;
+ }
+ }
+
+ SUP_DPRINTF(("supR3HardenedMonitor_LdrLoadDll: error opening '%ls': %u (NtPath=%.*ls; Input=%.*ls; rcNtGetDll=%#x\n",
+ wszPath, dwErr, NtPathUniStr.Length / sizeof(RTUTF16), NtPathUniStr.Buffer,
+ pOrgName->Length / sizeof(WCHAR), pOrgName->Buffer, rcNtGetDll));
+
+ RTNtPathFree(&NtPathUniStr, &hRootDir);
+ RtlRestoreLastWin32Error(dwSavedLastError);
+ SUP_DPRINTF(("supR3HardenedMonitor_LdrLoadDll: returns rcNt=%#x '%ls'\n", rcNt, wszPath));
+ return rcNt;
+ }
+ RTNtPathFree(&NtPathUniStr, &hRootDir);
+ }
+
+ /*
+ * Screened successfully enough. Call the real thing.
+ */
+ if (!fQuiet)
+ {
+ if (pOrgName != pName)
+ SUP_DPRINTF(("supR3HardenedMonitor_LdrLoadDll: pName=%.*ls (Input=%.*ls, rcNtResolve=%#x) *pfFlags=%#x pwszSearchPath=%p:%ls [calling]\n",
+ (unsigned)pName->Length / sizeof(WCHAR), pName->Buffer,
+ (unsigned)pOrgName->Length / sizeof(WCHAR), pOrgName->Buffer, rcNtResolve,
+ pfFlags ? *pfFlags : UINT32_MAX, pwszSearchPath,
+ !((uintptr_t)pwszSearchPath & 1) && (uintptr_t)pwszSearchPath >= 0x2000U ? pwszSearchPath : L"<flags>"));
+ else
+ SUP_DPRINTF(("supR3HardenedMonitor_LdrLoadDll: pName=%.*ls (rcNtResolve=%#x) *pfFlags=%#x pwszSearchPath=%p:%ls [calling]\n",
+ (unsigned)pName->Length / sizeof(WCHAR), pName->Buffer, rcNtResolve,
+ pfFlags ? *pfFlags : UINT32_MAX, pwszSearchPath,
+ !((uintptr_t)pwszSearchPath & 1) && (uintptr_t)pwszSearchPath >= 0x2000U ? pwszSearchPath : L"<flags>"));
+ }
+
+ RtlRestoreLastWin32Error(dwSavedLastError);
+ rcNt = g_pfnLdrLoadDllReal(pwszSearchPath, pfFlags, pName, phMod);
+
+ /*
+ * Log the result and process pending WinVerifyTrust work if we can.
+ */
+ dwSavedLastError = RtlGetLastWin32Error();
+
+ if (NT_SUCCESS(rcNt) && phMod)
+ SUP_DPRINTF(("supR3HardenedMonitor_LdrLoadDll: returns rcNt=%#x hMod=%p '%ls'\n", rcNt, *phMod, wszPath));
+ else if (!NT_SUCCESS(rcNt) || !fQuiet)
+ SUP_DPRINTF(("supR3HardenedMonitor_LdrLoadDll: returns rcNt=%#x '%ls'\n", rcNt, wszPath));
+
+ supR3HardenedWinVerifyCacheProcessWvtTodos();
+
+ RtlRestoreLastWin32Error(dwSavedLastError);
+
+ return rcNt;
+}
+
+
+/**
+ * DLL load and unload notification callback.
+ *
+ * This is a safety against our LdrLoadDll hook being replaced by protection
+ * software. Though, we prefer the LdrLoadDll hook to this one as it allows us
+ * to call WinVerifyTrust more freely.
+ *
+ * @param ulReason The reason we're called, see
+ * LDR_DLL_NOTIFICATION_REASON_XXX.
+ * @param pData Reason specific data. (Format is currently the same for
+ * both load and unload.)
+ * @param pvUser User parameter (ignored).
+ *
+ * @remarks Vista and later.
+ * @remarks The loader lock is held when we're called, at least on Windows 7.
+ */
+static VOID CALLBACK
+supR3HardenedDllNotificationCallback(ULONG ulReason, PCLDR_DLL_NOTIFICATION_DATA pData, PVOID pvUser) RT_NOTHROW_DEF
+{
+ NOREF(pvUser);
+
+ /*
+ * Screen the image on load. We will normally get a verification cache
+ * hit here because of the LdrLoadDll and NtCreateSection hooks, so it
+ * should be relatively cheap to recheck. In case our NtDll patches
+ * got re
+ *
+ * This ASSUMES that we get informed after the fact as indicated by the
+ * available documentation.
+ */
+ if (ulReason == LDR_DLL_NOTIFICATION_REASON_LOADED)
+ {
+ SUP_DPRINTF(("supR3HardenedDllNotificationCallback: load %p LB %#010x %.*ls [fFlags=%#x]\n",
+ pData->Loaded.DllBase, pData->Loaded.SizeOfImage,
+ pData->Loaded.FullDllName->Length / sizeof(WCHAR), pData->Loaded.FullDllName->Buffer,
+ pData->Loaded.Flags));
+
+ /* Convert the windows path to an NT path and open it. */
+ HANDLE hRootDir;
+ UNICODE_STRING NtPathUniStr;
+ int rc = RTNtPathFromWinUtf16Ex(&NtPathUniStr, &hRootDir, pData->Loaded.FullDllName->Buffer,
+ pData->Loaded.FullDllName->Length / sizeof(WCHAR));
+ if (RT_FAILURE(rc))
+ {
+ supR3HardenedFatal("supR3HardenedDllNotificationCallback: RTNtPathFromWinUtf16Ex failed on '%.*ls': %Rrc\n",
+ pData->Loaded.FullDllName->Length / sizeof(WCHAR), pData->Loaded.FullDllName->Buffer, rc);
+ return;
+ }
+
+ HANDLE hFile = RTNT_INVALID_HANDLE_VALUE;
+ IO_STATUS_BLOCK Ios = RTNT_IO_STATUS_BLOCK_INITIALIZER;
+ OBJECT_ATTRIBUTES ObjAttr;
+ InitializeObjectAttributes(&ObjAttr, &NtPathUniStr, OBJ_CASE_INSENSITIVE, hRootDir, NULL /*pSecDesc*/);
+
+ NTSTATUS rcNt = NtCreateFile(&hFile,
+ FILE_READ_DATA | READ_CONTROL | SYNCHRONIZE,
+ &ObjAttr,
+ &Ios,
+ NULL /* Allocation Size*/,
+ FILE_ATTRIBUTE_NORMAL,
+ FILE_SHARE_READ,
+ FILE_OPEN,
+ FILE_NON_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT,
+ NULL /*EaBuffer*/,
+ 0 /*EaLength*/);
+ if (NT_SUCCESS(rcNt))
+ rcNt = Ios.Status;
+ if (!NT_SUCCESS(rcNt))
+ {
+ supR3HardenedFatal("supR3HardenedDllNotificationCallback: NtCreateFile failed on '%.*ls' / '%.*ls': %#x\n",
+ pData->Loaded.FullDllName->Length / sizeof(WCHAR), pData->Loaded.FullDllName->Buffer,
+ NtPathUniStr.Length / sizeof(WCHAR), NtPathUniStr.Buffer, rcNt);
+ /* not reached */
+ }
+
+ /* Do the screening. */
+ ULONG fAccess = 0;
+ ULONG fProtect = 0;
+ bool fCallRealApi = false;
+ bool fQuietFailure = false;
+ rcNt = supR3HardenedScreenImage(hFile, true /*fImage*/, true /*fIgnoreArch*/, &fAccess, &fProtect, &fCallRealApi,
+ "LdrLoadDll", true /*fAvoidWinVerifyTrust*/, &fQuietFailure);
+ NtClose(hFile);
+ if (!NT_SUCCESS(rcNt))
+ {
+ supR3HardenedFatal("supR3HardenedDllNotificationCallback: supR3HardenedScreenImage failed on '%.*ls' / '%.*ls': %#x\n",
+ pData->Loaded.FullDllName->Length / sizeof(WCHAR), pData->Loaded.FullDllName->Buffer,
+ NtPathUniStr.Length / sizeof(WCHAR), NtPathUniStr.Buffer, rcNt);
+ /* not reached */
+ }
+ RTNtPathFree(&NtPathUniStr, &hRootDir);
+ }
+ /*
+ * Log the unload call.
+ */
+ else if (ulReason == LDR_DLL_NOTIFICATION_REASON_UNLOADED)
+ {
+ SUP_DPRINTF(("supR3HardenedDllNotificationCallback: Unload %p LB %#010x %.*ls [flags=%#x]\n",
+ pData->Unloaded.DllBase, pData->Unloaded.SizeOfImage,
+ pData->Unloaded.FullDllName->Length / sizeof(WCHAR), pData->Unloaded.FullDllName->Buffer,
+ pData->Unloaded.Flags));
+ }
+ /*
+ * Just log things we don't know and then return without caching anything.
+ */
+ else
+ {
+ static uint32_t s_cLogEntries = 0;
+ if (s_cLogEntries++ < 32)
+ SUP_DPRINTF(("supR3HardenedDllNotificationCallback: ulReason=%u pData=%p\n", ulReason, pData));
+ return;
+ }
+
+ /*
+ * Use this opportunity to make sure our NtDll patches are still in place,
+ * since they may be replaced by indecent protection software solutions.
+ */
+ supR3HardenedWinReInstallHooks(false /*fFirstCall */);
+}
+
+
+/**
+ * Registers the DLL notification callback if it hasn't already been registered.
+ */
+static void supR3HardenedWinRegisterDllNotificationCallback(void)
+{
+ /*
+ * The notification API was added in Vista, so it's an optional (weak) import.
+ */
+ if ( LdrRegisterDllNotification != NULL
+ && g_cDllNotificationRegistered <= 0
+ && g_cDllNotificationRegistered > -32)
+ {
+ NTSTATUS rcNt = LdrRegisterDllNotification(0, supR3HardenedDllNotificationCallback, NULL, &g_pvDllNotificationCookie);
+ if (NT_SUCCESS(rcNt))
+ {
+ SUP_DPRINTF(("Registered Dll notification callback with NTDLL.\n"));
+ g_cDllNotificationRegistered = 1;
+ }
+ else
+ {
+ supR3HardenedError(rcNt, false /*fFatal*/, "LdrRegisterDllNotification failed: %#x\n", rcNt);
+ g_cDllNotificationRegistered--;
+ }
+ }
+}
+
+
+/**
+ * Dummy replacement routine we use for passifying unwanted user APC
+ * callbacks during early process initialization.
+ *
+ * @sa supR3HardenedMonitor_KiUserApcDispatcher_C
+ */
+static VOID NTAPI supR3HardenedWinDummyApcRoutine(PVOID pvArg1, PVOID pvArg2, PVOID pvArg3)
+{
+ SUP_DPRINTF(("supR3HardenedWinDummyApcRoutine: pvArg1=%p pvArg2=%p pvArg3=%p\n", pvArg1, pvArg2, pvArg3));
+ RT_NOREF(pvArg1, pvArg2, pvArg3);
+}
+
+
+/**
+ * This is called when ntdll!KiUserApcDispatcher is invoked (via
+ * supR3HardenedMonitor_KiUserApcDispatcher).
+ *
+ * The parent process hooks KiUserApcDispatcher before the guest starts
+ * executing. There should only be one APC request dispatched while the process
+ * is being initialized, and that's the one calling ntdll!LdrInitializeThunk.
+ *
+ * @returns Where to go to run the original code.
+ * @param pvApcArgs The APC dispatcher arguments.
+ */
+DECLASM(uintptr_t) supR3HardenedMonitor_KiUserApcDispatcher_C(void *pvApcArgs)
+{
+#ifdef RT_ARCH_AMD64
+ PCONTEXT pCtx = (PCONTEXT)pvApcArgs;
+ uintptr_t *ppfnRoutine = (uintptr_t *)&pCtx->P4Home;
+#else
+ struct X86APCCTX
+ {
+ uintptr_t pfnRoutine;
+ uintptr_t pvCtx;
+ uintptr_t pvUser1;
+ uintptr_t pvUser2;
+ CONTEXT Ctx;
+ } *pCtx = (struct X86APCCTX *)pvApcArgs;
+ uintptr_t *ppfnRoutine = &pCtx->pfnRoutine;
+#endif
+ uintptr_t pfnRoutine = *ppfnRoutine;
+
+ if (g_enmSupR3HardenedMainState < SUPR3HARDENEDMAINSTATE_HARDENED_MAIN_CALLED)
+ {
+ if (pfnRoutine == g_pfnLdrInitializeThunk) /* Note! we could use this to detect thread creation too. */
+ SUP_DPRINTF(("supR3HardenedMonitor_KiUserApcDispatcher_C: pfnRoutine=%p enmState=%d - okay\n",
+ pfnRoutine, g_enmSupR3HardenedMainState));
+ else
+ {
+ *ppfnRoutine = (uintptr_t)supR3HardenedWinDummyApcRoutine;
+ SUP_DPRINTF(("supR3HardenedMonitor_KiUserApcDispatcher_C: pfnRoutine=%p enmState=%d -> supR3HardenedWinDummyApcRoutine\n",
+ pfnRoutine, g_enmSupR3HardenedMainState));
+ }
+ }
+ return (uintptr_t)g_pfnKiUserApcDispatcherReal;
+}
+
+
+/**
+ * SUP_DPRINTF on pCtx, with lead-in text.
+ */
+static void supR3HardNtDprintCtx(PCONTEXT pCtx, const char *pszLeadIn)
+{
+#ifdef RT_ARCH_AMD64
+ SUP_DPRINTF(("%s\n"
+ " rax=%016RX64 rbx=%016RX64 rcx=%016RX64 rdx=%016RX64\n"
+ " rsi=%016RX64 rdi=%016RX64 r8 =%016RX64 r9 =%016RX64\n"
+ " r10=%016RX64 r11=%016RX64 r12=%016RX64 r13=%016RX64\n"
+ " r14=%016RX64 r15=%016RX64 P1=%016RX64 P2=%016RX64\n"
+ " rip=%016RX64 rsp=%016RX64 rbp=%016RX64 ctxflags=%08x\n"
+ " cs=%04x ss=%04x ds=%04x es=%04x fs=%04x gs=%04x eflags=%08x mxcrx=%08x\n"
+ " P3=%016RX64 P4=%016RX64 P5=%016RX64 P6=%016RX64\n"
+ " dr0=%016RX64 dr1=%016RX64 dr2=%016RX64 dr3=%016RX64\n"
+ " dr6=%016RX64 dr7=%016RX64 vcr=%016RX64 dcr=%016RX64\n"
+ " lbt=%016RX64 lbf=%016RX64 lxt=%016RX64 lxf=%016RX64\n"
+ ,
+ pszLeadIn,
+ pCtx->Rax, pCtx->Rbx, pCtx->Rcx, pCtx->Rdx,
+ pCtx->Rsi, pCtx->Rdi, pCtx->R8, pCtx->R9,
+ pCtx->R10, pCtx->R11, pCtx->R12, pCtx->R13,
+ pCtx->R14, pCtx->R15, pCtx->P1Home, pCtx->P2Home,
+ pCtx->Rip, pCtx->Rsp, pCtx->Rbp, pCtx->ContextFlags,
+ pCtx->SegCs, pCtx->SegSs, pCtx->SegDs, pCtx->SegEs, pCtx->SegFs, pCtx->SegGs, pCtx->EFlags, pCtx->MxCsr,
+ pCtx->P3Home, pCtx->P4Home, pCtx->P5Home, pCtx->P6Home,
+ pCtx->Dr0, pCtx->Dr1, pCtx->Dr2, pCtx->Dr3,
+ pCtx->Dr6, pCtx->Dr7, pCtx->VectorControl, pCtx->DebugControl,
+ pCtx->LastBranchToRip, pCtx->LastBranchFromRip, pCtx->LastExceptionToRip, pCtx->LastExceptionFromRip ));
+#elif defined(RT_ARCH_X86)
+ SUP_DPRINTF(("%s\n"
+ " eax=%08RX32 ebx=%08RX32 ecx=%08RX32 edx=%08RX32 esi=%08rx64 edi=%08RX32\n"
+ " eip=%08RX32 esp=%08RX32 ebp=%08RX32 eflags=%08RX32\n"
+ " cs=%04RX16 ds=%04RX16 es=%04RX16 fs=%04RX16 gs=%04RX16\n"
+ " dr0=%08RX32 dr1=%08RX32 dr2=%08RX32 dr3=%08RX32 dr6=%08RX32 dr7=%08RX32\n",
+ pszLeadIn,
+ pCtx->Eax, pCtx->Ebx, pCtx->Ecx, pCtx->Edx, pCtx->Esi, pCtx->Edi,
+ pCtx->Eip, pCtx->Esp, pCtx->Ebp, pCtx->EFlags,
+ pCtx->SegCs, pCtx->SegDs, pCtx->SegEs, pCtx->SegFs, pCtx->SegGs,
+ pCtx->Dr0, pCtx->Dr1, pCtx->Dr2, pCtx->Dr3, pCtx->Dr6, pCtx->Dr7));
+#else
+# error "Unsupported arch."
+#endif
+ RT_NOREF(pCtx, pszLeadIn);
+}
+
+
+#ifndef VBOX_WITHOUT_HARDENDED_XCPT_LOGGING
+/**
+ * This is called when ntdll!KiUserExceptionDispatcher is invoked (via
+ * supR3HardenedMonitor_KiUserExceptionDispatcher).
+ *
+ * For 64-bit processes there is a return and two parameters on the stack.
+ *
+ * @returns Where to go to run the original code.
+ * @param pXcptRec The exception record.
+ * @param pCtx The exception context.
+ */
+DECLASM(uintptr_t) supR3HardenedMonitor_KiUserExceptionDispatcher_C(PEXCEPTION_RECORD pXcptRec, PCONTEXT pCtx)
+{
+ /*
+ * Ignore the guard page violation.
+ */
+ if (pXcptRec->ExceptionCode == STATUS_GUARD_PAGE_VIOLATION)
+ return (uintptr_t)g_pfnKiUserExceptionDispatcherReal;
+
+ /*
+ * Log the exception and context.
+ */
+ char szLeadIn[384];
+ if (pXcptRec->NumberParameters == 0)
+ RTStrPrintf(szLeadIn, sizeof(szLeadIn), "KiUserExceptionDispatcher: %#x @ %p (flags=%#x)",
+ pXcptRec->ExceptionCode, pXcptRec->ExceptionAddress, pXcptRec->ExceptionFlags);
+ else if (pXcptRec->NumberParameters == 1)
+ RTStrPrintf(szLeadIn, sizeof(szLeadIn), "KiUserExceptionDispatcher: %#x (%p) @ %p (flags=%#x)",
+ pXcptRec->ExceptionCode, pXcptRec->ExceptionInformation[0],
+ pXcptRec->ExceptionAddress, pXcptRec->ExceptionFlags);
+ else if (pXcptRec->NumberParameters == 2)
+ RTStrPrintf(szLeadIn, sizeof(szLeadIn), "KiUserExceptionDispatcher: %#x (%p, %p) @ %p (flags=%#x)",
+ pXcptRec->ExceptionCode, pXcptRec->ExceptionInformation[0], pXcptRec->ExceptionInformation[1],
+ pXcptRec->ExceptionAddress, pXcptRec->ExceptionFlags);
+ else if (pXcptRec->NumberParameters == 3)
+ RTStrPrintf(szLeadIn, sizeof(szLeadIn), "KiUserExceptionDispatcher: %#x (%p, %p, %p) @ %p (flags=%#x)",
+ pXcptRec->ExceptionCode, pXcptRec->ExceptionInformation[0], pXcptRec->ExceptionInformation[1],
+ pXcptRec->ExceptionInformation[2], pXcptRec->ExceptionAddress, pXcptRec->ExceptionFlags);
+ else
+ RTStrPrintf(szLeadIn, sizeof(szLeadIn), "KiUserExceptionDispatcher: %#x (#%u: %p, %p, %p, %p, %p, %p, %p, %p, ...) @ %p (flags=%#x)",
+ pXcptRec->ExceptionCode, pXcptRec->NumberParameters,
+ pXcptRec->ExceptionInformation[0], pXcptRec->ExceptionInformation[1],
+ pXcptRec->ExceptionInformation[2], pXcptRec->ExceptionInformation[3],
+ pXcptRec->ExceptionInformation[4], pXcptRec->ExceptionInformation[5],
+ pXcptRec->ExceptionInformation[6], pXcptRec->ExceptionInformation[7],
+ pXcptRec->ExceptionAddress, pXcptRec->ExceptionFlags);
+ supR3HardNtDprintCtx(pCtx, szLeadIn);
+
+ return (uintptr_t)g_pfnKiUserExceptionDispatcherReal;
+}
+#endif /* !VBOX_WITHOUT_HARDENDED_XCPT_LOGGING */
+
+
+static void supR3HardenedWinHookFailed(const char *pszWhich, uint8_t const *pbPrologue)
+{
+ supR3HardenedFatalMsg("supR3HardenedWinInstallHooks", kSupInitOp_Misc, VERR_NO_MEMORY,
+ "Failed to install %s monitor: %x %x %x %x %x %x %x %x %x %x %x %x %x %x %x %x\n "
+#ifdef RT_ARCH_X86
+ "(It is also possible you are running 32-bit VirtualBox under 64-bit windows.)\n"
+#endif
+ ,
+ pszWhich,
+ pbPrologue[0], pbPrologue[1], pbPrologue[2], pbPrologue[3],
+ pbPrologue[4], pbPrologue[5], pbPrologue[6], pbPrologue[7],
+ pbPrologue[8], pbPrologue[9], pbPrologue[10], pbPrologue[11],
+ pbPrologue[12], pbPrologue[13], pbPrologue[14], pbPrologue[15]);
+}
+
+
+/**
+ * IPRT thread that waits for the parent process to terminate and reacts by
+ * exiting the current process.
+ *
+ * @returns VINF_SUCCESS
+ * @param hSelf The current thread. Ignored.
+ * @param pvUser The handle of the parent process.
+ */
+static DECLCALLBACK(int) supR3HardenedWinParentWatcherThread(RTTHREAD hSelf, void *pvUser)
+{
+ HANDLE hProcWait = (HANDLE)pvUser;
+ NOREF(hSelf);
+
+ /*
+ * Wait for the parent to terminate.
+ */
+ NTSTATUS rcNt;
+ for (;;)
+ {
+ rcNt = NtWaitForSingleObject(hProcWait, TRUE /*Alertable*/, NULL /*pTimeout*/);
+ if ( rcNt == STATUS_WAIT_0
+ || rcNt == STATUS_ABANDONED_WAIT_0)
+ break;
+ if ( rcNt != STATUS_TIMEOUT
+ && rcNt != STATUS_USER_APC
+ && rcNt != STATUS_ALERTED)
+ supR3HardenedFatal("NtWaitForSingleObject returned %#x\n", rcNt);
+ }
+
+ /*
+ * Proxy the termination code of the child, if it exited already.
+ */
+ PROCESS_BASIC_INFORMATION BasicInfo;
+ NTSTATUS rcNt2 = NtQueryInformationProcess(hProcWait, ProcessBasicInformation, &BasicInfo, sizeof(BasicInfo), NULL);
+ if ( !NT_SUCCESS(rcNt2)
+ || BasicInfo.ExitStatus == STATUS_PENDING)
+ BasicInfo.ExitStatus = RTEXITCODE_FAILURE;
+
+ NtClose(hProcWait);
+ SUP_DPRINTF(("supR3HardenedWinParentWatcherThread: Quitting: ExitCode=%#x rcNt=%#x\n", BasicInfo.ExitStatus, rcNt));
+ suplibHardenedExit((RTEXITCODE)BasicInfo.ExitStatus);
+ /* not reached */
+}
+
+
+/**
+ * Creates the parent watcher thread that will make sure this process exits when
+ * the parent does.
+ *
+ * This is a necessary evil to make VBoxNetDhcp and VBoxNetNat termination from
+ * Main work without too much new magic. It also makes Ctrl-C or similar work
+ * in on the hardened processes in the windows console.
+ *
+ * @param hVBoxRT The VBoxRT.dll handle. We use RTThreadCreate to
+ * spawn the thread to avoid duplicating thread
+ * creation and thread naming code from IPRT.
+ */
+DECLHIDDEN(void) supR3HardenedWinCreateParentWatcherThread(HMODULE hVBoxRT)
+{
+ /*
+ * Resolve runtime methods that we need.
+ */
+ PFNRTTHREADCREATE pfnRTThreadCreate = (PFNRTTHREADCREATE)GetProcAddress(hVBoxRT, "RTThreadCreate");
+ SUPR3HARDENED_ASSERT(pfnRTThreadCreate != NULL);
+
+ /*
+ * Find the parent process ID.
+ */
+ PROCESS_BASIC_INFORMATION BasicInfo;
+ NTSTATUS rcNt = NtQueryInformationProcess(NtCurrentProcess(), ProcessBasicInformation, &BasicInfo, sizeof(BasicInfo), NULL);
+ if (!NT_SUCCESS(rcNt))
+ supR3HardenedFatal("supR3HardenedWinCreateParentWatcherThread: NtQueryInformationProcess failed: %#x\n", rcNt);
+
+ /*
+ * Open the parent process for waiting and exitcode query.
+ */
+ OBJECT_ATTRIBUTES ObjAttr;
+ InitializeObjectAttributes(&ObjAttr, NULL, 0, NULL /*hRootDir*/, NULL /*pSecDesc*/);
+
+ CLIENT_ID ClientId;
+ ClientId.UniqueProcess = (HANDLE)BasicInfo.InheritedFromUniqueProcessId;
+ ClientId.UniqueThread = NULL;
+
+ HANDLE hParent;
+ rcNt = NtOpenProcess(&hParent, SYNCHRONIZE | PROCESS_QUERY_INFORMATION, &ObjAttr, &ClientId);
+ if (!NT_SUCCESS(rcNt))
+ supR3HardenedFatalMsg("supR3HardenedWinCreateParentWatcherThread", kSupInitOp_Misc, VERR_GENERAL_FAILURE,
+ "NtOpenProcess(%p.0) failed: %#x\n", ClientId.UniqueProcess, rcNt);
+
+ /*
+ * Create the thread that should do the waiting.
+ */
+ int rc = pfnRTThreadCreate(NULL, supR3HardenedWinParentWatcherThread, hParent, _64K /* stack */,
+ RTTHREADTYPE_DEFAULT, 0 /*fFlags*/, "ParentWatcher");
+ if (RT_FAILURE(rc))
+ supR3HardenedFatal("supR3HardenedWinCreateParentWatcherThread: RTThreadCreate failed: %Rrc\n", rc);
+}
+
+
+/**
+ * Checks if the calling thread is the only one in the process.
+ *
+ * @returns true if we're positive we're alone, false if not.
+ */
+static bool supR3HardenedWinAmIAlone(void) RT_NOTHROW_DEF
+{
+ ULONG fAmIAlone = 0;
+ ULONG cbIgn = 0;
+ NTSTATUS rcNt = NtQueryInformationThread(NtCurrentThread(), ThreadAmILastThread, &fAmIAlone, sizeof(fAmIAlone), &cbIgn);
+ Assert(NT_SUCCESS(rcNt));
+ return NT_SUCCESS(rcNt) && fAmIAlone != 0;
+}
+
+
+/**
+ * Simplify NtProtectVirtualMemory interface.
+ *
+ * Modifies protection for the current process. Caller must know the current
+ * protection as it's not returned.
+ *
+ * @returns NT status code.
+ * @param pvMem The memory to change protection for.
+ * @param cbMem The amount of memory to change.
+ * @param fNewProt The new protection.
+ */
+static NTSTATUS supR3HardenedWinProtectMemory(PVOID pvMem, SIZE_T cbMem, ULONG fNewProt) RT_NOTHROW_DEF
+{
+ ULONG fOldProt = 0;
+ return NtProtectVirtualMemory(NtCurrentProcess(), &pvMem, &cbMem, fNewProt, &fOldProt);
+}
+
+
+/**
+ * Installs or reinstalls the NTDLL patches.
+ */
+static void supR3HardenedWinReInstallHooks(bool fFirstCall) RT_NOTHROW_DEF
+{
+ struct
+ {
+ size_t cbPatch;
+ uint8_t const *pabPatch;
+ uint8_t **ppbApi;
+ const char *pszName;
+ } const s_aPatches[] =
+ {
+ { sizeof(g_abNtCreateSectionPatch), g_abNtCreateSectionPatch, &g_pbNtCreateSection, "NtCreateSection" },
+ { sizeof(g_abLdrLoadDllPatch), g_abLdrLoadDllPatch, &g_pbLdrLoadDll, "LdrLoadDll" },
+ { sizeof(g_abKiUserApcDispatcherPatch), g_abKiUserApcDispatcherPatch, &g_pbKiUserApcDispatcher, "KiUserApcDispatcher" },
+#ifndef VBOX_WITHOUT_HARDENDED_XCPT_LOGGING
+ { sizeof(g_abKiUserExceptionDispatcherPatch), g_abKiUserExceptionDispatcherPatch, &g_pbKiUserExceptionDispatcher, "KiUserExceptionDispatcher" },
+#endif
+ };
+
+ ULONG fAmIAlone = ~(ULONG)0;
+
+ for (uint32_t i = 0; i < RT_ELEMENTS(s_aPatches); i++)
+ {
+ uint8_t *pbApi = *s_aPatches[i].ppbApi;
+ if (memcmp(pbApi, s_aPatches[i].pabPatch, s_aPatches[i].cbPatch) != 0)
+ {
+ /*
+ * Log the incident if it's not the initial call.
+ */
+ static uint32_t volatile s_cTimes = 0;
+ if (!fFirstCall && s_cTimes < 128)
+ {
+ s_cTimes++;
+ SUP_DPRINTF(("supR3HardenedWinReInstallHooks: Reinstalling %s (%p: %.*Rhxs).\n",
+ s_aPatches[i].pszName, pbApi, s_aPatches[i].cbPatch, pbApi));
+ }
+
+ Assert(s_aPatches[i].cbPatch >= 4);
+
+ SUPR3HARDENED_ASSERT_NT_SUCCESS(supR3HardenedWinProtectMemory(pbApi, s_aPatches[i].cbPatch, PAGE_EXECUTE_READWRITE));
+
+ /*
+ * If we're alone, just memcpy the patch in.
+ */
+
+ if (fAmIAlone == ~(ULONG)0)
+ fAmIAlone = supR3HardenedWinAmIAlone();
+ if (fAmIAlone)
+ memcpy(pbApi, s_aPatches[i].pabPatch, s_aPatches[i].cbPatch);
+ else
+ {
+ /*
+ * Not alone. Start by injecting a JMP $-2, then waste some
+ * CPU cycles to get the other threads a good chance of getting
+ * out of the code before we replace it.
+ */
+ RTUINT32U uJmpDollarMinus;
+ uJmpDollarMinus.au8[0] = 0xeb;
+ uJmpDollarMinus.au8[1] = 0xfe;
+ uJmpDollarMinus.au8[2] = pbApi[2];
+ uJmpDollarMinus.au8[3] = pbApi[3];
+ ASMAtomicXchgU32((uint32_t volatile *)pbApi, uJmpDollarMinus.u);
+
+ NtYieldExecution();
+ NtYieldExecution();
+
+ /* Copy in the tail bytes of the patch, then xchg the jmp $-2. */
+ if (s_aPatches[i].cbPatch > 4)
+ memcpy(&pbApi[4], &s_aPatches[i].pabPatch[4], s_aPatches[i].cbPatch - 4);
+ ASMAtomicXchgU32((uint32_t volatile *)pbApi, *(uint32_t *)s_aPatches[i].pabPatch);
+ }
+
+ SUPR3HARDENED_ASSERT_NT_SUCCESS(supR3HardenedWinProtectMemory(pbApi, s_aPatches[i].cbPatch, PAGE_EXECUTE_READ));
+ }
+ }
+}
+
+
+/**
+ * Install hooks for intercepting calls dealing with mapping shared libraries
+ * into the process.
+ *
+ * This allows us to prevent undesirable shared libraries from being loaded.
+ *
+ * @remarks We assume we're alone in this process, so no seralizing trickery is
+ * necessary when installing the patch.
+ *
+ * @remarks We would normally just copy the prologue sequence somewhere and add
+ * a jump back at the end of it. But because we wish to avoid
+ * allocating executable memory, we need to have preprepared assembly
+ * "copies". This makes the non-system call patching a little tedious
+ * and inflexible.
+ */
+static void supR3HardenedWinInstallHooks(void)
+{
+ NTSTATUS rcNt;
+
+ /*
+ * Disable hard error popups so we can quietly refuse images to be loaded.
+ */
+ ULONG fHardErr = 0;
+ rcNt = NtQueryInformationProcess(NtCurrentProcess(), ProcessDefaultHardErrorMode, &fHardErr, sizeof(fHardErr), NULL);
+ if (!NT_SUCCESS(rcNt))
+ supR3HardenedFatalMsg("supR3HardenedWinInstallHooks", kSupInitOp_Misc, VERR_GENERAL_FAILURE,
+ "NtQueryInformationProcess/ProcessDefaultHardErrorMode failed: %#x\n", rcNt);
+ if (fHardErr & PROCESS_HARDERR_CRITICAL_ERROR)
+ {
+ fHardErr &= ~PROCESS_HARDERR_CRITICAL_ERROR;
+ rcNt = NtSetInformationProcess(NtCurrentProcess(), ProcessDefaultHardErrorMode, &fHardErr, sizeof(fHardErr));
+ if (!NT_SUCCESS(rcNt))
+ supR3HardenedFatalMsg("supR3HardenedWinInstallHooks", kSupInitOp_Misc, VERR_GENERAL_FAILURE,
+ "NtSetInformationProcess/ProcessDefaultHardErrorMode failed: %#x\n", rcNt);
+ }
+
+ /*
+ * Locate the routines first so we can allocate memory that's near enough.
+ */
+ PFNRT pfnNtCreateSection = supR3HardenedWinGetRealDllSymbol("ntdll.dll", "NtCreateSection");
+ SUPR3HARDENED_ASSERT(pfnNtCreateSection != NULL);
+ //SUPR3HARDENED_ASSERT(pfnNtCreateSection == (FARPROC)NtCreateSection);
+
+ PFNRT pfnLdrLoadDll = supR3HardenedWinGetRealDllSymbol("ntdll.dll", "LdrLoadDll");
+ SUPR3HARDENED_ASSERT(pfnLdrLoadDll != NULL);
+ //SUPR3HARDENED_ASSERT(pfnLdrLoadDll == (FARPROC)LdrLoadDll);
+
+ PFNRT pfnKiUserApcDispatcher = supR3HardenedWinGetRealDllSymbol("ntdll.dll", "KiUserApcDispatcher");
+ SUPR3HARDENED_ASSERT(pfnKiUserApcDispatcher != NULL);
+ g_pfnLdrInitializeThunk = (uintptr_t)supR3HardenedWinGetRealDllSymbol("ntdll.dll", "LdrInitializeThunk");
+ SUPR3HARDENED_ASSERT(g_pfnLdrInitializeThunk != NULL);
+
+#ifndef VBOX_WITHOUT_HARDENDED_XCPT_LOGGING
+ PFNRT pfnKiUserExceptionDispatcher = supR3HardenedWinGetRealDllSymbol("ntdll.dll", "KiUserExceptionDispatcher");
+ SUPR3HARDENED_ASSERT(pfnKiUserExceptionDispatcher != NULL);
+#endif
+
+ /*
+ * Exec page setup & management.
+ */
+ uint32_t offExecPage = 0;
+ memset(g_abSupHardReadWriteExecPage, 0xcc, PAGE_SIZE);
+
+ /*
+ * Hook #1 - NtCreateSection.
+ * Purpose: Validate everything that can be mapped into the process before
+ * it's mapped and we still have a file handle to work with.
+ */
+ uint8_t * const pbNtCreateSection = (uint8_t *)(uintptr_t)pfnNtCreateSection;
+ g_pbNtCreateSection = pbNtCreateSection;
+ memcpy(g_abNtCreateSectionPatch, pbNtCreateSection, sizeof(g_abNtCreateSectionPatch));
+
+ g_pfnNtCreateSectionReal = NtCreateSection; /* our direct syscall */
+
+#ifdef RT_ARCH_AMD64
+ /*
+ * Patch 64-bit hosts.
+ */
+ /* Pattern #1: XP64/W2K3-64 thru Windows 8.1
+ 0:000> u ntdll!NtCreateSection
+ ntdll!NtCreateSection:
+ 00000000`779f1750 4c8bd1 mov r10,rcx
+ 00000000`779f1753 b847000000 mov eax,47h
+ 00000000`779f1758 0f05 syscall
+ 00000000`779f175a c3 ret
+ 00000000`779f175b 0f1f440000 nop dword ptr [rax+rax]
+ The variant is the value loaded into eax: W2K3=??, Vista=47h?, W7=47h, W80=48h, W81=49h */
+
+ /* Assemble the patch. */
+ g_abNtCreateSectionPatch[0] = 0x48; /* mov rax, qword */
+ g_abNtCreateSectionPatch[1] = 0xb8;
+ *(uint64_t *)&g_abNtCreateSectionPatch[2] = (uint64_t)supR3HardenedMonitor_NtCreateSection;
+ g_abNtCreateSectionPatch[10] = 0xff; /* jmp rax */
+ g_abNtCreateSectionPatch[11] = 0xe0;
+
+#else
+ /*
+ * Patch 32-bit hosts.
+ */
+ /* Pattern #1: XP thru Windows 7
+ kd> u ntdll!NtCreateSection
+ ntdll!NtCreateSection:
+ 7c90d160 b832000000 mov eax,32h
+ 7c90d165 ba0003fe7f mov edx,offset SharedUserData!SystemCallStub (7ffe0300)
+ 7c90d16a ff12 call dword ptr [edx]
+ 7c90d16c c21c00 ret 1Ch
+ 7c90d16f 90 nop
+ The variable bit is the value loaded into eax: XP=32h, W2K3=34h, Vista=4bh, W7=54h
+
+ Pattern #2: Windows 8.1
+ 0:000:x86> u ntdll_6a0f0000!NtCreateSection
+ ntdll_6a0f0000!NtCreateSection:
+ 6a15eabc b854010000 mov eax,154h
+ 6a15eac1 e803000000 call ntdll_6a0f0000!NtCreateSection+0xd (6a15eac9)
+ 6a15eac6 c21c00 ret 1Ch
+ 6a15eac9 8bd4 mov edx,esp
+ 6a15eacb 0f34 sysenter
+ 6a15eacd c3 ret
+ The variable bit is the value loaded into eax: W81=154h */
+
+ /* Assemble the patch. */
+ g_abNtCreateSectionPatch[0] = 0xe9; /* jmp rel32 */
+ *(uint32_t *)&g_abNtCreateSectionPatch[1] = (uintptr_t)supR3HardenedMonitor_NtCreateSection
+ - (uintptr_t)&pbNtCreateSection[1+4];
+
+#endif
+
+ /*
+ * Hook #2 - LdrLoadDll
+ * Purpose: (a) Enforce LdrLoadDll search path constraints, and (b) pre-validate
+ * DLLs so we can avoid calling WinVerifyTrust from the first hook,
+ * and thus avoiding messing up the loader data on some installations.
+ *
+ * This differs from the above function in that is no a system call and
+ * we're at the mercy of the compiler.
+ */
+ uint8_t * const pbLdrLoadDll = (uint8_t *)(uintptr_t)pfnLdrLoadDll;
+ g_pbLdrLoadDll = pbLdrLoadDll;
+ memcpy(g_abLdrLoadDllPatch, pbLdrLoadDll, sizeof(g_abLdrLoadDllPatch));
+
+ DISSTATE Dis;
+ uint32_t cbInstr;
+ uint32_t offJmpBack = 0;
+
+#ifdef RT_ARCH_AMD64
+ /*
+ * Patch 64-bit hosts.
+ */
+ /* Just use the disassembler to skip 12 bytes or more. */
+ while (offJmpBack < 12)
+ {
+ cbInstr = 1;
+ int rc = DISInstr(pbLdrLoadDll + offJmpBack, DISCPUMODE_64BIT, &Dis, &cbInstr);
+ if ( RT_FAILURE(rc)
+ || (Dis.pCurInstr->fOpType & (DISOPTYPE_CONTROLFLOW))
+ || (Dis.ModRM.Bits.Mod == 0 && Dis.ModRM.Bits.Rm == 5 /* wrt RIP */) )
+ supR3HardenedWinHookFailed("LdrLoadDll", pbLdrLoadDll);
+ offJmpBack += cbInstr;
+ }
+
+ /* Assemble the code for resuming the call.*/
+ *(PFNRT *)&g_pfnLdrLoadDllReal = (PFNRT)(uintptr_t)&g_abSupHardReadWriteExecPage[offExecPage];
+
+ memcpy(&g_abSupHardReadWriteExecPage[offExecPage], pbLdrLoadDll, offJmpBack);
+ offExecPage += offJmpBack;
+
+ g_abSupHardReadWriteExecPage[offExecPage++] = 0xff; /* jmp qword [$+8 wrt RIP] */
+ g_abSupHardReadWriteExecPage[offExecPage++] = 0x25;
+ *(uint32_t *)&g_abSupHardReadWriteExecPage[offExecPage] = RT_ALIGN_32(offExecPage + 4, 8) - (offExecPage + 4);
+ offExecPage = RT_ALIGN_32(offExecPage + 4, 8);
+ *(uint64_t *)&g_abSupHardReadWriteExecPage[offExecPage] = (uintptr_t)&pbLdrLoadDll[offJmpBack];
+ offExecPage = RT_ALIGN_32(offExecPage + 8, 16);
+
+ /* Assemble the LdrLoadDll patch. */
+ Assert(offJmpBack >= 12);
+ g_abLdrLoadDllPatch[0] = 0x48; /* mov rax, qword */
+ g_abLdrLoadDllPatch[1] = 0xb8;
+ *(uint64_t *)&g_abLdrLoadDllPatch[2] = (uint64_t)supR3HardenedMonitor_LdrLoadDll;
+ g_abLdrLoadDllPatch[10] = 0xff; /* jmp rax */
+ g_abLdrLoadDllPatch[11] = 0xe0;
+
+#else
+ /*
+ * Patch 32-bit hosts.
+ */
+ /* Just use the disassembler to skip 5 bytes or more. */
+ while (offJmpBack < 5)
+ {
+ cbInstr = 1;
+ int rc = DISInstr(pbLdrLoadDll + offJmpBack, DISCPUMODE_32BIT, &Dis, &cbInstr);
+ if ( RT_FAILURE(rc)
+ || (Dis.pCurInstr->fOpType & (DISOPTYPE_CONTROLFLOW)) )
+ supR3HardenedWinHookFailed("LdrLoadDll", pbLdrLoadDll);
+ offJmpBack += cbInstr;
+ }
+
+ /* Assemble the code for resuming the call.*/
+ *(PFNRT *)&g_pfnLdrLoadDllReal = (PFNRT)(uintptr_t)&g_abSupHardReadWriteExecPage[offExecPage];
+
+ memcpy(&g_abSupHardReadWriteExecPage[offExecPage], pbLdrLoadDll, offJmpBack);
+ offExecPage += offJmpBack;
+
+ g_abSupHardReadWriteExecPage[offExecPage++] = 0xe9; /* jmp rel32 */
+ *(uint32_t *)&g_abSupHardReadWriteExecPage[offExecPage] = (uintptr_t)&pbLdrLoadDll[offJmpBack]
+ - (uintptr_t)&g_abSupHardReadWriteExecPage[offExecPage + 4];
+ offExecPage = RT_ALIGN_32(offExecPage + 4, 16);
+
+ /* Assemble the LdrLoadDll patch. */
+ memcpy(g_abLdrLoadDllPatch, pbLdrLoadDll, sizeof(g_abLdrLoadDllPatch));
+ Assert(offJmpBack >= 5);
+ g_abLdrLoadDllPatch[0] = 0xe9;
+ *(uint32_t *)&g_abLdrLoadDllPatch[1] = (uintptr_t)supR3HardenedMonitor_LdrLoadDll - (uintptr_t)&pbLdrLoadDll[1+4];
+#endif
+
+ /*
+ * Hook #3 - KiUserApcDispatcher
+ * Purpose: Prevent user APC to memory we (or our parent) has freed from
+ * crashing the process. Also ensures no code injection via user
+ * APC during process init given the way we're vetting the APCs.
+ *
+ * This differs from the first function in that is no a system call and
+ * we're at the mercy of the handwritten assembly.
+ *
+ * Note! We depend on all waits up past the patching to be non-altertable,
+ * otherwise an APC might slip by us.
+ */
+ uint8_t * const pbKiUserApcDispatcher = (uint8_t *)(uintptr_t)pfnKiUserApcDispatcher;
+ g_pbKiUserApcDispatcher = pbKiUserApcDispatcher;
+ memcpy(g_abKiUserApcDispatcherPatch, pbKiUserApcDispatcher, sizeof(g_abKiUserApcDispatcherPatch));
+
+#ifdef RT_ARCH_AMD64
+ /*
+ * Patch 64-bit hosts.
+ */
+ /* Just use the disassembler to skip 12 bytes or more. */
+ offJmpBack = 0;
+ while (offJmpBack < 12)
+ {
+ cbInstr = 1;
+ int rc = DISInstr(pbKiUserApcDispatcher + offJmpBack, DISCPUMODE_64BIT, &Dis, &cbInstr);
+ if ( RT_FAILURE(rc)
+ || (Dis.pCurInstr->fOpType & (DISOPTYPE_CONTROLFLOW))
+ || (Dis.ModRM.Bits.Mod == 0 && Dis.ModRM.Bits.Rm == 5 /* wrt RIP */) )
+ supR3HardenedWinHookFailed("KiUserApcDispatcher", pbKiUserApcDispatcher);
+ offJmpBack += cbInstr;
+ }
+
+ /* Assemble the code for resuming the call.*/
+ *(PFNRT *)&g_pfnKiUserApcDispatcherReal = (PFNRT)(uintptr_t)&g_abSupHardReadWriteExecPage[offExecPage];
+
+ memcpy(&g_abSupHardReadWriteExecPage[offExecPage], pbKiUserApcDispatcher, offJmpBack);
+ offExecPage += offJmpBack;
+
+ g_abSupHardReadWriteExecPage[offExecPage++] = 0xff; /* jmp qword [$+8 wrt RIP] */
+ g_abSupHardReadWriteExecPage[offExecPage++] = 0x25;
+ *(uint32_t *)&g_abSupHardReadWriteExecPage[offExecPage] = RT_ALIGN_32(offExecPage + 4, 8) - (offExecPage + 4);
+ offExecPage = RT_ALIGN_32(offExecPage + 4, 8);
+ *(uint64_t *)&g_abSupHardReadWriteExecPage[offExecPage] = (uintptr_t)&pbKiUserApcDispatcher[offJmpBack];
+ offExecPage = RT_ALIGN_32(offExecPage + 8, 16);
+
+ /* Assemble the KiUserApcDispatcher patch. */
+ Assert(offJmpBack >= 12);
+ g_abKiUserApcDispatcherPatch[0] = 0x48; /* mov rax, qword */
+ g_abKiUserApcDispatcherPatch[1] = 0xb8;
+ *(uint64_t *)&g_abKiUserApcDispatcherPatch[2] = (uint64_t)supR3HardenedMonitor_KiUserApcDispatcher;
+ g_abKiUserApcDispatcherPatch[10] = 0xff; /* jmp rax */
+ g_abKiUserApcDispatcherPatch[11] = 0xe0;
+
+#else
+ /*
+ * Patch 32-bit hosts.
+ */
+ /* Just use the disassembler to skip 5 bytes or more. */
+ offJmpBack = 0;
+ while (offJmpBack < 5)
+ {
+ cbInstr = 1;
+ int rc = DISInstr(pbKiUserApcDispatcher + offJmpBack, DISCPUMODE_32BIT, &Dis, &cbInstr);
+ if ( RT_FAILURE(rc)
+ || (Dis.pCurInstr->fOpType & (DISOPTYPE_CONTROLFLOW)) )
+ supR3HardenedWinHookFailed("KiUserApcDispatcher", pbKiUserApcDispatcher);
+ offJmpBack += cbInstr;
+ }
+
+ /* Assemble the code for resuming the call.*/
+ *(PFNRT *)&g_pfnKiUserApcDispatcherReal = (PFNRT)(uintptr_t)&g_abSupHardReadWriteExecPage[offExecPage];
+
+ memcpy(&g_abSupHardReadWriteExecPage[offExecPage], pbKiUserApcDispatcher, offJmpBack);
+ offExecPage += offJmpBack;
+
+ g_abSupHardReadWriteExecPage[offExecPage++] = 0xe9; /* jmp rel32 */
+ *(uint32_t *)&g_abSupHardReadWriteExecPage[offExecPage] = (uintptr_t)&pbKiUserApcDispatcher[offJmpBack]
+ - (uintptr_t)&g_abSupHardReadWriteExecPage[offExecPage + 4];
+ offExecPage = RT_ALIGN_32(offExecPage + 4, 16);
+
+ /* Assemble the KiUserApcDispatcher patch. */
+ memcpy(g_abKiUserApcDispatcherPatch, pbKiUserApcDispatcher, sizeof(g_abKiUserApcDispatcherPatch));
+ Assert(offJmpBack >= 5);
+ g_abKiUserApcDispatcherPatch[0] = 0xe9;
+ *(uint32_t *)&g_abKiUserApcDispatcherPatch[1] = (uintptr_t)supR3HardenedMonitor_KiUserApcDispatcher - (uintptr_t)&pbKiUserApcDispatcher[1+4];
+#endif
+
+#ifndef VBOX_WITHOUT_HARDENDED_XCPT_LOGGING
+ /*
+ * Hook #4 - KiUserExceptionDispatcher
+ * Purpose: Logging crashes.
+ *
+ * This differs from the first function in that is no a system call and
+ * we're at the mercy of the handwritten assembly. This is not mandatory,
+ * so we ignore failures here.
+ */
+ uint8_t * const pbKiUserExceptionDispatcher = (uint8_t *)(uintptr_t)pfnKiUserExceptionDispatcher;
+ g_pbKiUserExceptionDispatcher = pbKiUserExceptionDispatcher;
+ memcpy(g_abKiUserExceptionDispatcherPatch, pbKiUserExceptionDispatcher, sizeof(g_abKiUserExceptionDispatcherPatch));
+
+# ifdef RT_ARCH_AMD64
+ /*
+ * Patch 64-bit hosts.
+ *
+ * Assume the following sequence and replacing the loaded Wow64PrepareForException
+ * function pointer with our callback:
+ * cld
+ * mov rax, Wow64PrepareForException ; Wow64PrepareForException(PCONTEXT, PEXCEPTION_RECORD)
+ * test rax, rax
+ * jz skip_wow64_callout
+ * <do_callout_thru_rax>
+ * (We're not a WOW64 process, so the callout should normally never happen.)
+ */
+ if ( pbKiUserExceptionDispatcher[ 0] == 0xfc /* CLD */
+ && pbKiUserExceptionDispatcher[ 1] == 0x48 /* MOV RAX, symbol wrt rip */
+ && pbKiUserExceptionDispatcher[ 2] == 0x8b
+ && pbKiUserExceptionDispatcher[ 3] == 0x05
+ && pbKiUserExceptionDispatcher[ 8] == 0x48 /* TEST RAX, RAX */
+ && pbKiUserExceptionDispatcher[ 9] == 0x85
+ && pbKiUserExceptionDispatcher[10] == 0xc0
+ && pbKiUserExceptionDispatcher[11] == 0x74)
+ {
+ /* Assemble the KiUserExceptionDispatcher patch. */
+ g_abKiUserExceptionDispatcherPatch[1] = 0x48; /* MOV RAX, supR3HardenedMonitor_KiUserExceptionDispatcher */
+ g_abKiUserExceptionDispatcherPatch[2] = 0xb8;
+ *(uint64_t *)&g_abKiUserExceptionDispatcherPatch[3] = (uint64_t)supR3HardenedMonitor_KiUserExceptionDispatcher;
+ g_abKiUserExceptionDispatcherPatch[11] = 0x90; /* NOP (was JZ) */
+ g_abKiUserExceptionDispatcherPatch[12] = 0x90; /* NOP (was DISP8 of JZ) */
+ }
+ else
+ SUP_DPRINTF(("supR3HardenedWinInstallHooks: failed to patch KiUserExceptionDispatcher (%.20Rhxs)\n",
+ pbKiUserExceptionDispatcher));
+# else
+ /*
+ * Patch 32-bit hosts.
+ */
+ /* Just use the disassembler to skip 5 bytes or more. */
+ offJmpBack = 0;
+ while (offJmpBack < 5)
+ {
+ cbInstr = 1;
+ int rc = DISInstr(pbKiUserExceptionDispatcher + offJmpBack, DISCPUMODE_32BIT, &Dis, &cbInstr);
+ if ( RT_FAILURE(rc)
+ || (Dis.pCurInstr->fOpType & (DISOPTYPE_CONTROLFLOW)) )
+ {
+ SUP_DPRINTF(("supR3HardenedWinInstallHooks: failed to patch KiUserExceptionDispatcher (off %#x in %.20Rhxs)\n",
+ offJmpBack, pbKiUserExceptionDispatcher));
+ break;
+ }
+ offJmpBack += cbInstr;
+ }
+ if (offJmpBack >= 5)
+ {
+ /* Assemble the code for resuming the call.*/
+ *(PFNRT *)&g_pfnKiUserExceptionDispatcherReal = (PFNRT)(uintptr_t)&g_abSupHardReadWriteExecPage[offExecPage];
+
+ memcpy(&g_abSupHardReadWriteExecPage[offExecPage], pbKiUserExceptionDispatcher, offJmpBack);
+ offExecPage += offJmpBack;
+
+ g_abSupHardReadWriteExecPage[offExecPage++] = 0xe9; /* jmp rel32 */
+ *(uint32_t *)&g_abSupHardReadWriteExecPage[offExecPage] = (uintptr_t)&pbKiUserExceptionDispatcher[offJmpBack]
+ - (uintptr_t)&g_abSupHardReadWriteExecPage[offExecPage + 4];
+ offExecPage = RT_ALIGN_32(offExecPage + 4, 16);
+
+ /* Assemble the KiUserExceptionDispatcher patch. */
+ memcpy(g_abKiUserExceptionDispatcherPatch, pbKiUserExceptionDispatcher, sizeof(g_abKiUserExceptionDispatcherPatch));
+ Assert(offJmpBack >= 5);
+ g_abKiUserExceptionDispatcherPatch[0] = 0xe9;
+ *(uint32_t *)&g_abKiUserExceptionDispatcherPatch[1] = (uintptr_t)supR3HardenedMonitor_KiUserExceptionDispatcher - (uintptr_t)&pbKiUserExceptionDispatcher[1+4];
+ }
+# endif
+#endif /* !VBOX_WITHOUT_HARDENDED_XCPT_LOGGING */
+
+ /*
+ * Seal the rwx page.
+ */
+ SUPR3HARDENED_ASSERT_NT_SUCCESS(supR3HardenedWinProtectMemory(g_abSupHardReadWriteExecPage, PAGE_SIZE, PAGE_EXECUTE_READ));
+
+ /*
+ * Install the patches.
+ */
+ supR3HardenedWinReInstallHooks(true /*fFirstCall*/);
+}
+
+
+
+
+
+
+/*
+ *
+ * T h r e a d c r e a t i o n c o n t r o l
+ * T h r e a d c r e a t i o n c o n t r o l
+ * T h r e a d c r e a t i o n c o n t r o l
+ *
+ */
+
+
+/**
+ * Common code used for child and parent to make new threads exit immediately.
+ *
+ * This patches the LdrInitializeThunk code to call NtTerminateThread with
+ * STATUS_SUCCESS instead of doing the NTDLL initialization.
+ *
+ * @returns VBox status code.
+ * @param hProcess The process to do this to.
+ * @param pvLdrInitThunk The address of the LdrInitializeThunk code to
+ * override.
+ * @param pvNtTerminateThread The address of the NtTerminateThread function in
+ * the NTDLL instance we're patching. (Must be +/-
+ * 2GB from the thunk code.)
+ * @param pabBackup Where to back up the original instruction bytes
+ * at pvLdrInitThunk.
+ * @param cbBackup The size of the backup area. Must be 16 bytes.
+ * @param pErrInfo Where to return extended error information.
+ * Optional.
+ */
+static int supR3HardNtDisableThreadCreationEx(HANDLE hProcess, void *pvLdrInitThunk, void *pvNtTerminateThread,
+ uint8_t *pabBackup, size_t cbBackup, PRTERRINFO pErrInfo)
+{
+ SUP_DPRINTF(("supR3HardNtDisableThreadCreation: pvLdrInitThunk=%p pvNtTerminateThread=%p\n", pvLdrInitThunk, pvNtTerminateThread));
+ SUPR3HARDENED_ASSERT(cbBackup == 16);
+ SUPR3HARDENED_ASSERT(RT_ABS((intptr_t)pvLdrInitThunk - (intptr_t)pvNtTerminateThread) < 16*_1M);
+
+ /*
+ * Back up the thunk code.
+ */
+ SIZE_T cbIgnored;
+ NTSTATUS rcNt = NtReadVirtualMemory(hProcess, pvLdrInitThunk, pabBackup, cbBackup, &cbIgnored);
+ if (!NT_SUCCESS(rcNt))
+ return RTErrInfoSetF(pErrInfo, VERR_GENERAL_FAILURE,
+ "supR3HardNtDisableThreadCreation: NtReadVirtualMemory/LdrInitializeThunk failed: %#x", rcNt);
+
+ /*
+ * Cook up replacement code that calls NtTerminateThread.
+ */
+ uint8_t abReplacement[16];
+ memcpy(abReplacement, pabBackup, sizeof(abReplacement));
+
+#ifdef RT_ARCH_AMD64
+ abReplacement[0] = 0x31; /* xor ecx, ecx */
+ abReplacement[1] = 0xc9;
+ abReplacement[2] = 0x31; /* xor edx, edx */
+ abReplacement[3] = 0xd2;
+ abReplacement[4] = 0xe8; /* call near NtTerminateThread */
+ *(int32_t *)&abReplacement[5] = (int32_t)((uintptr_t)pvNtTerminateThread - ((uintptr_t)pvLdrInitThunk + 9));
+ abReplacement[9] = 0xcc; /* int3 */
+#elif defined(RT_ARCH_X86)
+ abReplacement[0] = 0x6a; /* push 0 */
+ abReplacement[1] = 0x00;
+ abReplacement[2] = 0x6a; /* push 0 */
+ abReplacement[3] = 0x00;
+ abReplacement[4] = 0xe8; /* call near NtTerminateThread */
+ *(int32_t *)&abReplacement[5] = (int32_t)((uintptr_t)pvNtTerminateThread - ((uintptr_t)pvLdrInitThunk + 9));
+ abReplacement[9] = 0xcc; /* int3 */
+#else
+# error "Unsupported arch."
+#endif
+
+ /*
+ * Install the replacment code.
+ */
+ PVOID pvProt = pvLdrInitThunk;
+ SIZE_T cbProt = cbBackup;
+ ULONG fOldProt = 0;
+ rcNt = NtProtectVirtualMemory(hProcess, &pvProt, &cbProt, PAGE_EXECUTE_READWRITE, &fOldProt);
+ if (!NT_SUCCESS(rcNt))
+ return RTErrInfoSetF(pErrInfo, VERR_GENERAL_FAILURE,
+ "supR3HardNtDisableThreadCreationEx: NtProtectVirtualMemory/LdrInitializeThunk failed: %#x", rcNt);
+
+ rcNt = NtWriteVirtualMemory(hProcess, pvLdrInitThunk, abReplacement, sizeof(abReplacement), &cbIgnored);
+ if (!NT_SUCCESS(rcNt))
+ return RTErrInfoSetF(pErrInfo, VERR_GENERAL_FAILURE,
+ "supR3HardNtDisableThreadCreationEx: NtWriteVirtualMemory/LdrInitializeThunk failed: %#x", rcNt);
+
+ pvProt = pvLdrInitThunk;
+ cbProt = cbBackup;
+ rcNt = NtProtectVirtualMemory(hProcess, &pvProt, &cbProt, fOldProt, &fOldProt);
+ if (!NT_SUCCESS(rcNt))
+ return RTErrInfoSetF(pErrInfo, VERR_GENERAL_FAILURE,
+ "supR3HardNtDisableThreadCreationEx: NtProtectVirtualMemory/LdrInitializeThunk/2 failed: %#x", rcNt);
+
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * Undo the effects of supR3HardNtDisableThreadCreationEx.
+ *
+ * @returns VBox status code.
+ * @param hProcess The process to do this to.
+ * @param pvLdrInitThunk The address of the LdrInitializeThunk code to
+ * override.
+ * @param pabBackup Where to back up the original instruction bytes
+ * at pvLdrInitThunk.
+ * @param cbBackup The size of the backup area. Must be 16 bytes.
+ * @param pErrInfo Where to return extended error information.
+ * Optional.
+ */
+static int supR3HardNtEnableThreadCreationEx(HANDLE hProcess, void *pvLdrInitThunk, uint8_t const *pabBackup, size_t cbBackup,
+ PRTERRINFO pErrInfo)
+{
+ SUP_DPRINTF(("supR3HardNtEnableThreadCreationEx:\n"));
+ SUPR3HARDENED_ASSERT(cbBackup == 16);
+
+ PVOID pvProt = pvLdrInitThunk;
+ SIZE_T cbProt = cbBackup;
+ ULONG fOldProt = 0;
+ NTSTATUS rcNt = NtProtectVirtualMemory(hProcess, &pvProt, &cbProt, PAGE_EXECUTE_READWRITE, &fOldProt);
+ if (!NT_SUCCESS(rcNt))
+ return RTErrInfoSetF(pErrInfo, VERR_GENERAL_FAILURE,
+ "supR3HardNtEnableThreadCreationEx: NtProtectVirtualMemory/LdrInitializeThunk failed: %#x", rcNt);
+
+ SIZE_T cbIgnored;
+ rcNt = NtWriteVirtualMemory(hProcess, pvLdrInitThunk, pabBackup, cbBackup, &cbIgnored);
+ if (!NT_SUCCESS(rcNt))
+ return RTErrInfoSetF(pErrInfo, VERR_GENERAL_FAILURE,
+ "supR3HardNtEnableThreadCreationEx: NtWriteVirtualMemory/LdrInitializeThunk[restore] failed: %#x",
+ rcNt);
+
+ pvProt = pvLdrInitThunk;
+ cbProt = cbBackup;
+ rcNt = NtProtectVirtualMemory(hProcess, &pvProt, &cbProt, fOldProt, &fOldProt);
+ if (!NT_SUCCESS(rcNt))
+ return RTErrInfoSetF(pErrInfo, VERR_GENERAL_FAILURE,
+ "supR3HardNtEnableThreadCreationEx: NtProtectVirtualMemory/LdrInitializeThunk[restore] failed: %#x",
+ rcNt);
+
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * Disable thread creation for the current process.
+ *
+ * @remarks Doesn't really disables it, just makes the threads exit immediately
+ * without executing any real code.
+ */
+static void supR3HardenedWinDisableThreadCreation(void)
+{
+ /* Cannot use the imported NtTerminateThread as it's pointing to our own
+ syscall assembly code. */
+ static PFNRT s_pfnNtTerminateThread = NULL;
+ if (s_pfnNtTerminateThread == NULL)
+ s_pfnNtTerminateThread = supR3HardenedWinGetRealDllSymbol("ntdll.dll", "NtTerminateThread");
+ SUPR3HARDENED_ASSERT(s_pfnNtTerminateThread);
+
+ int rc = supR3HardNtDisableThreadCreationEx(NtCurrentProcess(),
+ (void *)(uintptr_t)&LdrInitializeThunk,
+ (void *)(uintptr_t)s_pfnNtTerminateThread,
+ g_abLdrInitThunkSelfBackup, sizeof(g_abLdrInitThunkSelfBackup),
+ NULL /* pErrInfo*/);
+ g_fSupInitThunkSelfPatched = RT_SUCCESS(rc);
+}
+
+
+/**
+ * Undoes the effects of supR3HardenedWinDisableThreadCreation.
+ */
+DECLHIDDEN(void) supR3HardenedWinEnableThreadCreation(void)
+{
+ if (g_fSupInitThunkSelfPatched)
+ {
+ int rc = supR3HardNtEnableThreadCreationEx(NtCurrentProcess(),
+ (void *)(uintptr_t)&LdrInitializeThunk,
+ g_abLdrInitThunkSelfBackup, sizeof(g_abLdrInitThunkSelfBackup),
+ RTErrInfoInitStatic(&g_ErrInfoStatic));
+ if (RT_FAILURE(rc))
+ supR3HardenedError(rc, true /*fFatal*/, "%s", g_ErrInfoStatic.szMsg);
+ g_fSupInitThunkSelfPatched = false;
+ }
+}
+
+
+
+
+/*
+ *
+ * R e s p a w n
+ * R e s p a w n
+ * R e s p a w n
+ *
+ */
+
+
+/**
+ * Gets the SID of the user associated with the process.
+ *
+ * @returns @c true if we've got a login SID, @c false if not.
+ * @param pSidUser Where to return the user SID.
+ * @param cbSidUser The size of the user SID buffer.
+ * @param pSidLogin Where to return the login SID.
+ * @param cbSidLogin The size of the login SID buffer.
+ */
+static bool supR3HardNtChildGetUserAndLogSids(PSID pSidUser, ULONG cbSidUser, PSID pSidLogin, ULONG cbSidLogin)
+{
+ HANDLE hToken;
+ SUPR3HARDENED_ASSERT_NT_SUCCESS(NtOpenProcessToken(NtCurrentProcess(), TOKEN_QUERY, &hToken));
+ union
+ {
+ TOKEN_USER UserInfo;
+ TOKEN_GROUPS Groups;
+ uint8_t abPadding[4096];
+ } uBuf;
+ ULONG cbRet = 0;
+ SUPR3HARDENED_ASSERT_NT_SUCCESS(NtQueryInformationToken(hToken, TokenUser, &uBuf, sizeof(uBuf), &cbRet));
+ SUPR3HARDENED_ASSERT_NT_SUCCESS(RtlCopySid(cbSidUser, pSidUser, uBuf.UserInfo.User.Sid));
+
+ bool fLoginSid = false;
+ NTSTATUS rcNt = NtQueryInformationToken(hToken, TokenLogonSid, &uBuf, sizeof(uBuf), &cbRet);
+ if (NT_SUCCESS(rcNt))
+ {
+ for (DWORD i = 0; i < uBuf.Groups.GroupCount; i++)
+ if ((uBuf.Groups.Groups[i].Attributes & SE_GROUP_LOGON_ID) == SE_GROUP_LOGON_ID)
+ {
+ SUPR3HARDENED_ASSERT_NT_SUCCESS(RtlCopySid(cbSidLogin, pSidLogin, uBuf.Groups.Groups[i].Sid));
+ fLoginSid = true;
+ break;
+ }
+ }
+
+ SUPR3HARDENED_ASSERT_NT_SUCCESS(NtClose(hToken));
+
+ return fLoginSid;
+}
+
+
+/**
+ * Build security attributes for the process or the primary thread (@a fProcess)
+ *
+ * Process DACLs can be bypassed using the SeDebugPrivilege (generally available
+ * to admins, i.e. normal windows users), or by taking ownership and/or
+ * modifying the DACL. However, it restricts
+ *
+ * @param pSecAttrs Where to return the security attributes.
+ * @param pCleanup Cleanup record.
+ * @param fProcess Set if it's for the process, clear if it's for
+ * the primary thread.
+ */
+static void supR3HardNtChildInitSecAttrs(PSECURITY_ATTRIBUTES pSecAttrs, PMYSECURITYCLEANUP pCleanup, bool fProcess)
+{
+ /*
+ * Safe return values.
+ */
+ suplibHardenedMemSet(pCleanup, 0, sizeof(*pCleanup));
+
+ pSecAttrs->nLength = sizeof(*pSecAttrs);
+ pSecAttrs->bInheritHandle = FALSE;
+ pSecAttrs->lpSecurityDescriptor = NULL;
+
+/** @todo This isn't at all complete, just sketches... */
+
+ /*
+ * Create an ACL detailing the access of the above groups.
+ */
+ SUPR3HARDENED_ASSERT_NT_SUCCESS(RtlCreateAcl(&pCleanup->Acl.AclHdr, sizeof(pCleanup->Acl), ACL_REVISION));
+
+ ULONG fDeny = DELETE | WRITE_DAC | WRITE_OWNER;
+ ULONG fAllow = SYNCHRONIZE | READ_CONTROL;
+ ULONG fAllowLogin = SYNCHRONIZE | READ_CONTROL;
+ if (fProcess)
+ {
+ fDeny |= PROCESS_CREATE_THREAD | PROCESS_SET_SESSIONID | PROCESS_VM_OPERATION | PROCESS_VM_WRITE
+ | PROCESS_CREATE_PROCESS | PROCESS_DUP_HANDLE | PROCESS_SET_QUOTA
+ | PROCESS_SET_INFORMATION | PROCESS_SUSPEND_RESUME;
+ fAllow |= PROCESS_TERMINATE | PROCESS_VM_READ | PROCESS_QUERY_INFORMATION;
+ fAllowLogin |= PROCESS_TERMINATE | PROCESS_VM_READ | PROCESS_QUERY_INFORMATION;
+ if (g_uNtVerCombined >= SUP_MAKE_NT_VER_SIMPLE(6, 0)) /* Introduced in Vista. */
+ {
+ fAllow |= PROCESS_QUERY_LIMITED_INFORMATION;
+ fAllowLogin |= PROCESS_QUERY_LIMITED_INFORMATION;
+ }
+ if (g_uNtVerCombined >= SUP_MAKE_NT_VER_SIMPLE(6, 3)) /* Introduced in Windows 8.1. */
+ fAllow |= PROCESS_SET_LIMITED_INFORMATION;
+ }
+ else
+ {
+ fDeny |= THREAD_SUSPEND_RESUME | THREAD_SET_CONTEXT | THREAD_SET_INFORMATION | THREAD_SET_THREAD_TOKEN
+ | THREAD_IMPERSONATE | THREAD_DIRECT_IMPERSONATION;
+ fAllow |= THREAD_GET_CONTEXT | THREAD_QUERY_INFORMATION;
+ fAllowLogin |= THREAD_GET_CONTEXT | THREAD_QUERY_INFORMATION;
+ if (g_uNtVerCombined >= SUP_MAKE_NT_VER_SIMPLE(6, 0)) /* Introduced in Vista. */
+ {
+ fAllow |= THREAD_QUERY_LIMITED_INFORMATION | THREAD_SET_LIMITED_INFORMATION;
+ fAllowLogin |= THREAD_QUERY_LIMITED_INFORMATION;
+ }
+
+ }
+ fDeny |= ~fAllow & (SPECIFIC_RIGHTS_ALL | STANDARD_RIGHTS_ALL);
+
+ /* Deny everyone access to bad bits. */
+#if 1
+ SID_IDENTIFIER_AUTHORITY SIDAuthWorld = SECURITY_WORLD_SID_AUTHORITY;
+ SUPR3HARDENED_ASSERT_NT_SUCCESS(RtlInitializeSid(&pCleanup->Everyone.Sid, &SIDAuthWorld, 1));
+ *RtlSubAuthoritySid(&pCleanup->Everyone.Sid, 0) = SECURITY_WORLD_RID;
+ SUPR3HARDENED_ASSERT_NT_SUCCESS(RtlAddAccessDeniedAce(&pCleanup->Acl.AclHdr, ACL_REVISION,
+ fDeny, &pCleanup->Everyone.Sid));
+#endif
+
+#if 0
+ /* Grant some access to the owner - doesn't work. */
+ SID_IDENTIFIER_AUTHORITY SIDAuthCreator = SECURITY_CREATOR_SID_AUTHORITY;
+ SUPR3HARDENED_ASSERT_NT_SUCCESS(RtlInitializeSid(&pCleanup->Owner.Sid, &SIDAuthCreator, 1));
+ *RtlSubAuthoritySid(&pCleanup->Owner.Sid, 0) = SECURITY_CREATOR_OWNER_RID;
+
+ SUPR3HARDENED_ASSERT_NT_SUCCESS(RtlAddAccessDeniedAce(&pCleanup->Acl.AclHdr, ACL_REVISION,
+ fDeny, &pCleanup->Owner.Sid));
+ SUPR3HARDENED_ASSERT_NT_SUCCESS(RtlAddAccessAllowedAce(&pCleanup->Acl.AclHdr, ACL_REVISION,
+ fAllow, &pCleanup->Owner.Sid));
+#endif
+
+#if 1
+ bool fHasLoginSid = supR3HardNtChildGetUserAndLogSids(&pCleanup->User.Sid, sizeof(pCleanup->User),
+ &pCleanup->Login.Sid, sizeof(pCleanup->Login));
+
+# if 1
+ /* Grant minimal access to the user. */
+ SUPR3HARDENED_ASSERT_NT_SUCCESS(RtlAddAccessDeniedAce(&pCleanup->Acl.AclHdr, ACL_REVISION,
+ fDeny, &pCleanup->User.Sid));
+ SUPR3HARDENED_ASSERT_NT_SUCCESS(RtlAddAccessAllowedAce(&pCleanup->Acl.AclHdr, ACL_REVISION,
+ fAllow, &pCleanup->User.Sid));
+# endif
+
+# if 1
+ /* Grant very limited access to the login sid. */
+ if (fHasLoginSid)
+ {
+ SUPR3HARDENED_ASSERT_NT_SUCCESS(RtlAddAccessAllowedAce(&pCleanup->Acl.AclHdr, ACL_REVISION,
+ fAllowLogin, &pCleanup->Login.Sid));
+ }
+# endif
+
+#endif
+
+ /*
+ * Create a security descriptor with the above ACL.
+ */
+ PSECURITY_DESCRIPTOR pSecDesc = (PSECURITY_DESCRIPTOR)RTMemAllocZ(SECURITY_DESCRIPTOR_MIN_LENGTH);
+ pCleanup->pSecDesc = pSecDesc;
+
+ SUPR3HARDENED_ASSERT_NT_SUCCESS(RtlCreateSecurityDescriptor(pSecDesc, SECURITY_DESCRIPTOR_REVISION));
+ SUPR3HARDENED_ASSERT_NT_SUCCESS(RtlSetDaclSecurityDescriptor(pSecDesc, TRUE /*fDaclPresent*/, &pCleanup->Acl.AclHdr,
+ FALSE /*fDaclDefaulted*/));
+ pSecAttrs->lpSecurityDescriptor = pSecDesc;
+}
+
+
+/**
+ * Predicate function which tests whether @a ch is a argument separator
+ * character.
+ *
+ * @returns True/false.
+ * @param ch The character to examine.
+ */
+DECLINLINE(bool) suplibCommandLineIsArgSeparator(int ch)
+{
+ return ch == ' '
+ || ch == '\t'
+ || ch == '\n'
+ || ch == '\r';
+}
+
+
+/**
+ * Construct the new command line.
+ *
+ * Since argc/argv are both derived from GetCommandLineW (see
+ * suplibHardenedWindowsMain), we skip the argument by argument UTF-8 -> UTF-16
+ * conversion and quoting by going to the original source.
+ *
+ * The executable name, though, is replaced in case it's not a fullly
+ * qualified path.
+ *
+ * The re-spawn indicator is added immediately after the executable name
+ * so that we don't get tripped up missing close quote chars in the last
+ * argument.
+ *
+ * @returns Pointer to a command line string (heap).
+ * @param pString Unicode string structure to initialize to the
+ * command line. Optional.
+ * @param iWhich Which respawn we're to check for, 1 being the first
+ * one, and 2 the second and final.
+ */
+static PRTUTF16 supR3HardNtChildConstructCmdLine(PUNICODE_STRING pString, int iWhich)
+{
+ SUPR3HARDENED_ASSERT(iWhich == 1 || iWhich == 2);
+
+ /*
+ * Get the command line and skip the executable name.
+ */
+ PUNICODE_STRING pCmdLineStr = &NtCurrentPeb()->ProcessParameters->CommandLine;
+ PCRTUTF16 pawcArgs = pCmdLineStr->Buffer;
+ uint32_t cwcArgs = pCmdLineStr->Length / sizeof(WCHAR);
+
+ /* Skip leading space (shouldn't be any, but whatever). */
+ while (cwcArgs > 0 && suplibCommandLineIsArgSeparator(*pawcArgs) )
+ cwcArgs--, pawcArgs++;
+ SUPR3HARDENED_ASSERT(cwcArgs > 0 && *pawcArgs != '\0');
+
+ /* Walk to the end of it. */
+ int fQuoted = false;
+ do
+ {
+ if (*pawcArgs == '"')
+ {
+ fQuoted = !fQuoted;
+ cwcArgs--; pawcArgs++;
+ }
+ else if (*pawcArgs != '\\' || (pawcArgs[1] != '\\' && pawcArgs[1] != '"'))
+ cwcArgs--, pawcArgs++;
+ else
+ {
+ unsigned cSlashes = 0;
+ do
+ {
+ cSlashes++;
+ cwcArgs--;
+ pawcArgs++;
+ }
+ while (cwcArgs > 0 && *pawcArgs == '\\');
+ if (cwcArgs > 0 && *pawcArgs == '"' && (cSlashes & 1))
+ cwcArgs--, pawcArgs++; /* odd number of slashes == escaped quote */
+ }
+ } while (cwcArgs > 0 && (fQuoted || !suplibCommandLineIsArgSeparator(*pawcArgs)));
+
+ /* Skip trailing spaces. */
+ while (cwcArgs > 0 && suplibCommandLineIsArgSeparator(*pawcArgs))
+ cwcArgs--, pawcArgs++;
+
+ /*
+ * Allocate a new buffer.
+ */
+ AssertCompile(sizeof(SUPR3_RESPAWN_1_ARG0) == sizeof(SUPR3_RESPAWN_2_ARG0));
+ size_t cwcCmdLine = (sizeof(SUPR3_RESPAWN_1_ARG0) - 1) / sizeof(SUPR3_RESPAWN_1_ARG0[0]) /* Respawn exe name. */
+ + !!cwcArgs + cwcArgs; /* if arguments present, add space + arguments. */
+ if (cwcCmdLine * sizeof(WCHAR) >= 0xfff0)
+ supR3HardenedFatalMsg("supR3HardNtChildConstructCmdLine", kSupInitOp_Misc, VERR_OUT_OF_RANGE,
+ "Command line is too long (%u chars)!", cwcCmdLine);
+
+ PRTUTF16 pwszCmdLine = (PRTUTF16)RTMemAlloc((cwcCmdLine + 1) * sizeof(RTUTF16));
+ SUPR3HARDENED_ASSERT(pwszCmdLine != NULL);
+
+ /*
+ * Construct the new command line.
+ */
+ PRTUTF16 pwszDst = pwszCmdLine;
+ for (const char *pszSrc = iWhich == 1 ? SUPR3_RESPAWN_1_ARG0 : SUPR3_RESPAWN_2_ARG0; *pszSrc; pszSrc++)
+ *pwszDst++ = *pszSrc;
+
+ if (cwcArgs)
+ {
+ *pwszDst++ = ' ';
+ suplibHardenedMemCopy(pwszDst, pawcArgs, cwcArgs * sizeof(RTUTF16));
+ pwszDst += cwcArgs;
+ }
+
+ *pwszDst = '\0';
+ SUPR3HARDENED_ASSERT((uintptr_t)(pwszDst - pwszCmdLine) == cwcCmdLine);
+
+ if (pString)
+ {
+ pString->Buffer = pwszCmdLine;
+ pString->Length = (USHORT)(cwcCmdLine * sizeof(WCHAR));
+ pString->MaximumLength = pString->Length + sizeof(WCHAR);
+ }
+ return pwszCmdLine;
+}
+
+
+/**
+ * Terminates the child process.
+ *
+ * @param hProcess The process handle.
+ * @param pszWhere Who's having child rasing troubles.
+ * @param rc The status code to report.
+ * @param pszFormat The message format string.
+ * @param ... Message format arguments.
+ */
+static void supR3HardenedWinKillChild(HANDLE hProcess, const char *pszWhere, int rc, const char *pszFormat, ...)
+{
+ /*
+ * Terminate the process ASAP and display error.
+ */
+ NtTerminateProcess(hProcess, RTEXITCODE_FAILURE);
+
+ va_list va;
+ va_start(va, pszFormat);
+ supR3HardenedErrorV(rc, false /*fFatal*/, pszFormat, va);
+ va_end(va);
+
+ /*
+ * Wait for the process to really go away.
+ */
+ PROCESS_BASIC_INFORMATION BasicInfo;
+ NTSTATUS rcNtExit = NtQueryInformationProcess(hProcess, ProcessBasicInformation, &BasicInfo, sizeof(BasicInfo), NULL);
+ bool fExitOk = NT_SUCCESS(rcNtExit) && BasicInfo.ExitStatus != STATUS_PENDING;
+ if (!fExitOk)
+ {
+ NTSTATUS rcNtWait;
+ uint64_t uMsTsStart = supR3HardenedWinGetMilliTS();
+ do
+ {
+ NtTerminateProcess(hProcess, DBG_TERMINATE_PROCESS);
+
+ LARGE_INTEGER Timeout;
+ Timeout.QuadPart = -20000000; /* 2 second */
+ rcNtWait = NtWaitForSingleObject(hProcess, TRUE /*Alertable*/, &Timeout);
+
+ rcNtExit = NtQueryInformationProcess(hProcess, ProcessBasicInformation, &BasicInfo, sizeof(BasicInfo), NULL);
+ fExitOk = NT_SUCCESS(rcNtExit) && BasicInfo.ExitStatus != STATUS_PENDING;
+ } while ( !fExitOk
+ && ( rcNtWait == STATUS_TIMEOUT
+ || rcNtWait == STATUS_USER_APC
+ || rcNtWait == STATUS_ALERTED)
+ && supR3HardenedWinGetMilliTS() - uMsTsStart < 60 * 1000);
+ if (fExitOk)
+ supR3HardenedError(rc, false /*fFatal*/,
+ "NtDuplicateObject failed and we failed to kill child: rc=%u (%#x) rcNtWait=%#x hProcess=%p\n",
+ rc, rc, rcNtWait, hProcess);
+ }
+
+ /*
+ * Final error message.
+ */
+ va_start(va, pszFormat);
+ supR3HardenedFatalMsgV(pszWhere, kSupInitOp_Misc, rc, pszFormat, va);
+ /* not reached */
+}
+
+
+/**
+ * Checks the child process when hEvtParent is signalled.
+ *
+ * This will read the request data from the child and check it against expected
+ * request. If an error is signalled, we'll raise it and make sure the child
+ * terminates before terminating the calling process.
+ *
+ * @param pThis The child process data structure.
+ * @param enmExpectedRequest The expected child request.
+ * @param pszWhat What we're waiting for.
+ */
+static void supR3HardNtChildProcessRequest(PSUPR3HARDNTCHILD pThis, SUPR3WINCHILDREQ enmExpectedRequest, const char *pszWhat)
+{
+ /*
+ * Read the process parameters from the child.
+ */
+ uintptr_t uChildAddr = (uintptr_t)pThis->Peb.ImageBaseAddress
+ + ((uintptr_t)&g_ProcParams - (uintptr_t)NtCurrentPeb()->ImageBaseAddress);
+ SIZE_T cbIgnored = 0;
+ RT_ZERO(pThis->ProcParams);
+ NTSTATUS rcNt = NtReadVirtualMemory(pThis->hProcess, (PVOID)uChildAddr,
+ &pThis->ProcParams, sizeof(pThis->ProcParams), &cbIgnored);
+ if (!NT_SUCCESS(rcNt))
+ supR3HardenedWinKillChild(pThis, "supR3HardNtChildProcessRequest", rcNt,
+ "NtReadVirtualMemory(,%p,) failed reading child process status: %#x\n", uChildAddr, rcNt);
+
+ /*
+ * Is it the expected request?
+ */
+ if (pThis->ProcParams.enmRequest == enmExpectedRequest)
+ return;
+
+ /*
+ * No, not the expected request. If it's an error request, tell the child
+ * to terminate itself, otherwise we'll have to terminate it.
+ */
+ pThis->ProcParams.szErrorMsg[sizeof(pThis->ProcParams.szErrorMsg) - 1] = '\0';
+ pThis->ProcParams.szWhere[sizeof(pThis->ProcParams.szWhere) - 1] = '\0';
+ SUP_DPRINTF(("supR3HardenedWinCheckChild: enmRequest=%d rc=%d enmWhat=%d %s: %s\n",
+ pThis->ProcParams.enmRequest, pThis->ProcParams.rc, pThis->ProcParams.enmWhat,
+ pThis->ProcParams.szWhere, pThis->ProcParams.szErrorMsg));
+
+ if (pThis->ProcParams.enmRequest != kSupR3WinChildReq_Error)
+ supR3HardenedWinKillChild(pThis, "supR3HardenedWinCheckChild", VERR_INVALID_PARAMETER,
+ "Unexpected child request #%d. Was expecting #%d (%s).\n",
+ pThis->ProcParams.enmRequest, enmExpectedRequest, pszWhat);
+
+ rcNt = NtSetEvent(pThis->hEvtChild, NULL);
+ if (!NT_SUCCESS(rcNt))
+ supR3HardenedWinKillChild(pThis, "supR3HardNtChildProcessRequest", rcNt, "NtSetEvent failed: %#x\n", rcNt);
+
+ /* Wait for it to terminate. */
+ LARGE_INTEGER Timeout;
+ Timeout.QuadPart = -50000000; /* 5 seconds */
+ rcNt = NtWaitForSingleObject(pThis->hProcess, FALSE /*Alertable*/, &Timeout);
+ if (rcNt != STATUS_WAIT_0)
+ {
+ SUP_DPRINTF(("supR3HardNtChildProcessRequest: Child is taking too long to quit (rcWait=%#x), killing it...\n", rcNt));
+ NtTerminateProcess(pThis->hProcess, DBG_TERMINATE_PROCESS);
+ }
+
+ /*
+ * Report the error in the same way as it occured in the guest.
+ */
+ if (pThis->ProcParams.enmWhat == kSupInitOp_Invalid)
+ supR3HardenedFatalMsg("supR3HardenedWinCheckChild", kSupInitOp_Misc, pThis->ProcParams.rc,
+ "%s", pThis->ProcParams.szErrorMsg);
+ else
+ supR3HardenedFatalMsg(pThis->ProcParams.szWhere, pThis->ProcParams.enmWhat, pThis->ProcParams.rc,
+ "%s", pThis->ProcParams.szErrorMsg);
+}
+
+
+/**
+ * Waits for the child to make a certain request or terminate.
+ *
+ * The stub process will also wait on it's parent to terminate.
+ * This call will only return if the child made the expected request.
+ *
+ * @param pThis The child process data structure.
+ * @param enmExpectedRequest The child request to wait for.
+ * @param cMsTimeout The number of milliseconds to wait (at least).
+ * @param pszWhat What we're waiting for.
+ */
+static void supR3HardNtChildWaitFor(PSUPR3HARDNTCHILD pThis, SUPR3WINCHILDREQ enmExpectedRequest, RTMSINTERVAL cMsTimeout,
+ const char *pszWhat)
+{
+ /*
+ * The wait loop.
+ * Will return when the expected request arrives.
+ * Will break out when one of the processes terminates.
+ */
+ NTSTATUS rcNtWait;
+ LARGE_INTEGER Timeout;
+ uint64_t uMsTsStart = supR3HardenedWinGetMilliTS();
+ uint64_t cMsElapsed = 0;
+ for (;;)
+ {
+ /*
+ * Assemble handles to wait for.
+ */
+ ULONG cHandles = 1;
+ HANDLE ahHandles[3];
+ ahHandles[0] = pThis->hProcess;
+ if (pThis->hEvtParent)
+ ahHandles[cHandles++] = pThis->hEvtParent;
+ if (pThis->hParent)
+ ahHandles[cHandles++] = pThis->hParent;
+
+ /*
+ * Do the waiting according to the callers wishes.
+ */
+ if ( enmExpectedRequest == kSupR3WinChildReq_End
+ || cMsTimeout == RT_INDEFINITE_WAIT)
+ rcNtWait = NtWaitForMultipleObjects(cHandles, &ahHandles[0], WaitAnyObject, TRUE /*Alertable*/, NULL /*Timeout*/);
+ else
+ {
+ Timeout.QuadPart = -(int64_t)(cMsTimeout - cMsElapsed) * 10000;
+ rcNtWait = NtWaitForMultipleObjects(cHandles, &ahHandles[0], WaitAnyObject, TRUE /*Alertable*/, &Timeout);
+ }
+
+ /*
+ * Process child request.
+ */
+ if (rcNtWait == STATUS_WAIT_0 + 1 && pThis->hEvtParent != NULL)
+ {
+ supR3HardNtChildProcessRequest(pThis, enmExpectedRequest, pszWhat);
+ SUP_DPRINTF(("supR3HardNtChildWaitFor: Found expected request %d (%s) after %llu ms.\n",
+ enmExpectedRequest, pszWhat, supR3HardenedWinGetMilliTS() - uMsTsStart));
+ return; /* Expected request received. */
+ }
+
+ /*
+ * Process termination?
+ */
+ if ( (ULONG)rcNtWait - (ULONG)STATUS_WAIT_0 < cHandles
+ || (ULONG)rcNtWait - (ULONG)STATUS_ABANDONED_WAIT_0 < cHandles)
+ break;
+
+ /*
+ * Check sanity.
+ */
+ if ( rcNtWait != STATUS_TIMEOUT
+ && rcNtWait != STATUS_USER_APC
+ && rcNtWait != STATUS_ALERTED)
+ supR3HardenedWinKillChild(pThis, "supR3HardNtChildWaitFor", rcNtWait,
+ "NtWaitForMultipleObjects returned %#x waiting for #%d (%s)\n",
+ rcNtWait, enmExpectedRequest, pszWhat);
+
+ /*
+ * Calc elapsed time for the next timeout calculation, checking to see
+ * if we've timed out already.
+ */
+ cMsElapsed = supR3HardenedWinGetMilliTS() - uMsTsStart;
+ if ( cMsElapsed > cMsTimeout
+ && cMsTimeout != RT_INDEFINITE_WAIT
+ && enmExpectedRequest != kSupR3WinChildReq_End)
+ {
+ if (rcNtWait == STATUS_USER_APC || rcNtWait == STATUS_ALERTED)
+ cMsElapsed = cMsTimeout - 1; /* try again */
+ else
+ {
+ /* We timed out. */
+ supR3HardenedWinKillChild(pThis, "supR3HardNtChildWaitFor", rcNtWait,
+ "Timed out after %llu ms waiting for child request #%d (%s).\n",
+ cMsElapsed, enmExpectedRequest, pszWhat);
+ }
+ }
+ }
+
+ /*
+ * Proxy the termination code of the child, if it exited already.
+ */
+ PROCESS_BASIC_INFORMATION BasicInfo;
+ NTSTATUS rcNt1 = NtQueryInformationProcess(pThis->hProcess, ProcessBasicInformation, &BasicInfo, sizeof(BasicInfo), NULL);
+ NTSTATUS rcNt2 = STATUS_PENDING;
+ NTSTATUS rcNt3 = STATUS_PENDING;
+ if ( !NT_SUCCESS(rcNt1)
+ || BasicInfo.ExitStatus == STATUS_PENDING)
+ {
+ rcNt2 = NtTerminateProcess(pThis->hProcess, RTEXITCODE_FAILURE);
+ Timeout.QuadPart = NT_SUCCESS(rcNt2) ? -20000000 /* 2 sec */ : -1280000 /* 128 ms */;
+ rcNt3 = NtWaitForSingleObject(pThis->hProcess, FALSE /*Alertable*/, NULL /*Timeout*/);
+ BasicInfo.ExitStatus = RTEXITCODE_FAILURE;
+ }
+
+ SUP_DPRINTF(("supR3HardNtChildWaitFor[%d]: Quitting: ExitCode=%#x (rcNtWait=%#x, rcNt1=%#x, rcNt2=%#x, rcNt3=%#x, %llu ms, %s);\n",
+ pThis->iWhich, BasicInfo.ExitStatus, rcNtWait, rcNt1, rcNt2, rcNt3,
+ supR3HardenedWinGetMilliTS() - uMsTsStart, pszWhat));
+ suplibHardenedExit((RTEXITCODE)BasicInfo.ExitStatus);
+}
+
+
+/**
+ * Closes full access child thread and process handles, making a harmless
+ * duplicate of the process handle first.
+ *
+ * The hProcess member of the child process data structure will be change to the
+ * harmless handle, while the hThread will be set to NULL.
+ *
+ * @param pThis The child process data structure.
+ */
+static void supR3HardNtChildCloseFullAccessHandles(PSUPR3HARDNTCHILD pThis)
+{
+ /*
+ * The thread handle.
+ */
+ NTSTATUS rcNt = NtClose(pThis->hThread);
+ if (!NT_SUCCESS(rcNt))
+ supR3HardenedWinKillChild(pThis, "supR3HardenedWinReSpawn", rcNt, "NtClose(hThread) failed: %#x", rcNt);
+ pThis->hThread = NULL;
+
+ /*
+ * Duplicate the process handle into a harmless one.
+ */
+ HANDLE hProcWait;
+ ULONG fRights = SYNCHRONIZE | PROCESS_TERMINATE | PROCESS_VM_READ;
+ if (g_uNtVerCombined >= SUP_MAKE_NT_VER_SIMPLE(6, 0)) /* Introduced in Vista. */
+ fRights |= PROCESS_QUERY_LIMITED_INFORMATION;
+ else
+ fRights |= PROCESS_QUERY_INFORMATION;
+ rcNt = NtDuplicateObject(NtCurrentProcess(), pThis->hProcess,
+ NtCurrentProcess(), &hProcWait,
+ fRights, 0 /*HandleAttributes*/, 0);
+ if (rcNt == STATUS_ACCESS_DENIED)
+ {
+ supR3HardenedError(rcNt, false /*fFatal*/,
+ "supR3HardenedWinDoReSpawn: NtDuplicateObject(,,,,%#x,,) -> %#x, retrying with only %#x...\n",
+ fRights, rcNt, SYNCHRONIZE);
+ rcNt = NtDuplicateObject(NtCurrentProcess(), pThis->hProcess,
+ NtCurrentProcess(), &hProcWait,
+ SYNCHRONIZE, 0 /*HandleAttributes*/, 0);
+ }
+ if (!NT_SUCCESS(rcNt))
+ supR3HardenedWinKillChild(pThis, "supR3HardenedWinReSpawn", rcNt,
+ "NtDuplicateObject failed on child process handle: %#x\n", rcNt);
+ /*
+ * Close the process handle and replace it with the harmless one.
+ */
+ rcNt = NtClose(pThis->hProcess);
+ pThis->hProcess = hProcWait;
+ if (!NT_SUCCESS(rcNt))
+ supR3HardenedWinKillChild(pThis, "supR3HardenedWinReSpawn", VERR_INVALID_NAME,
+ "NtClose failed on child process handle: %#x\n", rcNt);
+}
+
+
+/**
+ * This restores the child PEB and tweaks a couple of fields before we do the
+ * child purification and let the process run normally.
+ *
+ * @param pThis The child process data structure.
+ */
+static void supR3HardNtChildSanitizePeb(PSUPR3HARDNTCHILD pThis)
+{
+ /*
+ * Make a copy of the pre-execution PEB.
+ */
+ PEB Peb = pThis->Peb;
+
+#if 0
+ /*
+ * There should not be any activation context, so if there is, we scratch the memory associated with it.
+ */
+ int rc = 0;
+ if (RT_SUCCESS(rc) && Peb.pShimData && !((uintptr_t)Peb.pShimData & PAGE_OFFSET_MASK))
+ rc = supR3HardenedWinScratchChildMemory(hProcess, Peb.pShimData, PAGE_SIZE, "pShimData", pErrInfo);
+ if (RT_SUCCESS(rc) && Peb.ActivationContextData && !((uintptr_t)Peb.ActivationContextData & PAGE_OFFSET_MASK))
+ rc = supR3HardenedWinScratchChildMemory(hProcess, Peb.ActivationContextData, PAGE_SIZE, "ActivationContextData", pErrInfo);
+ if (RT_SUCCESS(rc) && Peb.ProcessAssemblyStorageMap && !((uintptr_t)Peb.ProcessAssemblyStorageMap & PAGE_OFFSET_MASK))
+ rc = supR3HardenedWinScratchChildMemory(hProcess, Peb.ProcessAssemblyStorageMap, PAGE_SIZE, "ProcessAssemblyStorageMap", pErrInfo);
+ if (RT_SUCCESS(rc) && Peb.SystemDefaultActivationContextData && !((uintptr_t)Peb.SystemDefaultActivationContextData & PAGE_OFFSET_MASK))
+ rc = supR3HardenedWinScratchChildMemory(hProcess, Peb.ProcessAssemblyStorageMap, PAGE_SIZE, "SystemDefaultActivationContextData", pErrInfo);
+ if (RT_SUCCESS(rc) && Peb.SystemAssemblyStorageMap && !((uintptr_t)Peb.SystemAssemblyStorageMap & PAGE_OFFSET_MASK))
+ rc = supR3HardenedWinScratchChildMemory(hProcess, Peb.SystemAssemblyStorageMap, PAGE_SIZE, "SystemAssemblyStorageMap", pErrInfo);
+ if (RT_FAILURE(rc))
+ return rc;
+#endif
+
+ /*
+ * Clear compatibility and activation related fields.
+ */
+ Peb.AppCompatFlags.QuadPart = 0;
+ Peb.AppCompatFlagsUser.QuadPart = 0;
+ Peb.pShimData = NULL;
+ Peb.AppCompatInfo = NULL;
+#if 0
+ Peb.ActivationContextData = NULL;
+ Peb.ProcessAssemblyStorageMap = NULL;
+ Peb.SystemDefaultActivationContextData = NULL;
+ Peb.SystemAssemblyStorageMap = NULL;
+ /*Peb.Diff0.W6.IsProtectedProcess = 1;*/
+#endif
+
+ /*
+ * Write back the PEB.
+ */
+ SIZE_T cbActualMem = pThis->cbPeb;
+ NTSTATUS rcNt = NtWriteVirtualMemory(pThis->hProcess, pThis->BasicInfo.PebBaseAddress, &Peb, pThis->cbPeb, &cbActualMem);
+ if (!NT_SUCCESS(rcNt))
+ supR3HardenedWinKillChild(pThis, "supR3HardNtChildSanitizePeb", rcNt,
+ "NtWriteVirtualMemory/Peb failed: %#x", rcNt);
+
+}
+
+
+/**
+ * Purifies the child process after very early init has been performed.
+ *
+ * @param pThis The child process data structure.
+ */
+static void supR3HardNtChildPurify(PSUPR3HARDNTCHILD pThis)
+{
+ /*
+ * We loop until we no longer make any fixes. This is similar to what
+ * we do (or used to do, really) in the fAvastKludge case of
+ * supR3HardenedWinInit. We might be up against asynchronous changes,
+ * which we fudge by waiting a short while before earch purification. This
+ * is arguably a fragile technique, but it's currently the best we've got.
+ * Fortunately, most AVs seems to either favor immediate action on initial
+ * load events or (much better for us) later events like kernel32.
+ */
+ uint64_t uMsTsOuterStart = supR3HardenedWinGetMilliTS();
+ uint32_t cMsFudge = g_fSupAdversaries ? 512 : 256;
+ uint32_t cTotalFixes = 0;
+ uint32_t cFixes = 0; /* (MSC wrongly thinks this maybe used uninitialized) */
+ for (uint32_t iLoop = 0; iLoop < 16; iLoop++)
+ {
+ /*
+ * Delay.
+ */
+ uint32_t cSleeps = 0;
+ uint64_t uMsTsStart = supR3HardenedWinGetMilliTS();
+ do
+ {
+ NtYieldExecution();
+ LARGE_INTEGER Time;
+ Time.QuadPart = -8000000 / 100; /* 8ms in 100ns units, relative time. */
+ NtDelayExecution(FALSE, &Time);
+ cSleeps++;
+ } while ( supR3HardenedWinGetMilliTS() - uMsTsStart <= cMsFudge
+ || cSleeps < 8);
+ SUP_DPRINTF(("supR3HardNtChildPurify: Startup delay kludge #1/%u: %u ms, %u sleeps\n",
+ iLoop, supR3HardenedWinGetMilliTS() - uMsTsStart, cSleeps));
+
+ /*
+ * Purify.
+ */
+ cFixes = 0;
+ int rc = supHardenedWinVerifyProcess(pThis->hProcess, pThis->hThread, SUPHARDNTVPKIND_CHILD_PURIFICATION,
+ g_fSupAdversaries & ( SUPHARDNT_ADVERSARY_TRENDMICRO_SAKFILE
+ | SUPHARDNT_ADVERSARY_DIGITAL_GUARDIAN_OLD)
+ ? SUPHARDNTVP_F_EXEC_ALLOC_REPLACE_WITH_RW : 0,
+ &cFixes, RTErrInfoInitStatic(&g_ErrInfoStatic));
+ if (RT_FAILURE(rc))
+ supR3HardenedWinKillChild(pThis, "supR3HardNtChildPurify", rc,
+ "supHardenedWinVerifyProcess failed with %Rrc: %s", rc, g_ErrInfoStatic.szMsg);
+ if (cFixes == 0)
+ {
+ SUP_DPRINTF(("supR3HardNtChildPurify: Done after %llu ms and %u fixes (loop #%u).\n",
+ supR3HardenedWinGetMilliTS() - uMsTsOuterStart, cTotalFixes, iLoop));
+ return; /* We're probably good. */
+ }
+ cTotalFixes += cFixes;
+
+ if (!g_fSupAdversaries)
+ g_fSupAdversaries |= SUPHARDNT_ADVERSARY_UNKNOWN;
+ cMsFudge = 512;
+
+ /*
+ * Log the KiOpPrefetchPatchCount value if available, hoping it might
+ * sched some light on spider38's case.
+ */
+ ULONG cPatchCount = 0;
+ NTSTATUS rcNt = NtQuerySystemInformation(SystemInformation_KiOpPrefetchPatchCount,
+ &cPatchCount, sizeof(cPatchCount), NULL);
+ if (NT_SUCCESS(rcNt))
+ SUP_DPRINTF(("supR3HardNtChildPurify: cFixes=%u g_fSupAdversaries=%#x cPatchCount=%#u\n",
+ cFixes, g_fSupAdversaries, cPatchCount));
+ else
+ SUP_DPRINTF(("supR3HardNtChildPurify: cFixes=%u g_fSupAdversaries=%#x\n", cFixes, g_fSupAdversaries));
+ }
+
+ /*
+ * We've given up fixing the child process. Probably fighting someone
+ * that monitors their patches or/and our activities.
+ */
+ supR3HardenedWinKillChild(pThis, "supR3HardNtChildPurify", VERR_TRY_AGAIN,
+ "Unable to purify child process! After 16 tries over %llu ms, we still %u fix(es) in the last pass.",
+ supR3HardenedWinGetMilliTS() - uMsTsOuterStart, cFixes);
+}
+
+
+/**
+ * Sets up the early process init.
+ *
+ * @param pThis The child process data structure.
+ */
+static void supR3HardNtChildSetUpChildInit(PSUPR3HARDNTCHILD pThis)
+{
+ uintptr_t const uChildExeAddr = (uintptr_t)pThis->Peb.ImageBaseAddress;
+
+ /*
+ * Plant the process parameters. This ASSUMES the handle inheritance is
+ * performed when creating the child process.
+ */
+ RT_ZERO(pThis->ProcParams);
+ pThis->ProcParams.hEvtChild = pThis->hEvtChild;
+ pThis->ProcParams.hEvtParent = pThis->hEvtParent;
+ pThis->ProcParams.uNtDllAddr = pThis->uNtDllAddr;
+ pThis->ProcParams.enmRequest = kSupR3WinChildReq_Error;
+ pThis->ProcParams.rc = VINF_SUCCESS;
+
+ uintptr_t uChildAddr = uChildExeAddr + ((uintptr_t)&g_ProcParams - (uintptr_t)NtCurrentPeb()->ImageBaseAddress);
+ SIZE_T cbIgnored;
+ NTSTATUS rcNt = NtWriteVirtualMemory(pThis->hProcess, (PVOID)uChildAddr, &pThis->ProcParams,
+ sizeof(pThis->ProcParams), &cbIgnored);
+ if (!NT_SUCCESS(rcNt))
+ supR3HardenedWinKillChild(pThis, "supR3HardenedWinSetupChildInit", rcNt,
+ "NtWriteVirtualMemory(,%p,) failed writing child process parameters: %#x\n", uChildAddr, rcNt);
+
+ /*
+ * Locate the LdrInitializeThunk address in the child as well as pristine
+ * code bits for it.
+ */
+ PSUPHNTLDRCACHEENTRY pLdrEntry;
+ int rc = supHardNtLdrCacheOpen("ntdll.dll", &pLdrEntry, NULL /*pErrInfo*/);
+ if (RT_FAILURE(rc))
+ supR3HardenedWinKillChild(pThis, "supR3HardenedWinSetupChildInit", rc,
+ "supHardNtLdrCacheOpen failed on NTDLL: %Rrc\n", rc);
+
+ uint8_t *pbChildNtDllBits;
+ rc = supHardNtLdrCacheEntryGetBits(pLdrEntry, &pbChildNtDllBits, pThis->uNtDllAddr, NULL, NULL, NULL /*pErrInfo*/);
+ if (RT_FAILURE(rc))
+ supR3HardenedWinKillChild(pThis, "supR3HardenedWinSetupChildInit", rc,
+ "supHardNtLdrCacheEntryGetBits failed on NTDLL: %Rrc\n", rc);
+
+ RTLDRADDR uLdrInitThunk;
+ rc = RTLdrGetSymbolEx(pLdrEntry->hLdrMod, pbChildNtDllBits, pThis->uNtDllAddr, UINT32_MAX,
+ "LdrInitializeThunk", &uLdrInitThunk);
+ if (RT_FAILURE(rc))
+ supR3HardenedWinKillChild(pThis, "supR3HardenedWinSetupChildInit", rc,
+ "Error locating LdrInitializeThunk in NTDLL: %Rrc", rc);
+ PVOID pvLdrInitThunk = (PVOID)(uintptr_t)uLdrInitThunk;
+ SUP_DPRINTF(("supR3HardenedWinSetupChildInit: uLdrInitThunk=%p\n", (uintptr_t)uLdrInitThunk));
+
+ /*
+ * Calculate the address of our code in the child process.
+ */
+ uintptr_t uEarlyProcInitEP = uChildExeAddr + ( (uintptr_t)&supR3HardenedEarlyProcessInitThunk
+ - (uintptr_t)NtCurrentPeb()->ImageBaseAddress);
+
+ /*
+ * Compose the LdrInitializeThunk replacement bytes.
+ * Note! The amount of code we replace here must be less or equal to what
+ * the process verification code ignores.
+ */
+ uint8_t abNew[16];
+ memcpy(abNew, pbChildNtDllBits + ((uintptr_t)uLdrInitThunk - pThis->uNtDllAddr), sizeof(abNew));
+#ifdef RT_ARCH_AMD64
+ abNew[0] = 0xff;
+ abNew[1] = 0x25;
+ *(uint32_t *)&abNew[2] = 0;
+ *(uint64_t *)&abNew[6] = uEarlyProcInitEP;
+#elif defined(RT_ARCH_X86)
+ abNew[0] = 0xe9;
+ *(uint32_t *)&abNew[1] = uEarlyProcInitEP - ((uint32_t)uLdrInitThunk + 5);
+#else
+# error "Unsupported arch."
+#endif
+
+ /*
+ * Install the LdrInitializeThunk replacement code in the child process.
+ */
+ PVOID pvProt = pvLdrInitThunk;
+ SIZE_T cbProt = sizeof(abNew);
+ ULONG fOldProt;
+ rcNt = NtProtectVirtualMemory(pThis->hProcess, &pvProt, &cbProt, PAGE_EXECUTE_READWRITE, &fOldProt);
+ if (!NT_SUCCESS(rcNt))
+ supR3HardenedWinKillChild(pThis, "supR3HardenedWinSetupChildInit", rcNt,
+ "NtProtectVirtualMemory/LdrInitializeThunk failed: %#x", rcNt);
+
+ rcNt = NtWriteVirtualMemory(pThis->hProcess, pvLdrInitThunk, abNew, sizeof(abNew), &cbIgnored);
+ if (!NT_SUCCESS(rcNt))
+ supR3HardenedWinKillChild(pThis, "supR3HardenedWinSetupChildInit", rcNt,
+ "NtWriteVirtualMemory/LdrInitializeThunk failed: %#x", rcNt);
+
+ pvProt = pvLdrInitThunk;
+ cbProt = sizeof(abNew);
+ rcNt = NtProtectVirtualMemory(pThis->hProcess, &pvProt, &cbProt, fOldProt, &fOldProt);
+ if (!NT_SUCCESS(rcNt))
+ supR3HardenedWinKillChild(pThis, "supR3HardenedWinSetupChildInit", rcNt,
+ "NtProtectVirtualMemory/LdrInitializeThunk[restore] failed: %#x", rcNt);
+
+ /*
+ * Check the sanity of the thread context.
+ */
+ CONTEXT Ctx;
+ RT_ZERO(Ctx);
+ Ctx.ContextFlags = CONTEXT_FULL | CONTEXT_DEBUG_REGISTERS;
+ rcNt = NtGetContextThread(pThis->hThread, &Ctx);
+ if (NT_SUCCESS(rcNt))
+ {
+#ifdef RT_ARCH_AMD64
+ DWORD64 *pPC = &Ctx.Rip;
+#elif defined(RT_ARCH_X86)
+ DWORD *pPC = &Ctx.Eip;
+#else
+# error "Unsupported arch."
+#endif
+ supR3HardNtDprintCtx(&Ctx, "supR3HardenedWinSetupChildInit: Initial context:");
+
+ /* Entrypoint for the executable: */
+ uintptr_t const uChildMain = uChildExeAddr + ( (uintptr_t)&suplibHardenedWindowsMain
+ - (uintptr_t)NtCurrentPeb()->ImageBaseAddress);
+
+ /* NtDll size and the more recent default thread start entrypoint (Vista+?): */
+ RTLDRADDR uSystemThreadStart;
+ rc = RTLdrGetSymbolEx(pLdrEntry->hLdrMod, pbChildNtDllBits, pThis->uNtDllAddr, UINT32_MAX,
+ "RtlUserThreadStart", &uSystemThreadStart);
+ if (RT_FAILURE(rc))
+ uSystemThreadStart = 0;
+
+ /* Kernel32 for thread start of older windows version, only XP64/W2K3-64 has an actual
+ export for it. Unfortunately, it is not yet loaded into the child, so we have to
+ assume same location as in the parent (safe): */
+ PSUPHNTLDRCACHEENTRY pLdrEntryKernel32;
+ rc = supHardNtLdrCacheOpen("kernel32.dll", &pLdrEntryKernel32, NULL /*pErrInfo*/);
+ if (RT_FAILURE(rc))
+ supR3HardenedWinKillChild(pThis, "supR3HardenedWinSetupChildInit", rc,
+ "supHardNtLdrCacheOpen failed on KERNEL32: %Rrc\n", rc);
+ size_t const cbKernel32 = RTLdrSize(pLdrEntryKernel32->hLdrMod);
+
+#ifdef RT_ARCH_AMD64
+ if (!uSystemThreadStart)
+ {
+ rc = RTLdrGetSymbolEx(pLdrEntry->hLdrMod, pbChildNtDllBits, pLdrEntryKernel32->uImageBase, UINT32_MAX,
+ "BaseProcessStart", &uSystemThreadStart);
+ if (RT_FAILURE(rc))
+ uSystemThreadStart = 0;
+ }
+#endif
+
+ bool fUpdateContext = false;
+
+ /* Check if the RIP looks half sane, try correct it if it isn't.
+ It should point to RtlUserThreadStart (Vista and later it seem), though only
+ tested on win10. The first parameter is the executable entrypoint, the 2nd
+ is probably the PEB. Before Vista it should point to Kernel32!BaseProcessStart,
+ though the symbol is only exported in 5.2/AMD64. */
+ if ( ( uSystemThreadStart
+ ? *pPC == uSystemThreadStart
+ : *pPC - ( pLdrEntryKernel32->uImageBase != ~(uintptr_t)0 ? pLdrEntryKernel32->uImageBase
+ : (uintptr_t)GetModuleHandleW(L"kernel32.dll")) <= cbKernel32)
+ || *pPC == uChildMain)
+ { }
+ else
+ {
+ SUP_DPRINTF(("Warning! Bogus RIP: %p (uSystemThreadStart=%p; kernel32 %p LB %p; uChildMain=%p)\n",
+ *pPC, uSystemThreadStart, pLdrEntryKernel32->uImageBase, cbKernel32, uChildMain));
+ if (uSystemThreadStart)
+ {
+ SUP_DPRINTF(("Correcting RIP from to %p hoping that it might work...\n", (uintptr_t)uSystemThreadStart));
+ *pPC = uSystemThreadStart;
+ fUpdateContext = true;
+ }
+ }
+#ifdef RT_ARCH_AMD64
+ if (g_uNtVerCombined >= SUP_MAKE_NT_VER_SIMPLE(10, 0)) /* W2K3: CS=33 SS=DS=ES=GS=2b FS=53 */
+ {
+ if (Ctx.SegDs != 0)
+ SUP_DPRINTF(("Warning! Bogus DS: %04x, expected zero\n", Ctx.SegDs));
+ if (Ctx.SegEs != 0)
+ SUP_DPRINTF(("Warning! Bogus ES: %04x, expected zero\n", Ctx.SegEs));
+ if (Ctx.SegFs != 0)
+ SUP_DPRINTF(("Warning! Bogus FS: %04x, expected zero\n", Ctx.SegFs));
+ if (Ctx.SegGs != 0)
+ SUP_DPRINTF(("Warning! Bogus GS: %04x, expected zero\n", Ctx.SegGs));
+ }
+ if (Ctx.Rcx != uChildMain)
+ SUP_DPRINTF(("Warning! Bogus RCX: %016RX64, expected %016RX64\n", Ctx.Rcx, uChildMain));
+ if (Ctx.Rdx & PAGE_OFFSET_MASK)
+ SUP_DPRINTF(("Warning! Bogus RDX: %016RX64, expected page aligned\n", Ctx.Rdx)); /* PEB */
+ if ((Ctx.Rsp & 15) != 8)
+ SUP_DPRINTF(("Warning! Misaligned RSP: %016RX64\n", Ctx.Rsp));
+#endif
+ if (Ctx.SegCs != ASMGetCS())
+ SUP_DPRINTF(("Warning! Bogus CS: %04x, expected %04x\n", Ctx.SegCs, ASMGetCS()));
+ if (Ctx.SegSs != ASMGetSS())
+ SUP_DPRINTF(("Warning! Bogus SS: %04x, expected %04x\n", Ctx.SegSs, ASMGetSS()));
+ if (Ctx.Dr0 != 0)
+ SUP_DPRINTF(("Warning! Bogus DR0: %016RX64, expected zero\n", Ctx.Dr0));
+ if (Ctx.Dr1 != 0)
+ SUP_DPRINTF(("Warning! Bogus DR1: %016RX64, expected zero\n", Ctx.Dr1));
+ if (Ctx.Dr2 != 0)
+ SUP_DPRINTF(("Warning! Bogus DR2: %016RX64, expected zero\n", Ctx.Dr2));
+ if (Ctx.Dr3 != 0)
+ SUP_DPRINTF(("Warning! Bogus DR3: %016RX64, expected zero\n", Ctx.Dr3));
+ if (Ctx.Dr6 != 0)
+ SUP_DPRINTF(("Warning! Bogus DR6: %016RX64, expected zero\n", Ctx.Dr6));
+ if (Ctx.Dr7 != 0)
+ {
+ SUP_DPRINTF(("Warning! Bogus DR7: %016RX64, expected zero\n", Ctx.Dr7));
+ Ctx.Dr7 = 0;
+ fUpdateContext = true;
+ }
+
+ if (fUpdateContext)
+ {
+ rcNt = NtSetContextThread(pThis->hThread, &Ctx);
+ if (!NT_SUCCESS(rcNt))
+ SUP_DPRINTF(("Error! NtSetContextThread failed: %#x\n", rcNt));
+ }
+ }
+
+ /* Caller starts child execution. */
+ SUP_DPRINTF(("supR3HardenedWinSetupChildInit: Start child.\n"));
+}
+
+
+
+/**
+ * This messes with the child PEB before we trigger the initial image events.
+ *
+ * @param pThis The child process data structure.
+ */
+static void supR3HardNtChildScrewUpPebForInitialImageEvents(PSUPR3HARDNTCHILD pThis)
+{
+ /*
+ * Not sure if any of the cracker software uses the PEB at this point, but
+ * just in case they do make some of the PEB fields a little less useful.
+ */
+ PEB Peb = pThis->Peb;
+
+ /* Make ImageBaseAddress useless. */
+ Peb.ImageBaseAddress = (PVOID)((uintptr_t)Peb.ImageBaseAddress ^ UINT32_C(0x5f139000));
+#ifdef RT_ARCH_AMD64
+ Peb.ImageBaseAddress = (PVOID)((uintptr_t)Peb.ImageBaseAddress | UINT64_C(0x0313000000000000));
+#endif
+
+ /*
+ * Write the PEB.
+ */
+ SIZE_T cbActualMem = pThis->cbPeb;
+ NTSTATUS rcNt = NtWriteVirtualMemory(pThis->hProcess, pThis->BasicInfo.PebBaseAddress, &Peb, pThis->cbPeb, &cbActualMem);
+ if (!NT_SUCCESS(rcNt))
+ supR3HardenedWinKillChild(pThis, "supR3HardNtChildScrewUpPebForInitialImageEvents", rcNt,
+ "NtWriteVirtualMemory/Peb failed: %#x", rcNt);
+}
+
+
+/**
+ * Check if the zero terminated NT unicode string is the path to the given
+ * system32 DLL.
+ *
+ * @returns true if it is, false if not.
+ * @param pUniStr The zero terminated NT unicode string path.
+ * @param pszName The name of the system32 DLL.
+ */
+static bool supR3HardNtIsNamedSystem32Dll(PUNICODE_STRING pUniStr, const char *pszName)
+{
+ if (pUniStr->Length > g_System32NtPath.UniStr.Length)
+ {
+ if (memcmp(pUniStr->Buffer, g_System32NtPath.UniStr.Buffer, g_System32NtPath.UniStr.Length) == 0)
+ {
+ if (pUniStr->Buffer[g_System32NtPath.UniStr.Length / sizeof(WCHAR)] == '\\')
+ {
+ if (RTUtf16ICmpAscii(&pUniStr->Buffer[g_System32NtPath.UniStr.Length / sizeof(WCHAR) + 1], pszName) == 0)
+ return true;
+ }
+ }
+ }
+
+ return false;
+}
+
+
+/**
+ * Worker for supR3HardNtChildGatherData that locates NTDLL in the child
+ * process.
+ *
+ * @param pThis The child process data structure.
+ */
+static void supR3HardNtChildFindNtdll(PSUPR3HARDNTCHILD pThis)
+{
+ /*
+ * Find NTDLL in this process first and take that as a starting point.
+ */
+ pThis->uNtDllParentAddr = (uintptr_t)GetModuleHandleW(L"ntdll.dll");
+ SUPR3HARDENED_ASSERT(pThis->uNtDllParentAddr != 0 && !(pThis->uNtDllParentAddr & PAGE_OFFSET_MASK));
+ pThis->uNtDllAddr = pThis->uNtDllParentAddr;
+
+ /*
+ * Scan the virtual memory of the child.
+ */
+ uintptr_t cbAdvance = 0;
+ uintptr_t uPtrWhere = 0;
+ for (uint32_t i = 0; i < 1024; i++)
+ {
+ /* Query information. */
+ SIZE_T cbActual = 0;
+ MEMORY_BASIC_INFORMATION MemInfo = { 0, 0, 0, 0, 0, 0, 0 };
+ NTSTATUS rcNt = NtQueryVirtualMemory(pThis->hProcess,
+ (void const *)uPtrWhere,
+ MemoryBasicInformation,
+ &MemInfo,
+ sizeof(MemInfo),
+ &cbActual);
+ if (!NT_SUCCESS(rcNt))
+ break;
+
+ if ( MemInfo.Type == SEC_IMAGE
+ || MemInfo.Type == SEC_PROTECTED_IMAGE
+ || MemInfo.Type == (SEC_IMAGE | SEC_PROTECTED_IMAGE))
+ {
+ if (MemInfo.BaseAddress == MemInfo.AllocationBase)
+ {
+ /* Get the image name. */
+ union
+ {
+ UNICODE_STRING UniStr;
+ uint8_t abPadding[4096];
+ } uBuf;
+ rcNt = NtQueryVirtualMemory(pThis->hProcess,
+ MemInfo.BaseAddress,
+ MemorySectionName,
+ &uBuf,
+ sizeof(uBuf) - sizeof(WCHAR),
+ &cbActual);
+ if (NT_SUCCESS(rcNt))
+ {
+ uBuf.UniStr.Buffer[uBuf.UniStr.Length / sizeof(WCHAR)] = '\0';
+ if (supR3HardNtIsNamedSystem32Dll(&uBuf.UniStr, "ntdll.dll"))
+ {
+ pThis->uNtDllAddr = (uintptr_t)MemInfo.AllocationBase;
+ SUP_DPRINTF(("supR3HardNtPuChFindNtdll: uNtDllParentAddr=%p uNtDllChildAddr=%p\n",
+ pThis->uNtDllParentAddr, pThis->uNtDllAddr));
+ return;
+ }
+ }
+ }
+ }
+
+ /*
+ * Advance.
+ */
+ cbAdvance = MemInfo.RegionSize;
+ if (uPtrWhere + cbAdvance <= uPtrWhere)
+ break;
+ uPtrWhere += MemInfo.RegionSize;
+ }
+
+ supR3HardenedWinKillChild(pThis, "supR3HardNtChildFindNtdll", VERR_MODULE_NOT_FOUND, "ntdll.dll not found in child process.");
+}
+
+
+/**
+ * Gather child data.
+ *
+ * @param pThis The child process data structure.
+ */
+static void supR3HardNtChildGatherData(PSUPR3HARDNTCHILD pThis)
+{
+ /*
+ * Basic info.
+ */
+ ULONG cbActual = 0;
+ NTSTATUS rcNt = NtQueryInformationProcess(pThis->hProcess, ProcessBasicInformation,
+ &pThis->BasicInfo, sizeof(pThis->BasicInfo), &cbActual);
+ if (!NT_SUCCESS(rcNt))
+ supR3HardenedWinKillChild(pThis, "supR3HardNtChildGatherData", rcNt,
+ "NtQueryInformationProcess/ProcessBasicInformation failed: %#x", rcNt);
+
+ /*
+ * If this is the middle (stub) process, we wish to wait for both child
+ * and parent. So open the parent process. Not fatal if we cannnot.
+ */
+ if (pThis->iWhich > 1)
+ {
+ PROCESS_BASIC_INFORMATION SelfInfo;
+ rcNt = NtQueryInformationProcess(NtCurrentProcess(), ProcessBasicInformation, &SelfInfo, sizeof(SelfInfo), &cbActual);
+ if (NT_SUCCESS(rcNt))
+ {
+ OBJECT_ATTRIBUTES ObjAttr;
+ InitializeObjectAttributes(&ObjAttr, NULL, 0, NULL /*hRootDir*/, NULL /*pSecDesc*/);
+
+ CLIENT_ID ClientId;
+ ClientId.UniqueProcess = (HANDLE)SelfInfo.InheritedFromUniqueProcessId;
+ ClientId.UniqueThread = NULL;
+
+ rcNt = NtOpenProcess(&pThis->hParent, SYNCHRONIZE | PROCESS_QUERY_INFORMATION, &ObjAttr, &ClientId);
+#ifdef DEBUG
+ SUPR3HARDENED_ASSERT_NT_SUCCESS(rcNt);
+#endif
+ if (!NT_SUCCESS(rcNt))
+ {
+ pThis->hParent = NULL;
+ SUP_DPRINTF(("supR3HardNtChildGatherData: Failed to open parent process (%#p): %#x\n", ClientId.UniqueProcess, rcNt));
+ }
+ }
+
+ }
+
+ /*
+ * Process environment block.
+ */
+ if (g_uNtVerCombined < SUP_NT_VER_W2K3)
+ pThis->cbPeb = PEB_SIZE_W51;
+ else if (g_uNtVerCombined < SUP_NT_VER_VISTA)
+ pThis->cbPeb = PEB_SIZE_W52;
+ else if (g_uNtVerCombined < SUP_NT_VER_W70)
+ pThis->cbPeb = PEB_SIZE_W6;
+ else if (g_uNtVerCombined < SUP_NT_VER_W80)
+ pThis->cbPeb = PEB_SIZE_W7;
+ else if (g_uNtVerCombined < SUP_NT_VER_W81)
+ pThis->cbPeb = PEB_SIZE_W80;
+ else
+ pThis->cbPeb = PEB_SIZE_W81;
+
+ SUP_DPRINTF(("supR3HardNtChildGatherData: PebBaseAddress=%p cbPeb=%#x\n",
+ pThis->BasicInfo.PebBaseAddress, pThis->cbPeb));
+
+ SIZE_T cbActualMem;
+ RT_ZERO(pThis->Peb);
+ rcNt = NtReadVirtualMemory(pThis->hProcess, pThis->BasicInfo.PebBaseAddress, &pThis->Peb, sizeof(pThis->Peb), &cbActualMem);
+ if (!NT_SUCCESS(rcNt))
+ supR3HardenedWinKillChild(pThis, "supR3HardNtChildGatherData", rcNt,
+ "NtReadVirtualMemory/Peb failed: %#x", rcNt);
+
+ /*
+ * Locate NtDll.
+ */
+ supR3HardNtChildFindNtdll(pThis);
+}
+
+
+/**
+ * Does the actually respawning.
+ *
+ * @returns Never, will call exit or raise fatal error.
+ * @param iWhich Which respawn we're to check for, 1 being the
+ * first one, and 2 the second and final.
+ */
+static DECL_NO_RETURN(void) supR3HardenedWinDoReSpawn(int iWhich)
+{
+ NTSTATUS rcNt;
+ PPEB pPeb = NtCurrentPeb();
+ PRTL_USER_PROCESS_PARAMETERS pParentProcParams = pPeb->ProcessParameters;
+
+ SUPR3HARDENED_ASSERT(g_cSuplibHardenedWindowsMainCalls == 1);
+
+ /*
+ * Init the child process data structure, creating the child communication
+ * event sempahores.
+ */
+ SUPR3HARDNTCHILD This;
+ RT_ZERO(This);
+ This.iWhich = iWhich;
+
+ OBJECT_ATTRIBUTES ObjAttrs;
+ This.hEvtChild = NULL;
+ InitializeObjectAttributes(&ObjAttrs, NULL /*pName*/, OBJ_INHERIT, NULL /*hRootDir*/, NULL /*pSecDesc*/);
+ SUPR3HARDENED_ASSERT_NT_SUCCESS(NtCreateEvent(&This.hEvtChild, EVENT_ALL_ACCESS, &ObjAttrs, SynchronizationEvent, FALSE));
+
+ This.hEvtParent = NULL;
+ InitializeObjectAttributes(&ObjAttrs, NULL /*pName*/, OBJ_INHERIT, NULL /*hRootDir*/, NULL /*pSecDesc*/);
+ SUPR3HARDENED_ASSERT_NT_SUCCESS(NtCreateEvent(&This.hEvtParent, EVENT_ALL_ACCESS, &ObjAttrs, SynchronizationEvent, FALSE));
+
+ /*
+ * Set up security descriptors.
+ */
+ SECURITY_ATTRIBUTES ProcessSecAttrs;
+ MYSECURITYCLEANUP ProcessSecAttrsCleanup;
+ supR3HardNtChildInitSecAttrs(&ProcessSecAttrs, &ProcessSecAttrsCleanup, true /*fProcess*/);
+
+ SECURITY_ATTRIBUTES ThreadSecAttrs;
+ MYSECURITYCLEANUP ThreadSecAttrsCleanup;
+ supR3HardNtChildInitSecAttrs(&ThreadSecAttrs, &ThreadSecAttrsCleanup, false /*fProcess*/);
+
+#if 1
+ /*
+ * Configure the startup info and creation flags.
+ */
+ DWORD dwCreationFlags = CREATE_SUSPENDED;
+
+ STARTUPINFOEXW SiEx;
+ suplibHardenedMemSet(&SiEx, 0, sizeof(SiEx));
+ if (1)
+ SiEx.StartupInfo.cb = sizeof(SiEx.StartupInfo);
+ else
+ {
+ SiEx.StartupInfo.cb = sizeof(SiEx);
+ dwCreationFlags |= EXTENDED_STARTUPINFO_PRESENT;
+ /** @todo experiment with protected process stuff later on. */
+ }
+
+ SiEx.StartupInfo.dwFlags |= pParentProcParams->WindowFlags & STARTF_USESHOWWINDOW;
+ SiEx.StartupInfo.wShowWindow = (WORD)pParentProcParams->ShowWindowFlags;
+
+ SiEx.StartupInfo.dwFlags |= STARTF_USESTDHANDLES;
+ SiEx.StartupInfo.hStdInput = pParentProcParams->StandardInput;
+ SiEx.StartupInfo.hStdOutput = pParentProcParams->StandardOutput;
+ SiEx.StartupInfo.hStdError = pParentProcParams->StandardError;
+
+ /*
+ * Construct the command line and launch the process.
+ */
+ PRTUTF16 pwszCmdLine = supR3HardNtChildConstructCmdLine(NULL, iWhich);
+
+ supR3HardenedWinEnableThreadCreation();
+ PROCESS_INFORMATION ProcessInfoW32 = { NULL, NULL, 0, 0 };
+ if (!CreateProcessW(g_wszSupLibHardenedExePath,
+ pwszCmdLine,
+ &ProcessSecAttrs,
+ &ThreadSecAttrs,
+ TRUE /*fInheritHandles*/,
+ dwCreationFlags,
+ NULL /*pwszzEnvironment*/,
+ NULL /*pwszCurDir*/,
+ &SiEx.StartupInfo,
+ &ProcessInfoW32))
+ supR3HardenedFatalMsg("supR3HardenedWinReSpawn", kSupInitOp_Misc, VERR_INVALID_NAME,
+ "Error relaunching VirtualBox VM process: %u\n"
+ "Command line: '%ls'",
+ RtlGetLastWin32Error(), pwszCmdLine);
+ supR3HardenedWinDisableThreadCreation();
+
+ SUP_DPRINTF(("supR3HardenedWinDoReSpawn(%d): New child %x.%x [kernel32].\n",
+ iWhich, ProcessInfoW32.dwProcessId, ProcessInfoW32.dwThreadId));
+ This.hProcess = ProcessInfoW32.hProcess;
+ This.hThread = ProcessInfoW32.hThread;
+
+#else
+
+ /*
+ * Construct the process parameters.
+ */
+ UNICODE_STRING W32ImageName;
+ W32ImageName.Buffer = g_wszSupLibHardenedExePath; /* Yes the windows name for the process parameters. */
+ W32ImageName.Length = (USHORT)RTUtf16Len(g_wszSupLibHardenedExePath) * sizeof(WCHAR);
+ W32ImageName.MaximumLength = W32ImageName.Length + sizeof(WCHAR);
+
+ UNICODE_STRING CmdLine;
+ supR3HardNtChildConstructCmdLine(&CmdLine, iWhich);
+
+ PRTL_USER_PROCESS_PARAMETERS pProcParams = NULL;
+ SUPR3HARDENED_ASSERT_NT_SUCCESS(RtlCreateProcessParameters(&pProcParams,
+ &W32ImageName,
+ NULL /* DllPath - inherit from this process */,
+ NULL /* CurrentDirectory - inherit from this process */,
+ &CmdLine,
+ NULL /* Environment - inherit from this process */,
+ NULL /* WindowsTitle - none */,
+ NULL /* DesktopTitle - none. */,
+ NULL /* ShellInfo - none. */,
+ NULL /* RuntimeInfo - none (byte array for MSVCRT file info) */)
+ );
+
+ /** @todo this doesn't work. :-( */
+ pProcParams->ConsoleHandle = pParentProcParams->ConsoleHandle;
+ pProcParams->ConsoleFlags = pParentProcParams->ConsoleFlags;
+ pProcParams->StandardInput = pParentProcParams->StandardInput;
+ pProcParams->StandardOutput = pParentProcParams->StandardOutput;
+ pProcParams->StandardError = pParentProcParams->StandardError;
+
+ RTL_USER_PROCESS_INFORMATION ProcessInfoNt = { sizeof(ProcessInfoNt) };
+ rcNt = RtlCreateUserProcess(&g_SupLibHardenedExeNtPath.UniStr,
+ OBJ_INHERIT | OBJ_CASE_INSENSITIVE /*Attributes*/,
+ pProcParams,
+ NULL, //&ProcessSecAttrs,
+ NULL, //&ThreadSecAttrs,
+ NtCurrentProcess() /* ParentProcess */,
+ FALSE /*fInheritHandles*/,
+ NULL /* DebugPort */,
+ NULL /* ExceptionPort */,
+ &ProcessInfoNt);
+ if (!NT_SUCCESS(rcNt))
+ supR3HardenedFatalMsg("supR3HardenedWinReSpawn", kSupInitOp_Misc, VERR_INVALID_NAME,
+ "Error relaunching VirtualBox VM process: %#x\n"
+ "Command line: '%ls'",
+ rcNt, CmdLine.Buffer);
+
+ SUP_DPRINTF(("supR3HardenedWinDoReSpawn(%d): New child %x.%x [ntdll].\n",
+ iWhich, ProcessInfo.ClientId.UniqueProcess, ProcessInfo.ClientId.UniqueThread));
+ RtlDestroyProcessParameters(pProcParams);
+
+ This.hProcess = ProcessInfoNt.ProcessHandle;
+ This.hThread = ProcessInfoNt.ThreadHandle;
+#endif
+
+#ifndef VBOX_WITHOUT_DEBUGGER_CHECKS
+ /*
+ * Apply anti debugger notification trick to the thread. (Also done in
+ * supR3HardenedWinInit.) This may fail with STATUS_ACCESS_DENIED and
+ * maybe other errors. (Unfortunately, recent (SEP 12.1) of symantec's
+ * sysplant.sys driver will cause process deadlocks and a shutdown/reboot
+ * denial of service problem if we hide the initial thread, so we postpone
+ * this action if we've detected SEP.)
+ */
+ if (!(g_fSupAdversaries & (SUPHARDNT_ADVERSARY_SYMANTEC_SYSPLANT | SUPHARDNT_ADVERSARY_SYMANTEC_N360)))
+ {
+ rcNt = NtSetInformationThread(This.hThread, ThreadHideFromDebugger, NULL, 0);
+ if (!NT_SUCCESS(rcNt))
+ SUP_DPRINTF(("supR3HardenedWinReSpawn: NtSetInformationThread/ThreadHideFromDebugger failed: %#x (harmless)\n", rcNt));
+ }
+#endif
+
+ /*
+ * Perform very early child initialization.
+ */
+ supR3HardNtChildGatherData(&This);
+ supR3HardNtChildScrewUpPebForInitialImageEvents(&This);
+ supR3HardNtChildSetUpChildInit(&This);
+
+ ULONG cSuspendCount = 0;
+ rcNt = NtResumeThread(This.hThread, &cSuspendCount);
+ if (!NT_SUCCESS(rcNt))
+ supR3HardenedWinKillChild(&This, "supR3HardenedWinDoReSpawn", rcNt, "NtResumeThread failed: %#x", rcNt);
+
+ /*
+ * Santizie the pre-NTDLL child when it's ready.
+ *
+ * AV software and other things injecting themselves into the embryonic
+ * and budding process to intercept API calls and what not. Unfortunately
+ * this is also the behavior of viruses, malware and other unfriendly
+ * software, so we won't stand for it. AV software can scan our image
+ * as they are loaded via kernel hooks, that's sufficient. No need for
+ * patching half of NTDLL or messing with the import table of the
+ * process executable.
+ */
+ supR3HardNtChildWaitFor(&This, kSupR3WinChildReq_PurifyChildAndCloseHandles, 2000 /*ms*/, "PurifyChildAndCloseHandles");
+ supR3HardNtChildPurify(&This);
+ supR3HardNtChildSanitizePeb(&This);
+
+ /*
+ * Close the unrestricted access handles. Since we need to wait on the
+ * child process, we'll reopen the process with limited access before doing
+ * away with the process handle returned by CreateProcess.
+ */
+ supR3HardNtChildCloseFullAccessHandles(&This);
+
+ /*
+ * Signal the child that we've closed the unrestricted handles and it can
+ * safely try open the driver.
+ */
+ rcNt = NtSetEvent(This.hEvtChild, NULL);
+ if (!NT_SUCCESS(rcNt))
+ supR3HardenedWinKillChild(&This, "supR3HardenedWinReSpawn", VERR_INVALID_NAME,
+ "NtSetEvent failed on child process handle: %#x\n", rcNt);
+
+ /*
+ * Ditch the loader cache so we don't sit on too much memory while waiting.
+ */
+ supR3HardenedWinFlushLoaderCache();
+ supR3HardenedWinCompactHeaps();
+
+ /*
+ * Enable thread creation at this point so Ctrl-C and Ctrl-Break can be processed.
+ */
+ supR3HardenedWinEnableThreadCreation();
+
+ /*
+ * Wait for the child to get to suplibHardenedWindowsMain so we can close the handles.
+ */
+ supR3HardNtChildWaitFor(&This, kSupR3WinChildReq_CloseEvents, 60000 /*ms*/, "CloseEvents");
+
+ NtClose(This.hEvtChild);
+ NtClose(This.hEvtParent);
+ This.hEvtChild = NULL;
+ This.hEvtParent = NULL;
+
+ /*
+ * Wait for the process to terminate.
+ */
+ supR3HardNtChildWaitFor(&This, kSupR3WinChildReq_End, RT_INDEFINITE_WAIT, "the end");
+ supR3HardenedFatal("supR3HardenedWinDoReSpawn: supR3HardNtChildWaitFor unexpectedly returned!\n");
+ /* not reached*/
+}
+
+
+/**
+ * Logs the content of the given object directory.
+ *
+ * @returns true if it exists, false if not.
+ * @param pszDir The path of the directory to log (ASCII).
+ */
+static void supR3HardenedWinLogObjDir(const char *pszDir)
+{
+ /*
+ * Open the driver object directory.
+ */
+ RTUTF16 wszDir[128];
+ int rc = RTUtf16CopyAscii(wszDir, RT_ELEMENTS(wszDir), pszDir);
+ if (RT_FAILURE(rc))
+ {
+ SUP_DPRINTF(("supR3HardenedWinLogObjDir: RTUtf16CopyAscii -> %Rrc on '%s'\n", rc, pszDir));
+ return;
+ }
+
+ UNICODE_STRING NtDirName;
+ NtDirName.Buffer = (WCHAR *)wszDir;
+ NtDirName.Length = (USHORT)(RTUtf16Len(wszDir) * sizeof(WCHAR));
+ NtDirName.MaximumLength = NtDirName.Length + sizeof(WCHAR);
+
+ OBJECT_ATTRIBUTES ObjAttr;
+ InitializeObjectAttributes(&ObjAttr, &NtDirName, OBJ_CASE_INSENSITIVE, NULL /*hRootDir*/, NULL /*pSecDesc*/);
+
+ HANDLE hDir;
+ NTSTATUS rcNt = NtOpenDirectoryObject(&hDir, DIRECTORY_QUERY | FILE_LIST_DIRECTORY, &ObjAttr);
+ SUP_DPRINTF(("supR3HardenedWinLogObjDir: %ls => %#x\n", wszDir, rcNt));
+ if (!NT_SUCCESS(rcNt))
+ return;
+
+ /*
+ * Enumerate it, looking for the driver.
+ */
+ ULONG uObjDirCtx = 0;
+ for (;;)
+ {
+ uint32_t abBuffer[_64K + _1K];
+ ULONG cbActual;
+ rcNt = NtQueryDirectoryObject(hDir,
+ abBuffer,
+ sizeof(abBuffer) - 4, /* minus four for string terminator space. */
+ FALSE /*ReturnSingleEntry */,
+ FALSE /*RestartScan*/,
+ &uObjDirCtx,
+ &cbActual);
+ if (!NT_SUCCESS(rcNt) || cbActual < sizeof(OBJECT_DIRECTORY_INFORMATION))
+ {
+ SUP_DPRINTF(("supR3HardenedWinLogObjDir: NtQueryDirectoryObject => rcNt=%#x cbActual=%#x\n", rcNt, cbActual));
+ break;
+ }
+
+ POBJECT_DIRECTORY_INFORMATION pObjDir = (POBJECT_DIRECTORY_INFORMATION)abBuffer;
+ while (pObjDir->Name.Length != 0)
+ {
+ SUP_DPRINTF((" %.*ls %.*ls\n",
+ pObjDir->TypeName.Length / sizeof(WCHAR), pObjDir->TypeName.Buffer,
+ pObjDir->Name.Length / sizeof(WCHAR), pObjDir->Name.Buffer));
+
+ /* Next directory entry. */
+ pObjDir++;
+ }
+ }
+
+ /*
+ * Clean up and return.
+ */
+ NtClose(hDir);
+}
+
+
+/**
+ * Tries to open VBoxDrvErrorInfo and read extra error info from it.
+ *
+ * @returns pszErrorInfo.
+ * @param pszErrorInfo The destination buffer. Will always be
+ * terminated.
+ * @param cbErrorInfo The size of the destination buffer.
+ * @param pszPrefix What to prefix the error info with, if we got
+ * anything.
+ */
+DECLHIDDEN(char *) supR3HardenedWinReadErrorInfoDevice(char *pszErrorInfo, size_t cbErrorInfo, const char *pszPrefix)
+{
+ RT_BZERO(pszErrorInfo, cbErrorInfo);
+
+ /*
+ * Try open the device.
+ */
+ HANDLE hFile = RTNT_INVALID_HANDLE_VALUE;
+ IO_STATUS_BLOCK Ios = RTNT_IO_STATUS_BLOCK_INITIALIZER;
+ UNICODE_STRING NtName = RTNT_CONSTANT_UNISTR(SUPDRV_NT_DEVICE_NAME_ERROR_INFO);
+ OBJECT_ATTRIBUTES ObjAttr;
+ InitializeObjectAttributes(&ObjAttr, &NtName, OBJ_CASE_INSENSITIVE, NULL /*hRootDir*/, NULL /*pSecDesc*/);
+ NTSTATUS rcNt = NtCreateFile(&hFile,
+ GENERIC_READ, /* No SYNCHRONIZE. */
+ &ObjAttr,
+ &Ios,
+ NULL /* Allocation Size*/,
+ FILE_ATTRIBUTE_NORMAL,
+ FILE_SHARE_READ | FILE_SHARE_WRITE,
+ FILE_OPEN,
+ FILE_NON_DIRECTORY_FILE, /* No FILE_SYNCHRONOUS_IO_NONALERT. */
+ NULL /*EaBuffer*/,
+ 0 /*EaLength*/);
+ if (NT_SUCCESS(rcNt))
+ rcNt = Ios.Status;
+ if (NT_SUCCESS(rcNt))
+ {
+ /*
+ * Try read error info.
+ */
+ size_t cchPrefix = strlen(pszPrefix);
+ if (cchPrefix + 3 < cbErrorInfo)
+ {
+ LARGE_INTEGER offRead;
+ offRead.QuadPart = 0;
+ rcNt = NtReadFile(hFile, NULL /*hEvent*/, NULL /*ApcRoutine*/, NULL /*ApcContext*/, &Ios,
+ &pszErrorInfo[cchPrefix], (ULONG)(cbErrorInfo - cchPrefix - 1), &offRead, NULL);
+ if (NT_SUCCESS(rcNt) && NT_SUCCESS(Ios.Status) && Ios.Information > 0)
+ {
+ memcpy(pszErrorInfo, pszPrefix, cchPrefix);
+ pszErrorInfo[RT_MIN(cbErrorInfo - 1, cchPrefix + Ios.Information)] = '\0';
+ SUP_DPRINTF(("supR3HardenedWinReadErrorInfoDevice: '%s'", &pszErrorInfo[cchPrefix]));
+ }
+ else
+ {
+ *pszErrorInfo = '\0';
+ if (rcNt != STATUS_END_OF_FILE || Ios.Status != STATUS_END_OF_FILE)
+ SUP_DPRINTF(("supR3HardenedWinReadErrorInfoDevice: NtReadFile -> %#x / %#x / %p\n",
+ rcNt, Ios.Status, Ios.Information));
+ }
+ }
+ else
+ RTStrCopy(pszErrorInfo, cbErrorInfo, "error info buffer too small");
+ NtClose(hFile);
+ }
+ else
+ SUP_DPRINTF(("supR3HardenedWinReadErrorInfoDevice: NtCreateFile -> %#x\n", rcNt));
+
+ return pszErrorInfo;
+}
+
+
+
+/**
+ * Checks if the driver exists.
+ *
+ * This checks whether the driver is present in the /Driver object directory.
+ * Drivers being initialized or terminated will have an object there
+ * before/after their devices nodes are created/deleted.
+ *
+ * @returns true if it exists, false if not.
+ * @param pszDriver The driver name.
+ */
+static bool supR3HardenedWinDriverExists(const char *pszDriver)
+{
+ /*
+ * Open the driver object directory.
+ */
+ UNICODE_STRING NtDirName = RTNT_CONSTANT_UNISTR(L"\\Driver");
+
+ OBJECT_ATTRIBUTES ObjAttr;
+ InitializeObjectAttributes(&ObjAttr, &NtDirName, OBJ_CASE_INSENSITIVE, NULL /*hRootDir*/, NULL /*pSecDesc*/);
+
+ HANDLE hDir;
+ NTSTATUS rcNt = NtOpenDirectoryObject(&hDir, DIRECTORY_QUERY | FILE_LIST_DIRECTORY, &ObjAttr);
+#ifdef VBOX_STRICT
+ SUPR3HARDENED_ASSERT_NT_SUCCESS(rcNt);
+#endif
+ if (!NT_SUCCESS(rcNt))
+ return true;
+
+ /*
+ * Enumerate it, looking for the driver.
+ */
+ bool fFound = true;
+ ULONG uObjDirCtx = 0;
+ do
+ {
+ uint32_t abBuffer[_64K + _1K];
+ ULONG cbActual;
+ rcNt = NtQueryDirectoryObject(hDir,
+ abBuffer,
+ sizeof(abBuffer) - 4, /* minus four for string terminator space. */
+ FALSE /*ReturnSingleEntry */,
+ FALSE /*RestartScan*/,
+ &uObjDirCtx,
+ &cbActual);
+ if (!NT_SUCCESS(rcNt) || cbActual < sizeof(OBJECT_DIRECTORY_INFORMATION))
+ break;
+
+ POBJECT_DIRECTORY_INFORMATION pObjDir = (POBJECT_DIRECTORY_INFORMATION)abBuffer;
+ while (pObjDir->Name.Length != 0)
+ {
+ WCHAR wcSaved = pObjDir->Name.Buffer[pObjDir->Name.Length / sizeof(WCHAR)];
+ pObjDir->Name.Buffer[pObjDir->Name.Length / sizeof(WCHAR)] = '\0';
+ if ( pObjDir->Name.Length > 1
+ && RTUtf16ICmpAscii(pObjDir->Name.Buffer, pszDriver) == 0)
+ {
+ fFound = true;
+ break;
+ }
+ pObjDir->Name.Buffer[pObjDir->Name.Length / sizeof(WCHAR)] = wcSaved;
+
+ /* Next directory entry. */
+ pObjDir++;
+ }
+ } while (!fFound);
+
+ /*
+ * Clean up and return.
+ */
+ NtClose(hDir);
+
+ return fFound;
+}
+
+
+/**
+ * Open the stub device before the 2nd respawn.
+ */
+static void supR3HardenedWinOpenStubDevice(void)
+{
+ if (g_fSupStubOpened)
+ return;
+
+ /*
+ * Retry if we think driver might still be initializing (STATUS_NO_SUCH_DEVICE + \Drivers\VBoxDrv).
+ */
+ static const WCHAR s_wszName[] = SUPDRV_NT_DEVICE_NAME_STUB;
+ uint64_t const uMsTsStart = supR3HardenedWinGetMilliTS();
+ NTSTATUS rcNt;
+ uint32_t iTry;
+
+ for (iTry = 0;; iTry++)
+ {
+ HANDLE hFile = RTNT_INVALID_HANDLE_VALUE;
+ IO_STATUS_BLOCK Ios = RTNT_IO_STATUS_BLOCK_INITIALIZER;
+
+ UNICODE_STRING NtName;
+ NtName.Buffer = (PWSTR)s_wszName;
+ NtName.Length = sizeof(s_wszName) - sizeof(WCHAR);
+ NtName.MaximumLength = sizeof(s_wszName);
+
+ OBJECT_ATTRIBUTES ObjAttr;
+ InitializeObjectAttributes(&ObjAttr, &NtName, OBJ_CASE_INSENSITIVE, NULL /*hRootDir*/, NULL /*pSecDesc*/);
+
+ rcNt = NtCreateFile(&hFile,
+ GENERIC_READ | GENERIC_WRITE, /* No SYNCHRONIZE. */
+ &ObjAttr,
+ &Ios,
+ NULL /* Allocation Size*/,
+ FILE_ATTRIBUTE_NORMAL,
+ FILE_SHARE_READ | FILE_SHARE_WRITE,
+ FILE_OPEN,
+ FILE_NON_DIRECTORY_FILE, /* No FILE_SYNCHRONOUS_IO_NONALERT. */
+ NULL /*EaBuffer*/,
+ 0 /*EaLength*/);
+ if (NT_SUCCESS(rcNt))
+ rcNt = Ios.Status;
+
+ /* The STATUS_NO_SUCH_DEVICE might be returned if the device is not
+ completely initialized. Delay a little bit and try again. */
+ if (rcNt != STATUS_NO_SUCH_DEVICE)
+ break;
+ if (iTry > 0 && supR3HardenedWinGetMilliTS() - uMsTsStart > 5000) /* 5 sec, at least two tries */
+ break;
+ if (!supR3HardenedWinDriverExists("VBoxDrv"))
+ {
+ /** @todo Consider starting the VBoxdrv.sys service. Requires 2nd process
+ * though, rather complicated actually as CreateProcess causes all
+ * kind of things to happen to this process which would make it hard to
+ * pass the process verification tests... :-/ */
+ break;
+ }
+
+ LARGE_INTEGER Time;
+ if (iTry < 8)
+ Time.QuadPart = -1000000 / 100; /* 1ms in 100ns units, relative time. */
+ else
+ Time.QuadPart = -32000000 / 100; /* 32ms in 100ns units, relative time. */
+ NtDelayExecution(TRUE, &Time);
+ }
+
+ if (NT_SUCCESS(rcNt))
+ g_fSupStubOpened = true;
+ else
+ {
+ /*
+ * Report trouble (fatal). For some errors codes we try gather some
+ * extra information that goes into VBoxStartup.log so that we stand a
+ * better chance resolving the issue.
+ */
+ char szErrorInfo[16384];
+ int rc = VERR_OPEN_FAILED;
+ if (SUP_NT_STATUS_IS_VBOX(rcNt)) /* See VBoxDrvNtErr2NtStatus. */
+ {
+ rc = SUP_NT_STATUS_TO_VBOX(rcNt);
+
+ /*
+ * \Windows\ApiPort open trouble. So far only
+ * STATUS_OBJECT_TYPE_MISMATCH has been observed.
+ */
+ if (rc == VERR_SUPDRV_APIPORT_OPEN_ERROR)
+ {
+ SUP_DPRINTF(("Error opening VBoxDrvStub: VERR_SUPDRV_APIPORT_OPEN_ERROR\n"));
+
+ uint32_t uSessionId = NtCurrentPeb()->SessionId;
+ SUP_DPRINTF((" SessionID=%#x\n", uSessionId));
+ char szDir[64];
+ if (uSessionId == 0)
+ RTStrCopy(szDir, sizeof(szDir), "\\Windows");
+ else
+ {
+ RTStrPrintf(szDir, sizeof(szDir), "\\Sessions\\%u\\Windows", uSessionId);
+ supR3HardenedWinLogObjDir(szDir);
+ }
+ supR3HardenedWinLogObjDir("\\Windows");
+ supR3HardenedWinLogObjDir("\\Sessions");
+
+ supR3HardenedFatalMsg("supR3HardenedWinReSpawn", kSupInitOp_Misc, rc,
+ "NtCreateFile(%ls) failed: VERR_SUPDRV_APIPORT_OPEN_ERROR\n"
+ "\n"
+ "Error getting %s\\ApiPort in the driver from vboxsup.\n"
+ "\n"
+ "Could be due to security software is redirecting access to it, so please include full "
+ "details of such software in a bug report. VBoxStartup.log may contain details important "
+ "to resolving the issue.%s"
+ , s_wszName, szDir,
+ supR3HardenedWinReadErrorInfoDevice(szErrorInfo, sizeof(szErrorInfo),
+ "\n\nVBoxDrvStub error: "));
+ }
+
+ /*
+ * Generic VBox failure message.
+ */
+ supR3HardenedFatalMsg("supR3HardenedWinReSpawn", kSupInitOp_Driver, rc,
+ "NtCreateFile(%ls) failed: %Rrc (rcNt=%#x)%s", s_wszName, rc, rcNt,
+ supR3HardenedWinReadErrorInfoDevice(szErrorInfo, sizeof(szErrorInfo),
+ "\nVBoxDrvStub error: "));
+ }
+ else
+ {
+ const char *pszDefine;
+ switch (rcNt)
+ {
+ case STATUS_NO_SUCH_DEVICE: pszDefine = " STATUS_NO_SUCH_DEVICE"; break;
+ case STATUS_OBJECT_NAME_NOT_FOUND: pszDefine = " STATUS_OBJECT_NAME_NOT_FOUND"; break;
+ case STATUS_ACCESS_DENIED: pszDefine = " STATUS_ACCESS_DENIED"; break;
+ case STATUS_TRUST_FAILURE: pszDefine = " STATUS_TRUST_FAILURE"; break;
+ default: pszDefine = ""; break;
+ }
+
+ /*
+ * Problems opening the device is generally due to driver load/
+ * unload issues. Check whether the driver is loaded and make
+ * suggestions accordingly.
+ */
+/** @todo don't fail during early init, wait till later and try load the driver if missing or at least query the service manager for additional information. */
+ if ( rcNt == STATUS_NO_SUCH_DEVICE
+ || rcNt == STATUS_OBJECT_NAME_NOT_FOUND)
+ {
+ SUP_DPRINTF(("Error opening VBoxDrvStub: %s\n", pszDefine));
+ if (supR3HardenedWinDriverExists("VBoxDrv"))
+ supR3HardenedFatalMsg("supR3HardenedWinReSpawn", kSupInitOp_Driver, VERR_OPEN_FAILED,
+ "NtCreateFile(%ls) failed: %#x%s (%u retries)\n"
+ "\n"
+ "Driver is probably stuck stopping/starting. Try 'sc.exe query vboxsup' to get more "
+ "information about its state. Rebooting may actually help.%s"
+ , s_wszName, rcNt, pszDefine, iTry,
+ supR3HardenedWinReadErrorInfoDevice(szErrorInfo, sizeof(szErrorInfo),
+ "\nVBoxDrvStub error: "));
+ else
+ supR3HardenedFatalMsg("supR3HardenedWinReSpawn", kSupInitOp_Driver, VERR_OPEN_FAILED,
+ "NtCreateFile(%ls) failed: %#x%s (%u retries)\n"
+ "\n"
+ "Driver is does not appear to be loaded. Try 'sc.exe start vboxsup', reinstall "
+ "VirtualBox or reboot.%s"
+ , s_wszName, rcNt, pszDefine, iTry,
+ supR3HardenedWinReadErrorInfoDevice(szErrorInfo, sizeof(szErrorInfo),
+ "\nVBoxDrvStub error: "));
+ }
+
+ /* Generic NT failure message. */
+ supR3HardenedFatalMsg("supR3HardenedWinReSpawn", kSupInitOp_Driver, VERR_OPEN_FAILED,
+ "NtCreateFile(%ls) failed: %#x%s (%u retries)%s",
+ s_wszName, rcNt, pszDefine, iTry,
+ supR3HardenedWinReadErrorInfoDevice(szErrorInfo, sizeof(szErrorInfo),
+ "\nVBoxDrvStub error: "));
+ }
+ }
+}
+
+
+/**
+ * Called by the main code if supR3HardenedWinIsReSpawnNeeded returns @c true.
+ *
+ * @returns Program exit code.
+ */
+DECLHIDDEN(int) supR3HardenedWinReSpawn(int iWhich)
+{
+ /*
+ * Before the 2nd respawn we set up a child protection deal with the
+ * support driver via /Devices/VBoxDrvStub. (We tried to do this
+ * during the early init, but in case we had trouble accessing vboxdrv
+ * (renamed to vboxsup in 7.0 and 6.1.34) we retry it here where we
+ * have kernel32.dll and others to pull in for better diagnostics.)
+ */
+ if (iWhich == 2)
+ supR3HardenedWinOpenStubDevice();
+
+ /*
+ * Make sure we're alone in the stub process before creating the VM process
+ * and that there aren't any debuggers attached.
+ */
+ if (iWhich == 2)
+ {
+ int rc = supHardNtVpDebugger(NtCurrentProcess(), RTErrInfoInitStatic(&g_ErrInfoStatic));
+ if (RT_SUCCESS(rc))
+ rc = supHardNtVpThread(NtCurrentProcess(), NtCurrentThread(), RTErrInfoInitStatic(&g_ErrInfoStatic));
+ if (RT_FAILURE(rc))
+ supR3HardenedFatalMsg("supR3HardenedWinReSpawn", kSupInitOp_Integrity, rc, "%s", g_ErrInfoStatic.szMsg);
+ }
+
+
+ /*
+ * Respawn the process with kernel protection for the new process.
+ */
+ supR3HardenedWinDoReSpawn(iWhich);
+ /* not reached! */
+}
+
+
+/**
+ * Checks if re-spawning is required, replacing the respawn argument if not.
+ *
+ * @returns true if required, false if not. In the latter case, the first
+ * argument in the vector is replaced.
+ * @param iWhich Which respawn we're to check for, 1 being the
+ * first one, and 2 the second and final.
+ * @param cArgs The number of arguments.
+ * @param papszArgs Pointer to the argument vector.
+ */
+DECLHIDDEN(bool) supR3HardenedWinIsReSpawnNeeded(int iWhich, int cArgs, char **papszArgs)
+{
+ SUPR3HARDENED_ASSERT(g_cSuplibHardenedWindowsMainCalls == 1);
+ SUPR3HARDENED_ASSERT(iWhich == 1 || iWhich == 2);
+
+ if (cArgs < 1)
+ return true;
+
+ if (suplibHardenedStrCmp(papszArgs[0], SUPR3_RESPAWN_1_ARG0) == 0)
+ {
+ if (iWhich > 1)
+ return true;
+ }
+ else if (suplibHardenedStrCmp(papszArgs[0], SUPR3_RESPAWN_2_ARG0) == 0)
+ {
+ if (iWhich < 2)
+ return false;
+ }
+ else
+ return true;
+
+ /* Replace the argument. */
+ papszArgs[0] = g_szSupLibHardenedExePath;
+ return false;
+}
+
+
+/**
+ * Initializes the windows verficiation bits and other things we're better off
+ * doing after main() has passed on it's data.
+ *
+ * @param fFlags The main flags.
+ * @param fAvastKludge Whether to apply the avast kludge.
+ */
+DECLHIDDEN(void) supR3HardenedWinInit(uint32_t fFlags, bool fAvastKludge)
+{
+ NTSTATUS rcNt;
+
+#ifndef VBOX_WITHOUT_DEBUGGER_CHECKS
+ /*
+ * Install a anti debugging hack before we continue. This prevents most
+ * notifications from ending up in the debugger. (Also applied to the
+ * child process when respawning.)
+ */
+ rcNt = NtSetInformationThread(NtCurrentThread(), ThreadHideFromDebugger, NULL, 0);
+ if (!NT_SUCCESS(rcNt))
+ supR3HardenedFatalMsg("supR3HardenedWinInit", kSupInitOp_Misc, VERR_GENERAL_FAILURE,
+ "NtSetInformationThread/ThreadHideFromDebugger failed: %#x\n", rcNt);
+#endif
+
+ /*
+ * Init the verifier.
+ */
+ RTErrInfoInitStatic(&g_ErrInfoStatic);
+ int rc = supHardenedWinInitImageVerifier(&g_ErrInfoStatic.Core);
+ if (RT_FAILURE(rc))
+ supR3HardenedFatalMsg("supR3HardenedWinInit", kSupInitOp_Misc, rc,
+ "supHardenedWinInitImageVerifier failed: %s", g_ErrInfoStatic.szMsg);
+
+ /*
+ * Get the windows system directory from the KnownDlls dir.
+ */
+ HANDLE hSymlink = INVALID_HANDLE_VALUE;
+ UNICODE_STRING UniStr = RTNT_CONSTANT_UNISTR(L"\\KnownDlls\\KnownDllPath");
+ OBJECT_ATTRIBUTES ObjAttrs;
+ InitializeObjectAttributes(&ObjAttrs, &UniStr, OBJ_CASE_INSENSITIVE, NULL /*hRootDir*/, NULL /*pSecDesc*/);
+ rcNt = NtOpenSymbolicLinkObject(&hSymlink, SYMBOLIC_LINK_QUERY, &ObjAttrs);
+ if (!NT_SUCCESS(rcNt))
+ supR3HardenedFatalMsg("supR3HardenedWinInit", kSupInitOp_Misc, rcNt, "Error opening '%ls': %#x", UniStr.Buffer, rcNt);
+
+ g_System32WinPath.UniStr.Buffer = g_System32WinPath.awcBuffer;
+ g_System32WinPath.UniStr.Length = 0;
+ g_System32WinPath.UniStr.MaximumLength = sizeof(g_System32WinPath.awcBuffer) - sizeof(RTUTF16);
+ rcNt = NtQuerySymbolicLinkObject(hSymlink, &g_System32WinPath.UniStr, NULL);
+ if (!NT_SUCCESS(rcNt))
+ supR3HardenedFatalMsg("supR3HardenedWinInit", kSupInitOp_Misc, rcNt, "Error querying '%ls': %#x", UniStr.Buffer, rcNt);
+ g_System32WinPath.UniStr.Buffer[g_System32WinPath.UniStr.Length / sizeof(RTUTF16)] = '\0';
+
+ SUP_DPRINTF(("KnownDllPath: %ls\n", g_System32WinPath.UniStr.Buffer));
+ NtClose(hSymlink);
+
+ if (!(fFlags & SUPSECMAIN_FLAGS_DONT_OPEN_DEV))
+ {
+ if (fAvastKludge)
+ {
+ /*
+ * Do a self purification to cure avast's weird NtOpenFile write-thru
+ * change in GetBinaryTypeW change in kernel32. Unfortunately, avast
+ * uses a system thread to perform the process modifications, which
+ * means it's hard to make sure it had the chance to make them...
+ *
+ * We have to resort to kludge doing yield and sleep fudging for a
+ * number of milliseconds and schedulings before we can hope that avast
+ * and similar products have done what they need to do. If we do any
+ * fixes, we wait for a while again and redo it until we're clean.
+ *
+ * This is unfortunately kind of fragile.
+ */
+ uint32_t cMsFudge = g_fSupAdversaries ? 512 : 128;
+ uint32_t cFixes;
+ for (uint32_t iLoop = 0; iLoop < 16; iLoop++)
+ {
+ uint32_t cSleeps = 0;
+ uint64_t uMsTsStart = supR3HardenedWinGetMilliTS();
+ do
+ {
+ NtYieldExecution();
+ LARGE_INTEGER Time;
+ Time.QuadPart = -8000000 / 100; /* 8ms in 100ns units, relative time. */
+ NtDelayExecution(FALSE, &Time);
+ cSleeps++;
+ } while ( supR3HardenedWinGetMilliTS() - uMsTsStart <= cMsFudge
+ || cSleeps < 8);
+ SUP_DPRINTF(("supR3HardenedWinInit: Startup delay kludge #2/%u: %u ms, %u sleeps\n",
+ iLoop, supR3HardenedWinGetMilliTS() - uMsTsStart, cSleeps));
+
+ cFixes = 0;
+ rc = supHardenedWinVerifyProcess(NtCurrentProcess(), NtCurrentThread(), SUPHARDNTVPKIND_SELF_PURIFICATION,
+ 0 /*fFlags*/, &cFixes, NULL /*pErrInfo*/);
+ if (RT_FAILURE(rc) || cFixes == 0)
+ break;
+
+ if (!g_fSupAdversaries)
+ g_fSupAdversaries |= SUPHARDNT_ADVERSARY_UNKNOWN;
+ cMsFudge = 512;
+
+ /* Log the KiOpPrefetchPatchCount value if available, hoping it might sched some light on spider38's case. */
+ ULONG cPatchCount = 0;
+ rcNt = NtQuerySystemInformation(SystemInformation_KiOpPrefetchPatchCount,
+ &cPatchCount, sizeof(cPatchCount), NULL);
+ if (NT_SUCCESS(rcNt))
+ SUP_DPRINTF(("supR3HardenedWinInit: cFixes=%u g_fSupAdversaries=%#x cPatchCount=%#u\n",
+ cFixes, g_fSupAdversaries, cPatchCount));
+ else
+ SUP_DPRINTF(("supR3HardenedWinInit: cFixes=%u g_fSupAdversaries=%#x\n", cFixes, g_fSupAdversaries));
+ }
+ }
+
+ /*
+ * Install the hooks.
+ */
+ supR3HardenedWinInstallHooks();
+ }
+ else if (fFlags & SUPSECMAIN_FLAGS_FIRST_PROCESS)
+ {
+ /*
+ * Try shake anyone (e.g. easyhook) patching process creation code in
+ * kernelbase, kernel32 or ntdll so they won't so easily cause the child
+ * to crash when we respawn and purify it.
+ */
+ SUP_DPRINTF(("supR3HardenedWinInit: Performing a limited self purification...\n"));
+ uint32_t cFixes = 0;
+ rc = supHardenedWinVerifyProcess(NtCurrentProcess(), NtCurrentThread(), SUPHARDNTVPKIND_SELF_PURIFICATION_LIMITED,
+ 0 /*fFlags*/, &cFixes, NULL /*pErrInfo*/);
+ SUP_DPRINTF(("supR3HardenedWinInit: SUPHARDNTVPKIND_SELF_PURIFICATION_LIMITED -> %Rrc, cFixes=%d\n", rc, cFixes));
+ RT_NOREF(rc); /* ignored on purpose */
+ }
+
+#ifndef VBOX_WITH_VISTA_NO_SP
+ /*
+ * Complain about Vista w/o service pack if we're launching a VM.
+ */
+ if ( !(fFlags & SUPSECMAIN_FLAGS_DONT_OPEN_DEV)
+ && g_uNtVerCombined >= SUP_NT_VER_VISTA
+ && g_uNtVerCombined < SUP_MAKE_NT_VER_COMBINED(6, 0, 6001, 0, 0))
+ supR3HardenedFatalMsg("supR3HardenedWinInit", kSupInitOp_Misc, VERR_NOT_SUPPORTED,
+ "Window Vista without any service pack installed is not supported. Please install the latest service pack.");
+#endif
+}
+
+
+/**
+ * Modifies the DLL search path for testcases.
+ *
+ * This makes sure the application binary path is in the search path. When
+ * starting a testcase executable in the testcase/ subdirectory this isn't the
+ * case by default. So, unless we do something about it we won't be able to
+ * import VBox DLLs.
+ *
+ * @param fFlags The main flags (giving the location).
+ * @param pszAppBinPath The path to the application binary directory
+ * (windows style).
+ */
+DECLHIDDEN(void) supR3HardenedWinModifyDllSearchPath(uint32_t fFlags, const char *pszAppBinPath)
+{
+ /*
+ * For the testcases to work, we must add the app bin directory to the
+ * DLL search list before the testcase dll is loaded or it won't be
+ * able to find the VBox DLLs. This is done _after_ VBoxRT.dll is
+ * initialized and sets its defaults.
+ */
+ switch (fFlags & SUPSECMAIN_FLAGS_LOC_MASK)
+ {
+ case SUPSECMAIN_FLAGS_LOC_TESTCASE:
+ break;
+ default:
+ return;
+ }
+
+ /*
+ * Dynamically resolve the two APIs we need (the latter uses forwarders on w7).
+ */
+ HMODULE hModKernel32 = GetModuleHandleW(L"kernel32.dll");
+
+ typedef BOOL (WINAPI *PFNSETDLLDIRECTORY)(LPCWSTR);
+ PFNSETDLLDIRECTORY pfnSetDllDir;
+ pfnSetDllDir = (PFNSETDLLDIRECTORY)GetProcAddress(hModKernel32, "SetDllDirectoryW");
+
+ typedef BOOL (WINAPI *PFNSETDEFAULTDLLDIRECTORIES)(DWORD);
+ PFNSETDEFAULTDLLDIRECTORIES pfnSetDefDllDirs;
+ pfnSetDefDllDirs = (PFNSETDEFAULTDLLDIRECTORIES)GetProcAddress(hModKernel32, "SetDefaultDllDirectories");
+
+ if (pfnSetDllDir != NULL)
+ {
+ /*
+ * Convert the path to UTF-16 and try set it.
+ */
+ PRTUTF16 pwszAppBinPath = NULL;
+ int rc = RTStrToUtf16(pszAppBinPath, &pwszAppBinPath);
+ if (RT_SUCCESS(rc))
+ {
+ if (pfnSetDllDir(pwszAppBinPath))
+ {
+ SUP_DPRINTF(("supR3HardenedWinModifyDllSearchPath: Set dll dir to '%ls'\n", pwszAppBinPath));
+ g_fSupLibHardenedDllSearchUserDirs = true;
+
+ /*
+ * We set it alright, on W7 and later we also must modify the
+ * default DLL search order. See @bugref{6861} for details on
+ * why we don't do this on Vista (also see init-win.cpp in IPRT).
+ */
+ if ( pfnSetDefDllDirs
+ && g_uNtVerCombined >= SUP_NT_VER_W70)
+ {
+ if (pfnSetDefDllDirs( LOAD_LIBRARY_SEARCH_APPLICATION_DIR
+ | LOAD_LIBRARY_SEARCH_SYSTEM32
+ | LOAD_LIBRARY_SEARCH_USER_DIRS))
+ SUP_DPRINTF(("supR3HardenedWinModifyDllSearchPath: Successfully modified search dirs.\n"));
+ else
+ supR3HardenedFatal("supR3HardenedWinModifyDllSearchPath: SetDllDirectoryW(%ls) failed: %d\n",
+ pwszAppBinPath, RtlGetLastWin32Error());
+ }
+ }
+ else
+ supR3HardenedFatal("supR3HardenedWinModifyDllSearchPath: SetDllDirectoryW(%ls) failed: %d\n",
+ pwszAppBinPath, RtlGetLastWin32Error());
+ RTUtf16Free(pwszAppBinPath);
+ }
+ else
+ supR3HardenedFatal("supR3HardenedWinModifyDllSearchPath: RTStrToUtf16(%s) failed: %d\n", pszAppBinPath, rc);
+ }
+}
+
+
+/**
+ * Initializes the application binary directory path.
+ *
+ * This is called once or twice.
+ *
+ * @param fFlags The main flags (giving the location).
+ */
+DECLHIDDEN(void) supR3HardenedWinInitAppBin(uint32_t fFlags)
+{
+ USHORT cwc = (USHORT)g_offSupLibHardenedExeNtName - 1;
+ g_SupLibHardenedAppBinNtPath.UniStr.Buffer = g_SupLibHardenedAppBinNtPath.awcBuffer;
+ memcpy(g_SupLibHardenedAppBinNtPath.UniStr.Buffer, g_SupLibHardenedExeNtPath.UniStr.Buffer, cwc * sizeof(WCHAR));
+
+ switch (fFlags & SUPSECMAIN_FLAGS_LOC_MASK)
+ {
+ case SUPSECMAIN_FLAGS_LOC_APP_BIN:
+ break;
+ case SUPSECMAIN_FLAGS_LOC_TESTCASE:
+ {
+ /* Drop one directory level. */
+ USHORT off = cwc;
+ WCHAR wc;
+ while ( off > 1
+ && (wc = g_SupLibHardenedAppBinNtPath.UniStr.Buffer[off - 1]) != '\0')
+ if (wc != '\\' && wc != '/')
+ off--;
+ else
+ {
+ if (g_SupLibHardenedAppBinNtPath.UniStr.Buffer[off - 2] == ':')
+ cwc = off;
+ else
+ cwc = off - 1;
+ break;
+ }
+ break;
+ }
+ default:
+ supR3HardenedFatal("supR3HardenedWinInitAppBin: Unknown program binary location: %#x\n", fFlags);
+ }
+
+ g_SupLibHardenedAppBinNtPath.UniStr.Buffer[cwc] = '\0';
+ g_SupLibHardenedAppBinNtPath.UniStr.Length = cwc * sizeof(WCHAR);
+ g_SupLibHardenedAppBinNtPath.UniStr.MaximumLength = sizeof(g_SupLibHardenedAppBinNtPath.awcBuffer);
+ SUP_DPRINTF(("supR3HardenedWinInitAppBin(%#x): '%ls'\n", fFlags, g_SupLibHardenedAppBinNtPath.UniStr.Buffer));
+}
+
+
+/**
+ * Converts the Windows command line string (UTF-16) to an array of UTF-8
+ * arguments suitable for passing to main().
+ *
+ * @returns Pointer to the argument array.
+ * @param pawcCmdLine The UTF-16 windows command line to parse.
+ * @param cwcCmdLine The length of the command line.
+ * @param pcArgs Where to return the number of arguments.
+ */
+static char **suplibCommandLineToArgvWStub(PCRTUTF16 pawcCmdLine, size_t cwcCmdLine, int *pcArgs)
+{
+ /*
+ * Convert the command line string to UTF-8.
+ */
+ char *pszCmdLine = NULL;
+ SUPR3HARDENED_ASSERT(RT_SUCCESS(RTUtf16ToUtf8Ex(pawcCmdLine, cwcCmdLine, &pszCmdLine, 0, NULL)));
+
+ /*
+ * Parse the command line, carving argument strings out of it.
+ */
+ int cArgs = 0;
+ int cArgsAllocated = 4;
+ char **papszArgs = (char **)RTMemAllocZ(sizeof(char *) * cArgsAllocated);
+ char *pszSrc = pszCmdLine;
+ for (;;)
+ {
+ /* skip leading blanks. */
+ char ch = *pszSrc;
+ while (suplibCommandLineIsArgSeparator(ch))
+ ch = *++pszSrc;
+ if (!ch)
+ break;
+
+ /* Add argument to the vector. */
+ if (cArgs + 2 >= cArgsAllocated)
+ {
+ cArgsAllocated *= 2;
+ papszArgs = (char **)RTMemRealloc(papszArgs, sizeof(char *) * cArgsAllocated);
+ }
+ papszArgs[cArgs++] = pszSrc;
+ papszArgs[cArgs] = NULL;
+
+ /* Unquote and unescape the string. */
+ char *pszDst = pszSrc++;
+ bool fQuoted = false;
+ do
+ {
+ if (ch == '"')
+ fQuoted = !fQuoted;
+ else if (ch != '\\' || (*pszSrc != '\\' && *pszSrc != '"'))
+ *pszDst++ = ch;
+ else
+ {
+ unsigned cSlashes = 0;
+ while ((ch = *pszSrc++) == '\\')
+ cSlashes++;
+ if (ch == '"')
+ {
+ while (cSlashes >= 2)
+ {
+ cSlashes -= 2;
+ *pszDst++ = '\\';
+ }
+ if (cSlashes)
+ *pszDst++ = '"';
+ else
+ fQuoted = !fQuoted;
+ }
+ else
+ {
+ pszSrc--;
+ while (cSlashes-- > 0)
+ *pszDst++ = '\\';
+ }
+ }
+
+ ch = *pszSrc++;
+ } while (ch != '\0' && (fQuoted || !suplibCommandLineIsArgSeparator(ch)));
+
+ /* Terminate the argument. */
+ *pszDst = '\0';
+ if (!ch)
+ break;
+ }
+
+ *pcArgs = cArgs;
+ return papszArgs;
+}
+
+
+/**
+ * Worker for supR3HardenedFindVersionRsrcOffset.
+ *
+ * @returns RVA the version resource data, UINT32_MAX if not found.
+ * @param pRootDir The root resource directory. Expects data to
+ * follow.
+ * @param cbBuf The amount of data at pRootDir.
+ * @param offData The offset to the data entry.
+ * @param pcbData Where to return the size of the data.
+ */
+static uint32_t supR3HardenedGetRvaFromRsrcDataEntry(PIMAGE_RESOURCE_DIRECTORY pRootDir, uint32_t cbBuf, uint32_t offData,
+ uint32_t *pcbData)
+{
+ if ( offData <= cbBuf
+ && offData + sizeof(IMAGE_RESOURCE_DATA_ENTRY) <= cbBuf)
+ {
+ PIMAGE_RESOURCE_DATA_ENTRY pRsrcData = (PIMAGE_RESOURCE_DATA_ENTRY)((uintptr_t)pRootDir + offData);
+ SUP_DPRINTF((" [Raw version resource data: %#x LB %#x, codepage %#x (reserved %#x)]\n",
+ pRsrcData->OffsetToData, pRsrcData->Size, pRsrcData->CodePage, pRsrcData->Reserved));
+ if (pRsrcData->Size > 0)
+ {
+ *pcbData = pRsrcData->Size;
+ return pRsrcData->OffsetToData;
+ }
+ }
+ else
+ SUP_DPRINTF((" Version resource data (%#x) is outside the buffer (%#x)! :-(\n", offData, cbBuf));
+
+ *pcbData = 0;
+ return UINT32_MAX;
+}
+
+
+/** @def SUP_RSRC_DPRINTF
+ * Dedicated debug printf for resource directory parsing.
+ * @sa SUP_DPRINTF
+ */
+#if 0 /* more details */
+# define SUP_RSRC_DPRINTF(a) SUP_DPRINTF(a)
+#else
+# define SUP_RSRC_DPRINTF(a) do { } while (0)
+#endif
+
+/**
+ * Scans the resource directory for a version resource.
+ *
+ * @returns RVA of the version resource data, UINT32_MAX if not found.
+ * @param pRootDir The root resource directory. Expects data to
+ * follow.
+ * @param cbBuf The amount of data at pRootDir.
+ * @param pcbData Where to return the size of the version data.
+ */
+static uint32_t supR3HardenedFindVersionRsrcRva(PIMAGE_RESOURCE_DIRECTORY pRootDir, uint32_t cbBuf, uint32_t *pcbData)
+{
+ SUP_RSRC_DPRINTF((" ResDir: Char=%#x Time=%#x Ver=%d%d #NamedEntries=%#x #IdEntries=%#x\n",
+ pRootDir->Characteristics,
+ pRootDir->TimeDateStamp,
+ pRootDir->MajorVersion,
+ pRootDir->MinorVersion,
+ pRootDir->NumberOfNamedEntries,
+ pRootDir->NumberOfIdEntries));
+
+ PIMAGE_RESOURCE_DIRECTORY_ENTRY paEntries = (PIMAGE_RESOURCE_DIRECTORY_ENTRY)(pRootDir + 1);
+ unsigned cMaxEntries = (cbBuf - sizeof(IMAGE_RESOURCE_DIRECTORY)) / sizeof(IMAGE_RESOURCE_DIRECTORY_ENTRY);
+ unsigned cEntries = pRootDir->NumberOfNamedEntries + pRootDir->NumberOfIdEntries;
+ if (cEntries > cMaxEntries)
+ cEntries = cMaxEntries;
+ for (unsigned i = 0; i < cEntries; i++)
+ {
+ if (!paEntries[i].NameIsString)
+ {
+ if (!paEntries[i].DataIsDirectory)
+ SUP_RSRC_DPRINTF((" #%u: ID: #%#06x Data: %#010x\n",
+ i, paEntries[i].Id, paEntries[i].OffsetToData));
+ else
+ SUP_RSRC_DPRINTF((" #%u: ID: #%#06x Dir: %#010x\n",
+ i, paEntries[i].Id, paEntries[i].OffsetToDirectory));
+ }
+ else
+ {
+ if (!paEntries[i].DataIsDirectory)
+ SUP_RSRC_DPRINTF((" #%u: Name: #%#06x Data: %#010x\n",
+ i, paEntries[i].NameOffset, paEntries[i].OffsetToData));
+ else
+ SUP_RSRC_DPRINTF((" #%u: Name: #%#06x Dir: %#010x\n",
+ i, paEntries[i].NameOffset, paEntries[i].OffsetToDirectory));
+ }
+
+ /*
+ * Look for the version resource type. Skip to the next entry if not found.
+ */
+ if (paEntries[i].NameIsString)
+ continue;
+ if (paEntries[i].Id != 0x10 /*RT_VERSION*/)
+ continue;
+ if (!paEntries[i].DataIsDirectory)
+ {
+ SUP_DPRINTF((" #%u: ID: #%#06x Data: %#010x - WEIRD!\n", i, paEntries[i].Id, paEntries[i].OffsetToData));
+ continue;
+ }
+ SUP_RSRC_DPRINTF((" Version resource dir entry #%u: dir offset: %#x (cbBuf=%#x)\n",
+ i, paEntries[i].OffsetToDirectory, cbBuf));
+
+ /*
+ * Locate the sub-resource directory for it.
+ */
+ if (paEntries[i].OffsetToDirectory >= cbBuf)
+ {
+ SUP_DPRINTF((" Version resource dir is outside the buffer! :-(\n"));
+ continue;
+ }
+ uint32_t cbMax = cbBuf - paEntries[i].OffsetToDirectory;
+ if (cbMax < sizeof(IMAGE_RESOURCE_DIRECTORY) + sizeof(IMAGE_RESOURCE_DIRECTORY_ENTRY))
+ {
+ SUP_DPRINTF((" Version resource dir entry #0 is outside the buffer! :-(\n"));
+ continue;
+ }
+ PIMAGE_RESOURCE_DIRECTORY pVerDir = (PIMAGE_RESOURCE_DIRECTORY)((uintptr_t)pRootDir + paEntries[i].OffsetToDirectory);
+ SUP_RSRC_DPRINTF((" VerDir: Char=%#x Time=%#x Ver=%d%d #NamedEntries=%#x #IdEntries=%#x\n",
+ pVerDir->Characteristics,
+ pVerDir->TimeDateStamp,
+ pVerDir->MajorVersion,
+ pVerDir->MinorVersion,
+ pVerDir->NumberOfNamedEntries,
+ pVerDir->NumberOfIdEntries));
+ PIMAGE_RESOURCE_DIRECTORY_ENTRY paVerEntries = (PIMAGE_RESOURCE_DIRECTORY_ENTRY)(pVerDir + 1);
+ unsigned cMaxVerEntries = (cbMax - sizeof(IMAGE_RESOURCE_DIRECTORY)) / sizeof(IMAGE_RESOURCE_DIRECTORY_ENTRY);
+ unsigned cVerEntries = pVerDir->NumberOfNamedEntries + pVerDir->NumberOfIdEntries;
+ if (cVerEntries > cMaxVerEntries)
+ cVerEntries = cMaxVerEntries;
+ for (unsigned iVer = 0; iVer < cVerEntries; iVer++)
+ {
+ if (!paVerEntries[iVer].NameIsString)
+ {
+ if (!paVerEntries[iVer].DataIsDirectory)
+ SUP_RSRC_DPRINTF((" #%u: ID: #%#06x Data: %#010x\n",
+ iVer, paVerEntries[iVer].Id, paVerEntries[iVer].OffsetToData));
+ else
+ SUP_RSRC_DPRINTF((" #%u: ID: #%#06x Dir: %#010x\n",
+ iVer, paVerEntries[iVer].Id, paVerEntries[iVer].OffsetToDirectory));
+ }
+ else
+ {
+ if (!paVerEntries[iVer].DataIsDirectory)
+ SUP_RSRC_DPRINTF((" #%u: Name: #%#06x Data: %#010x\n",
+ iVer, paVerEntries[iVer].NameOffset, paVerEntries[iVer].OffsetToData));
+ else
+ SUP_RSRC_DPRINTF((" #%u: Name: #%#06x Dir: %#010x\n",
+ iVer, paVerEntries[iVer].NameOffset, paVerEntries[iVer].OffsetToDirectory));
+ }
+ if (!paVerEntries[iVer].DataIsDirectory)
+ {
+ SUP_DPRINTF((" [Version info resource found at %#x! (ID/Name: #%#x)]\n",
+ paVerEntries[iVer].OffsetToData, paVerEntries[iVer].Name));
+ return supR3HardenedGetRvaFromRsrcDataEntry(pRootDir, cbBuf, paVerEntries[iVer].OffsetToData, pcbData);
+ }
+
+ /*
+ * Check out the next directory level.
+ */
+ if (paVerEntries[iVer].OffsetToDirectory >= cbBuf)
+ {
+ SUP_DPRINTF((" Version resource subdir is outside the buffer! :-(\n"));
+ continue;
+ }
+ cbMax = cbBuf - paVerEntries[iVer].OffsetToDirectory;
+ if (cbMax < sizeof(IMAGE_RESOURCE_DIRECTORY) + sizeof(IMAGE_RESOURCE_DIRECTORY_ENTRY))
+ {
+ SUP_DPRINTF((" Version resource subdir entry #0 is outside the buffer! :-(\n"));
+ continue;
+ }
+ PIMAGE_RESOURCE_DIRECTORY pVerSubDir = (PIMAGE_RESOURCE_DIRECTORY)((uintptr_t)pRootDir + paVerEntries[iVer].OffsetToDirectory);
+ SUP_RSRC_DPRINTF((" VerSubDir#%u: Char=%#x Time=%#x Ver=%d%d #NamedEntries=%#x #IdEntries=%#x\n",
+ iVer,
+ pVerSubDir->Characteristics,
+ pVerSubDir->TimeDateStamp,
+ pVerSubDir->MajorVersion,
+ pVerSubDir->MinorVersion,
+ pVerSubDir->NumberOfNamedEntries,
+ pVerSubDir->NumberOfIdEntries));
+ PIMAGE_RESOURCE_DIRECTORY_ENTRY paVerSubEntries = (PIMAGE_RESOURCE_DIRECTORY_ENTRY)(pVerSubDir + 1);
+ unsigned cMaxVerSubEntries = (cbMax - sizeof(IMAGE_RESOURCE_DIRECTORY)) / sizeof(IMAGE_RESOURCE_DIRECTORY_ENTRY);
+ unsigned cVerSubEntries = pVerSubDir->NumberOfNamedEntries + pVerSubDir->NumberOfIdEntries;
+ if (cVerSubEntries > cMaxVerSubEntries)
+ cVerSubEntries = cMaxVerSubEntries;
+ for (unsigned iVerSub = 0; iVerSub < cVerSubEntries; iVerSub++)
+ {
+ if (!paVerSubEntries[iVerSub].NameIsString)
+ {
+ if (!paVerSubEntries[iVerSub].DataIsDirectory)
+ SUP_RSRC_DPRINTF((" #%u: ID: #%#06x Data: %#010x\n",
+ iVerSub, paVerSubEntries[iVerSub].Id, paVerSubEntries[iVerSub].OffsetToData));
+ else
+ SUP_RSRC_DPRINTF((" #%u: ID: #%#06x Dir: %#010x\n",
+ iVerSub, paVerSubEntries[iVerSub].Id, paVerSubEntries[iVerSub].OffsetToDirectory));
+ }
+ else
+ {
+ if (!paVerSubEntries[iVerSub].DataIsDirectory)
+ SUP_RSRC_DPRINTF((" #%u: Name: #%#06x Data: %#010x\n",
+ iVerSub, paVerSubEntries[iVerSub].NameOffset, paVerSubEntries[iVerSub].OffsetToData));
+ else
+ SUP_RSRC_DPRINTF((" #%u: Name: #%#06x Dir: %#010x\n",
+ iVerSub, paVerSubEntries[iVerSub].NameOffset, paVerSubEntries[iVerSub].OffsetToDirectory));
+ }
+ if (!paVerSubEntries[iVerSub].DataIsDirectory)
+ {
+ SUP_DPRINTF((" [Version info resource found at %#x! (ID/Name: %#x; SubID/SubName: %#x)]\n",
+ paVerSubEntries[iVerSub].OffsetToData, paVerEntries[iVer].Name, paVerSubEntries[iVerSub].Name));
+ return supR3HardenedGetRvaFromRsrcDataEntry(pRootDir, cbBuf, paVerSubEntries[iVerSub].OffsetToData, pcbData);
+ }
+ }
+ }
+ }
+
+ *pcbData = 0;
+ return UINT32_MAX;
+}
+
+
+/**
+ * Logs information about a file from a protection product or from Windows,
+ * optionally returning the file version.
+ *
+ * The purpose here is to better see which version of the product is installed
+ * and not needing to depend on the user supplying the correct information.
+ *
+ * @param pwszFile The NT path to the file.
+ * @param pwszFileVersion Where to return the file version, if found. NULL if
+ * not interested.
+ * @param cwcFileVersion The size of the file version buffer (UTF-16 units).
+ */
+static void supR3HardenedLogFileInfo(PCRTUTF16 pwszFile, PRTUTF16 pwszFileVersion, size_t cwcFileVersion)
+{
+ /*
+ * Make sure the file version is always set when we return.
+ */
+ if (pwszFileVersion && cwcFileVersion)
+ *pwszFileVersion = '\0';
+
+ /*
+ * Open the file.
+ */
+ HANDLE hFile = RTNT_INVALID_HANDLE_VALUE;
+ IO_STATUS_BLOCK Ios = RTNT_IO_STATUS_BLOCK_INITIALIZER;
+ UNICODE_STRING UniStrName;
+ UniStrName.Buffer = (WCHAR *)pwszFile;
+ UniStrName.Length = (USHORT)(RTUtf16Len(pwszFile) * sizeof(WCHAR));
+ UniStrName.MaximumLength = UniStrName.Length + sizeof(WCHAR);
+ OBJECT_ATTRIBUTES ObjAttr;
+ InitializeObjectAttributes(&ObjAttr, &UniStrName, OBJ_CASE_INSENSITIVE, NULL /*hRootDir*/, NULL /*pSecDesc*/);
+ NTSTATUS rcNt = NtCreateFile(&hFile,
+ GENERIC_READ | SYNCHRONIZE,
+ &ObjAttr,
+ &Ios,
+ NULL /* Allocation Size*/,
+ FILE_ATTRIBUTE_NORMAL,
+ FILE_SHARE_READ,
+ FILE_OPEN,
+ FILE_NON_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT,
+ NULL /*EaBuffer*/,
+ 0 /*EaLength*/);
+ if (NT_SUCCESS(rcNt))
+ rcNt = Ios.Status;
+ if (NT_SUCCESS(rcNt))
+ {
+ SUP_DPRINTF(("%ls:\n", pwszFile));
+ union
+ {
+ uint64_t u64AlignmentInsurance;
+ FILE_BASIC_INFORMATION BasicInfo;
+ FILE_STANDARD_INFORMATION StdInfo;
+ uint8_t abBuf[32768];
+ RTUTF16 awcBuf[16384];
+ IMAGE_DOS_HEADER MzHdr;
+ IMAGE_RESOURCE_DIRECTORY ResDir;
+ } u;
+ RTTIMESPEC TimeSpec;
+ char szTmp[64];
+
+ /*
+ * Print basic file information available via NtQueryInformationFile.
+ */
+ RTNT_IO_STATUS_BLOCK_REINIT(&Ios);
+ rcNt = NtQueryInformationFile(hFile, &Ios, &u.BasicInfo, sizeof(u.BasicInfo), FileBasicInformation);
+ if (NT_SUCCESS(rcNt) && NT_SUCCESS(Ios.Status))
+ {
+ SUP_DPRINTF((" CreationTime: %s\n", RTTimeSpecToString(RTTimeSpecSetNtTime(&TimeSpec, u.BasicInfo.CreationTime.QuadPart), szTmp, sizeof(szTmp))));
+ /*SUP_DPRINTF((" LastAccessTime: %s\n", RTTimeSpecToString(RTTimeSpecSetNtTime(&TimeSpec, u.BasicInfo.LastAccessTime.QuadPart), szTmp, sizeof(szTmp))));*/
+ SUP_DPRINTF((" LastWriteTime: %s\n", RTTimeSpecToString(RTTimeSpecSetNtTime(&TimeSpec, u.BasicInfo.LastWriteTime.QuadPart), szTmp, sizeof(szTmp))));
+ SUP_DPRINTF((" ChangeTime: %s\n", RTTimeSpecToString(RTTimeSpecSetNtTime(&TimeSpec, u.BasicInfo.ChangeTime.QuadPart), szTmp, sizeof(szTmp))));
+ SUP_DPRINTF((" FileAttributes: %#x\n", u.BasicInfo.FileAttributes));
+ }
+ else
+ SUP_DPRINTF((" FileBasicInformation -> %#x %#x\n", rcNt, Ios.Status));
+
+ RTNT_IO_STATUS_BLOCK_REINIT(&Ios);
+ rcNt = NtQueryInformationFile(hFile, &Ios, &u.StdInfo, sizeof(u.StdInfo), FileStandardInformation);
+ if (NT_SUCCESS(rcNt) && NT_SUCCESS(Ios.Status))
+ SUP_DPRINTF((" Size: %#llx\n", u.StdInfo.EndOfFile.QuadPart));
+ else
+ SUP_DPRINTF((" FileStandardInformation -> %#x %#x\n", rcNt, Ios.Status));
+
+ /*
+ * Read the image header and extract the timestamp and other useful info.
+ */
+ RT_ZERO(u);
+ RTNT_IO_STATUS_BLOCK_REINIT(&Ios);
+ LARGE_INTEGER offRead;
+ offRead.QuadPart = 0;
+ rcNt = NtReadFile(hFile, NULL /*hEvent*/, NULL /*ApcRoutine*/, NULL /*ApcContext*/, &Ios,
+ &u, (ULONG)sizeof(u), &offRead, NULL);
+ if (NT_SUCCESS(rcNt) && NT_SUCCESS(Ios.Status))
+ {
+ uint32_t offNtHdrs = 0;
+ if (u.MzHdr.e_magic == IMAGE_DOS_SIGNATURE)
+ offNtHdrs = u.MzHdr.e_lfanew;
+ if (offNtHdrs < sizeof(u) - sizeof(IMAGE_NT_HEADERS))
+ {
+ PIMAGE_NT_HEADERS64 pNtHdrs64 = (PIMAGE_NT_HEADERS64)&u.abBuf[offNtHdrs];
+ PIMAGE_NT_HEADERS32 pNtHdrs32 = (PIMAGE_NT_HEADERS32)&u.abBuf[offNtHdrs];
+ if (pNtHdrs64->Signature == IMAGE_NT_SIGNATURE)
+ {
+ SUP_DPRINTF((" NT Headers: %#x\n", offNtHdrs));
+ SUP_DPRINTF((" Timestamp: %#x\n", pNtHdrs64->FileHeader.TimeDateStamp));
+ SUP_DPRINTF((" Machine: %#x%s\n", pNtHdrs64->FileHeader.Machine,
+ pNtHdrs64->FileHeader.Machine == IMAGE_FILE_MACHINE_I386 ? " - i386"
+ : pNtHdrs64->FileHeader.Machine == IMAGE_FILE_MACHINE_AMD64 ? " - amd64" : ""));
+ SUP_DPRINTF((" Timestamp: %#x\n", pNtHdrs64->FileHeader.TimeDateStamp));
+ SUP_DPRINTF((" Image Version: %u.%u\n",
+ pNtHdrs64->OptionalHeader.MajorImageVersion, pNtHdrs64->OptionalHeader.MinorImageVersion));
+ SUP_DPRINTF((" SizeOfImage: %#x (%u)\n", pNtHdrs64->OptionalHeader.SizeOfImage, pNtHdrs64->OptionalHeader.SizeOfImage));
+
+ /*
+ * Very crude way to extract info from the file version resource.
+ */
+ PIMAGE_SECTION_HEADER paSectHdrs = (PIMAGE_SECTION_HEADER)( (uintptr_t)&pNtHdrs64->OptionalHeader
+ + pNtHdrs64->FileHeader.SizeOfOptionalHeader);
+ IMAGE_DATA_DIRECTORY RsrcDir = { 0, 0 };
+ if ( pNtHdrs64->FileHeader.SizeOfOptionalHeader == sizeof(IMAGE_OPTIONAL_HEADER64)
+ && pNtHdrs64->OptionalHeader.NumberOfRvaAndSizes > IMAGE_DIRECTORY_ENTRY_RESOURCE)
+ RsrcDir = pNtHdrs64->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_RESOURCE];
+ else if ( pNtHdrs64->FileHeader.SizeOfOptionalHeader == sizeof(IMAGE_OPTIONAL_HEADER32)
+ && pNtHdrs32->OptionalHeader.NumberOfRvaAndSizes > IMAGE_DIRECTORY_ENTRY_RESOURCE)
+ RsrcDir = pNtHdrs32->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_RESOURCE];
+ SUP_DPRINTF((" Resource Dir: %#x LB %#x\n", RsrcDir.VirtualAddress, RsrcDir.Size));
+ if ( RsrcDir.VirtualAddress > offNtHdrs
+ && RsrcDir.Size > 0
+ && (uintptr_t)&u + sizeof(u) - (uintptr_t)paSectHdrs
+ >= pNtHdrs64->FileHeader.NumberOfSections * sizeof(IMAGE_SECTION_HEADER) )
+ {
+ uint32_t uRvaRsrcSect = 0;
+ uint32_t cbRsrcSect = 0;
+ uint32_t offRsrcSect = 0;
+ offRead.QuadPart = 0;
+ for (uint32_t i = 0; i < pNtHdrs64->FileHeader.NumberOfSections; i++)
+ {
+ uRvaRsrcSect = paSectHdrs[i].VirtualAddress;
+ cbRsrcSect = paSectHdrs[i].Misc.VirtualSize;
+ offRsrcSect = paSectHdrs[i].PointerToRawData;
+ if ( RsrcDir.VirtualAddress - uRvaRsrcSect < cbRsrcSect
+ && offRsrcSect > offNtHdrs)
+ {
+ offRead.QuadPart = offRsrcSect + (RsrcDir.VirtualAddress - uRvaRsrcSect);
+ break;
+ }
+ }
+ if (offRead.QuadPart > 0)
+ {
+ RTNT_IO_STATUS_BLOCK_REINIT(&Ios);
+ RT_ZERO(u);
+ rcNt = NtReadFile(hFile, NULL /*hEvent*/, NULL /*ApcRoutine*/, NULL /*ApcContext*/, &Ios,
+ &u, (ULONG)sizeof(u), &offRead, NULL);
+ PCRTUTF16 pwcVersionData = &u.awcBuf[0];
+ size_t cbVersionData = sizeof(u);
+
+ if (NT_SUCCESS(rcNt) && NT_SUCCESS(Ios.Status))
+ {
+ /* Make it less crude by try find the version resource data. */
+ uint32_t cbVersion;
+ uint32_t uRvaVersion = supR3HardenedFindVersionRsrcRva(&u.ResDir, sizeof(u), &cbVersion);
+ NOREF(uRvaVersion);
+ if ( uRvaVersion != UINT32_MAX
+ && cbVersion < cbRsrcSect
+ && uRvaVersion - uRvaRsrcSect <= cbRsrcSect - cbVersion)
+ {
+ uint32_t const offVersion = uRvaVersion - uRvaRsrcSect;
+ if ( offVersion < sizeof(u)
+ && offVersion + cbVersion <= sizeof(u))
+ {
+ pwcVersionData = (PCRTUTF16)&u.abBuf[offVersion];
+ cbVersionData = cbVersion;
+ }
+ else
+ {
+ offRead.QuadPart = offVersion + offRsrcSect;
+ RT_ZERO(u);
+ rcNt = NtReadFile(hFile, NULL /*hEvent*/, NULL /*ApcRoutine*/, NULL /*ApcContext*/, &Ios,
+ &u, (ULONG)sizeof(u), &offRead, NULL);
+ pwcVersionData = &u.awcBuf[0];
+ cbVersionData = RT_MIN(cbVersion, sizeof(u));
+ }
+ }
+ }
+
+ if (NT_SUCCESS(rcNt) && NT_SUCCESS(Ios.Status))
+ {
+ static const struct { PCRTUTF16 pwsz; size_t cb; bool fRet; } s_abFields[] =
+ {
+#define MY_WIDE_STR_TUPLE(a_sz, a_fRet) { L ## a_sz, sizeof(L ## a_sz) - sizeof(RTUTF16), a_fRet }
+ MY_WIDE_STR_TUPLE("ProductName", false),
+ MY_WIDE_STR_TUPLE("ProductVersion", false),
+ MY_WIDE_STR_TUPLE("FileVersion", true),
+ MY_WIDE_STR_TUPLE("SpecialBuild", false),
+ MY_WIDE_STR_TUPLE("PrivateBuild", false),
+ MY_WIDE_STR_TUPLE("FileDescription", false),
+#undef MY_WIDE_STR_TUPLE
+ };
+ for (uint32_t i = 0; i < RT_ELEMENTS(s_abFields); i++)
+ {
+ if (cbVersionData <= s_abFields[i].cb + 10)
+ continue;
+ size_t cwcLeft = (cbVersionData - s_abFields[i].cb - 10) / sizeof(RTUTF16);
+ PCRTUTF16 pwc = pwcVersionData;
+ RTUTF16 const wcFirst = *s_abFields[i].pwsz;
+ while (cwcLeft-- > 0)
+ {
+ if ( pwc[0] == 1 /* wType == text */
+ && pwc[1] == wcFirst)
+ {
+ if (memcmp(pwc + 1, s_abFields[i].pwsz, s_abFields[i].cb + sizeof(RTUTF16)) == 0)
+ {
+ size_t cwcField = s_abFields[i].cb / sizeof(RTUTF16);
+ pwc += cwcField + 2;
+ cwcLeft -= cwcField + 2;
+ for (uint32_t iPadding = 0; iPadding < 3; iPadding++, pwc++, cwcLeft--)
+ if (*pwc)
+ break;
+ int rc = RTUtf16ValidateEncodingEx(pwc, cwcLeft,
+ RTSTR_VALIDATE_ENCODING_ZERO_TERMINATED);
+ if (RT_SUCCESS(rc))
+ {
+ SUP_DPRINTF((" %ls:%*s %ls",
+ s_abFields[i].pwsz, cwcField < 15 ? 15 - cwcField : 0, "", pwc));
+ if ( s_abFields[i].fRet
+ && pwszFileVersion
+ && cwcFileVersion > 1)
+ RTUtf16Copy(pwszFileVersion, cwcFileVersion, pwc);
+ }
+ else
+ SUP_DPRINTF((" %ls:%*s rc=%Rrc",
+ s_abFields[i].pwsz, cwcField < 15 ? 15 - cwcField : 0, "", rc));
+
+ break;
+ }
+ }
+ pwc++;
+ }
+ }
+ }
+ else
+ SUP_DPRINTF((" NtReadFile @%#llx -> %#x %#x\n", offRead.QuadPart, rcNt, Ios.Status));
+ }
+ else
+ SUP_DPRINTF((" Resource section not found.\n"));
+ }
+ }
+ else
+ SUP_DPRINTF((" Nt Headers @%#x: Invalid signature\n", offNtHdrs));
+ }
+ else
+ SUP_DPRINTF((" Nt Headers @%#x: out side buffer\n", offNtHdrs));
+ }
+ else
+ SUP_DPRINTF((" NtReadFile @0 -> %#x %#x\n", rcNt, Ios.Status));
+ NtClose(hFile);
+ }
+}
+
+
+/**
+ * Scans the Driver directory for drivers which may invade our processes.
+ *
+ * @returns Mask of SUPHARDNT_ADVERSARY_XXX flags.
+ *
+ * @remarks The enumeration of \\Driver normally requires administrator
+ * privileges. So, the detection we're doing here isn't always gonna
+ * work just based on that.
+ *
+ * @todo Find drivers in \\FileSystems as well, then we could detect VrNsdDrv
+ * from ViRobot APT Shield 2.0.
+ */
+static uint32_t supR3HardenedWinFindAdversaries(void)
+{
+ static const struct
+ {
+ uint32_t fAdversary;
+ const char *pszDriver;
+ } s_aDrivers[] =
+ {
+ { SUPHARDNT_ADVERSARY_SYMANTEC_SYSPLANT, "SysPlant" },
+
+ { SUPHARDNT_ADVERSARY_SYMANTEC_N360, "SRTSPX" },
+ { SUPHARDNT_ADVERSARY_SYMANTEC_N360, "SymDS" },
+ { SUPHARDNT_ADVERSARY_SYMANTEC_N360, "SymEvent" },
+ { SUPHARDNT_ADVERSARY_SYMANTEC_N360, "SymIRON" },
+ { SUPHARDNT_ADVERSARY_SYMANTEC_N360, "SymNetS" },
+
+ { SUPHARDNT_ADVERSARY_AVAST, "aswHwid" },
+ { SUPHARDNT_ADVERSARY_AVAST, "aswMonFlt" },
+ { SUPHARDNT_ADVERSARY_AVAST, "aswRdr2" },
+ { SUPHARDNT_ADVERSARY_AVAST, "aswRvrt" },
+ { SUPHARDNT_ADVERSARY_AVAST, "aswSnx" },
+ { SUPHARDNT_ADVERSARY_AVAST, "aswsp" },
+ { SUPHARDNT_ADVERSARY_AVAST, "aswStm" },
+ { SUPHARDNT_ADVERSARY_AVAST, "aswVmm" },
+
+ { SUPHARDNT_ADVERSARY_TRENDMICRO, "tmcomm" },
+ { SUPHARDNT_ADVERSARY_TRENDMICRO, "tmactmon" },
+ { SUPHARDNT_ADVERSARY_TRENDMICRO, "tmevtmgr" },
+ { SUPHARDNT_ADVERSARY_TRENDMICRO, "tmtdi" },
+ { SUPHARDNT_ADVERSARY_TRENDMICRO, "tmebc64" }, /* Titanium internet security, not officescan. */
+ { SUPHARDNT_ADVERSARY_TRENDMICRO, "tmeevw" }, /* Titanium internet security, not officescan. */
+ { SUPHARDNT_ADVERSARY_TRENDMICRO, "tmciesc" }, /* Titanium internet security, not officescan. */
+
+ { SUPHARDNT_ADVERSARY_MCAFEE, "cfwids" },
+ { SUPHARDNT_ADVERSARY_MCAFEE, "McPvDrv" },
+ { SUPHARDNT_ADVERSARY_MCAFEE, "mfeapfk" },
+ { SUPHARDNT_ADVERSARY_MCAFEE, "mfeavfk" },
+ { SUPHARDNT_ADVERSARY_MCAFEE, "mfefirek" },
+ { SUPHARDNT_ADVERSARY_MCAFEE, "mfehidk" },
+ { SUPHARDNT_ADVERSARY_MCAFEE, "mfencbdc" },
+ { SUPHARDNT_ADVERSARY_MCAFEE, "mfewfpk" },
+
+ { SUPHARDNT_ADVERSARY_KASPERSKY, "kl1" },
+ { SUPHARDNT_ADVERSARY_KASPERSKY, "klflt" },
+ { SUPHARDNT_ADVERSARY_KASPERSKY, "klif" },
+ { SUPHARDNT_ADVERSARY_KASPERSKY, "KLIM6" },
+ { SUPHARDNT_ADVERSARY_KASPERSKY, "klkbdflt" },
+ { SUPHARDNT_ADVERSARY_KASPERSKY, "klmouflt" },
+ { SUPHARDNT_ADVERSARY_KASPERSKY, "kltdi" },
+ { SUPHARDNT_ADVERSARY_KASPERSKY, "kneps" },
+
+ { SUPHARDNT_ADVERSARY_MBAM, "MBAMWebAccessControl" },
+ { SUPHARDNT_ADVERSARY_MBAM, "mbam" },
+ { SUPHARDNT_ADVERSARY_MBAM, "mbamchameleon" },
+ { SUPHARDNT_ADVERSARY_MBAM, "mwav" },
+ { SUPHARDNT_ADVERSARY_MBAM, "mbamswissarmy" },
+
+ { SUPHARDNT_ADVERSARY_AVG, "avgfwfd" },
+ { SUPHARDNT_ADVERSARY_AVG, "avgtdia" },
+
+ { SUPHARDNT_ADVERSARY_PANDA, "PSINAflt" },
+ { SUPHARDNT_ADVERSARY_PANDA, "PSINFile" },
+ { SUPHARDNT_ADVERSARY_PANDA, "PSINKNC" },
+ { SUPHARDNT_ADVERSARY_PANDA, "PSINProc" },
+ { SUPHARDNT_ADVERSARY_PANDA, "PSINProt" },
+ { SUPHARDNT_ADVERSARY_PANDA, "PSINReg" },
+ { SUPHARDNT_ADVERSARY_PANDA, "PSKMAD" },
+ { SUPHARDNT_ADVERSARY_PANDA, "NNSAlpc" },
+ { SUPHARDNT_ADVERSARY_PANDA, "NNSHttp" },
+ { SUPHARDNT_ADVERSARY_PANDA, "NNShttps" },
+ { SUPHARDNT_ADVERSARY_PANDA, "NNSIds" },
+ { SUPHARDNT_ADVERSARY_PANDA, "NNSNAHSL" },
+ { SUPHARDNT_ADVERSARY_PANDA, "NNSpicc" },
+ { SUPHARDNT_ADVERSARY_PANDA, "NNSPihsw" },
+ { SUPHARDNT_ADVERSARY_PANDA, "NNSPop3" },
+ { SUPHARDNT_ADVERSARY_PANDA, "NNSProt" },
+ { SUPHARDNT_ADVERSARY_PANDA, "NNSPrv" },
+ { SUPHARDNT_ADVERSARY_PANDA, "NNSSmtp" },
+ { SUPHARDNT_ADVERSARY_PANDA, "NNSStrm" },
+ { SUPHARDNT_ADVERSARY_PANDA, "NNStlsc" },
+
+ { SUPHARDNT_ADVERSARY_MSE, "NisDrv" },
+
+ /*{ SUPHARDNT_ADVERSARY_COMODO, "cmdguard" }, file system */
+ { SUPHARDNT_ADVERSARY_COMODO, "inspect" },
+ { SUPHARDNT_ADVERSARY_COMODO, "cmdHlp" },
+
+ { SUPHARDNT_ADVERSARY_DIGITAL_GUARDIAN_OLD, "dgmaster" },
+
+ { SUPHARDNT_ADVERSARY_CYLANCE, "cyprotectdrv" }, /* Not verified. */
+
+ { SUPHARDNT_ADVERSARY_BEYONDTRUST, "privman" }, /* Not verified. */
+ { SUPHARDNT_ADVERSARY_BEYONDTRUST, "privmanfi" }, /* Not verified. */
+
+ { SUPHARDNT_ADVERSARY_AVECTO, "PGDriver" },
+
+ { SUPHARDNT_ADVERSARY_SOPHOS, "SophosED" }, /* Not verified. */
+
+ { SUPHARDNT_ADVERSARY_HORIZON_VIEW_AGENT, "vmwicpdr" },
+ };
+
+ static const struct
+ {
+ uint32_t fAdversary;
+ PCRTUTF16 pwszFile;
+ } s_aFiles[] =
+ {
+ { SUPHARDNT_ADVERSARY_SYMANTEC_SYSPLANT, L"\\SystemRoot\\System32\\drivers\\SysPlant.sys" },
+ { SUPHARDNT_ADVERSARY_SYMANTEC_SYSPLANT, L"\\SystemRoot\\System32\\sysfer.dll" },
+ { SUPHARDNT_ADVERSARY_SYMANTEC_SYSPLANT, L"\\SystemRoot\\System32\\sysferThunk.dll" },
+
+ { SUPHARDNT_ADVERSARY_SYMANTEC_N360, L"\\SystemRoot\\System32\\drivers\\N360x64\\1505000.013\\ccsetx64.sys" },
+ { SUPHARDNT_ADVERSARY_SYMANTEC_N360, L"\\SystemRoot\\System32\\drivers\\N360x64\\1505000.013\\ironx64.sys" },
+ { SUPHARDNT_ADVERSARY_SYMANTEC_N360, L"\\SystemRoot\\System32\\drivers\\N360x64\\1505000.013\\srtsp64.sys" },
+ { SUPHARDNT_ADVERSARY_SYMANTEC_N360, L"\\SystemRoot\\System32\\drivers\\N360x64\\1505000.013\\srtspx64.sys" },
+ { SUPHARDNT_ADVERSARY_SYMANTEC_N360, L"\\SystemRoot\\System32\\drivers\\N360x64\\1505000.013\\symds64.sys" },
+ { SUPHARDNT_ADVERSARY_SYMANTEC_N360, L"\\SystemRoot\\System32\\drivers\\N360x64\\1505000.013\\symefa64.sys" },
+ { SUPHARDNT_ADVERSARY_SYMANTEC_N360, L"\\SystemRoot\\System32\\drivers\\N360x64\\1505000.013\\symelam.sys" },
+ { SUPHARDNT_ADVERSARY_SYMANTEC_N360, L"\\SystemRoot\\System32\\drivers\\N360x64\\1505000.013\\symnets.sys" },
+ { SUPHARDNT_ADVERSARY_SYMANTEC_N360, L"\\SystemRoot\\System32\\drivers\\symevent64x86.sys" },
+
+ { SUPHARDNT_ADVERSARY_AVAST, L"\\SystemRoot\\System32\\drivers\\aswHwid.sys" },
+ { SUPHARDNT_ADVERSARY_AVAST, L"\\SystemRoot\\System32\\drivers\\aswMonFlt.sys" },
+ { SUPHARDNT_ADVERSARY_AVAST, L"\\SystemRoot\\System32\\drivers\\aswRdr2.sys" },
+ { SUPHARDNT_ADVERSARY_AVAST, L"\\SystemRoot\\System32\\drivers\\aswRvrt.sys" },
+ { SUPHARDNT_ADVERSARY_AVAST, L"\\SystemRoot\\System32\\drivers\\aswSnx.sys" },
+ { SUPHARDNT_ADVERSARY_AVAST, L"\\SystemRoot\\System32\\drivers\\aswsp.sys" },
+ { SUPHARDNT_ADVERSARY_AVAST, L"\\SystemRoot\\System32\\drivers\\aswStm.sys" },
+ { SUPHARDNT_ADVERSARY_AVAST, L"\\SystemRoot\\System32\\drivers\\aswVmm.sys" },
+
+ { SUPHARDNT_ADVERSARY_TRENDMICRO, L"\\SystemRoot\\System32\\drivers\\tmcomm.sys" },
+ { SUPHARDNT_ADVERSARY_TRENDMICRO, L"\\SystemRoot\\System32\\drivers\\tmactmon.sys" },
+ { SUPHARDNT_ADVERSARY_TRENDMICRO, L"\\SystemRoot\\System32\\drivers\\tmevtmgr.sys" },
+ { SUPHARDNT_ADVERSARY_TRENDMICRO, L"\\SystemRoot\\System32\\drivers\\tmtdi.sys" },
+ { SUPHARDNT_ADVERSARY_TRENDMICRO, L"\\SystemRoot\\System32\\drivers\\tmebc64.sys" },
+ { SUPHARDNT_ADVERSARY_TRENDMICRO, L"\\SystemRoot\\System32\\drivers\\tmeevw.sys" },
+ { SUPHARDNT_ADVERSARY_TRENDMICRO, L"\\SystemRoot\\System32\\drivers\\tmciesc.sys" },
+ { SUPHARDNT_ADVERSARY_TRENDMICRO_SAKFILE, L"\\SystemRoot\\System32\\drivers\\sakfile.sys" }, /* Data Loss Prevention, not officescan. */
+ { SUPHARDNT_ADVERSARY_TRENDMICRO, L"\\SystemRoot\\System32\\drivers\\sakcd.sys" }, /* Data Loss Prevention, not officescan. */
+
+
+ { SUPHARDNT_ADVERSARY_MCAFEE, L"\\SystemRoot\\System32\\drivers\\cfwids.sys" },
+ { SUPHARDNT_ADVERSARY_MCAFEE, L"\\SystemRoot\\System32\\drivers\\McPvDrv.sys" },
+ { SUPHARDNT_ADVERSARY_MCAFEE, L"\\SystemRoot\\System32\\drivers\\mfeapfk.sys" },
+ { SUPHARDNT_ADVERSARY_MCAFEE, L"\\SystemRoot\\System32\\drivers\\mfeavfk.sys" },
+ { SUPHARDNT_ADVERSARY_MCAFEE, L"\\SystemRoot\\System32\\drivers\\mfefirek.sys" },
+ { SUPHARDNT_ADVERSARY_MCAFEE, L"\\SystemRoot\\System32\\drivers\\mfehidk.sys" },
+ { SUPHARDNT_ADVERSARY_MCAFEE, L"\\SystemRoot\\System32\\drivers\\mfencbdc.sys" },
+ { SUPHARDNT_ADVERSARY_MCAFEE, L"\\SystemRoot\\System32\\drivers\\mfewfpk.sys" },
+
+ { SUPHARDNT_ADVERSARY_KASPERSKY, L"\\SystemRoot\\System32\\drivers\\kl1.sys" },
+ { SUPHARDNT_ADVERSARY_KASPERSKY, L"\\SystemRoot\\System32\\drivers\\klflt.sys" },
+ { SUPHARDNT_ADVERSARY_KASPERSKY, L"\\SystemRoot\\System32\\drivers\\klif.sys" },
+ { SUPHARDNT_ADVERSARY_KASPERSKY, L"\\SystemRoot\\System32\\drivers\\klim6.sys" },
+ { SUPHARDNT_ADVERSARY_KASPERSKY, L"\\SystemRoot\\System32\\drivers\\klkbdflt.sys" },
+ { SUPHARDNT_ADVERSARY_KASPERSKY, L"\\SystemRoot\\System32\\drivers\\klmouflt.sys" },
+ { SUPHARDNT_ADVERSARY_KASPERSKY, L"\\SystemRoot\\System32\\drivers\\kltdi.sys" },
+ { SUPHARDNT_ADVERSARY_KASPERSKY, L"\\SystemRoot\\System32\\drivers\\kneps.sys" },
+ { SUPHARDNT_ADVERSARY_KASPERSKY, L"\\SystemRoot\\System32\\klfphc.dll" },
+
+ { SUPHARDNT_ADVERSARY_MBAM, L"\\SystemRoot\\System32\\drivers\\MBAMSwissArmy.sys" },
+ { SUPHARDNT_ADVERSARY_MBAM, L"\\SystemRoot\\System32\\drivers\\mwac.sys" },
+ { SUPHARDNT_ADVERSARY_MBAM, L"\\SystemRoot\\System32\\drivers\\mbamchameleon.sys" },
+ { SUPHARDNT_ADVERSARY_MBAM, L"\\SystemRoot\\System32\\drivers\\mbam.sys" },
+
+ { SUPHARDNT_ADVERSARY_AVG, L"\\SystemRoot\\System32\\drivers\\avgrkx64.sys" },
+ { SUPHARDNT_ADVERSARY_AVG, L"\\SystemRoot\\System32\\drivers\\avgmfx64.sys" },
+ { SUPHARDNT_ADVERSARY_AVG, L"\\SystemRoot\\System32\\drivers\\avgidsdrivera.sys" },
+ { SUPHARDNT_ADVERSARY_AVG, L"\\SystemRoot\\System32\\drivers\\avgidsha.sys" },
+ { SUPHARDNT_ADVERSARY_AVG, L"\\SystemRoot\\System32\\drivers\\avgtdia.sys" },
+ { SUPHARDNT_ADVERSARY_AVG, L"\\SystemRoot\\System32\\drivers\\avgloga.sys" },
+ { SUPHARDNT_ADVERSARY_AVG, L"\\SystemRoot\\System32\\drivers\\avgldx64.sys" },
+ { SUPHARDNT_ADVERSARY_AVG, L"\\SystemRoot\\System32\\drivers\\avgdiska.sys" },
+
+ { SUPHARDNT_ADVERSARY_PANDA, L"\\SystemRoot\\System32\\drivers\\PSINAflt.sys" },
+ { SUPHARDNT_ADVERSARY_PANDA, L"\\SystemRoot\\System32\\drivers\\PSINFile.sys" },
+ { SUPHARDNT_ADVERSARY_PANDA, L"\\SystemRoot\\System32\\drivers\\PSINKNC.sys" },
+ { SUPHARDNT_ADVERSARY_PANDA, L"\\SystemRoot\\System32\\drivers\\PSINProc.sys" },
+ { SUPHARDNT_ADVERSARY_PANDA, L"\\SystemRoot\\System32\\drivers\\PSINProt.sys" },
+ { SUPHARDNT_ADVERSARY_PANDA, L"\\SystemRoot\\System32\\drivers\\PSINReg.sys" },
+ { SUPHARDNT_ADVERSARY_PANDA, L"\\SystemRoot\\System32\\drivers\\PSKMAD.sys" },
+ { SUPHARDNT_ADVERSARY_PANDA, L"\\SystemRoot\\System32\\drivers\\NNSAlpc.sys" },
+ { SUPHARDNT_ADVERSARY_PANDA, L"\\SystemRoot\\System32\\drivers\\NNSHttp.sys" },
+ { SUPHARDNT_ADVERSARY_PANDA, L"\\SystemRoot\\System32\\drivers\\NNShttps.sys" },
+ { SUPHARDNT_ADVERSARY_PANDA, L"\\SystemRoot\\System32\\drivers\\NNSIds.sys" },
+ { SUPHARDNT_ADVERSARY_PANDA, L"\\SystemRoot\\System32\\drivers\\NNSNAHSL.sys" },
+ { SUPHARDNT_ADVERSARY_PANDA, L"\\SystemRoot\\System32\\drivers\\NNSpicc.sys" },
+ { SUPHARDNT_ADVERSARY_PANDA, L"\\SystemRoot\\System32\\drivers\\NNSPihsw.sys" },
+ { SUPHARDNT_ADVERSARY_PANDA, L"\\SystemRoot\\System32\\drivers\\NNSPop3.sys" },
+ { SUPHARDNT_ADVERSARY_PANDA, L"\\SystemRoot\\System32\\drivers\\NNSProt.sys" },
+ { SUPHARDNT_ADVERSARY_PANDA, L"\\SystemRoot\\System32\\drivers\\NNSPrv.sys" },
+ { SUPHARDNT_ADVERSARY_PANDA, L"\\SystemRoot\\System32\\drivers\\NNSSmtp.sys" },
+ { SUPHARDNT_ADVERSARY_PANDA, L"\\SystemRoot\\System32\\drivers\\NNSStrm.sys" },
+ { SUPHARDNT_ADVERSARY_PANDA, L"\\SystemRoot\\System32\\drivers\\NNStlsc.sys" },
+
+ { SUPHARDNT_ADVERSARY_MSE, L"\\SystemRoot\\System32\\drivers\\MpFilter.sys" },
+ { SUPHARDNT_ADVERSARY_MSE, L"\\SystemRoot\\System32\\drivers\\NisDrvWFP.sys" },
+
+ { SUPHARDNT_ADVERSARY_COMODO, L"\\SystemRoot\\System32\\drivers\\cmdguard.sys" },
+ { SUPHARDNT_ADVERSARY_COMODO, L"\\SystemRoot\\System32\\drivers\\cmderd.sys" },
+ { SUPHARDNT_ADVERSARY_COMODO, L"\\SystemRoot\\System32\\drivers\\inspect.sys" },
+ { SUPHARDNT_ADVERSARY_COMODO, L"\\SystemRoot\\System32\\drivers\\cmdhlp.sys" },
+ { SUPHARDNT_ADVERSARY_COMODO, L"\\SystemRoot\\System32\\drivers\\cfrmd.sys" },
+ { SUPHARDNT_ADVERSARY_COMODO, L"\\SystemRoot\\System32\\drivers\\hmd.sys" },
+ { SUPHARDNT_ADVERSARY_COMODO, L"\\SystemRoot\\System32\\guard64.dll" },
+ { SUPHARDNT_ADVERSARY_COMODO, L"\\SystemRoot\\System32\\cmdvrt64.dll" },
+ { SUPHARDNT_ADVERSARY_COMODO, L"\\SystemRoot\\System32\\cmdkbd64.dll" },
+ { SUPHARDNT_ADVERSARY_COMODO, L"\\SystemRoot\\System32\\cmdcsr.dll" },
+
+ { SUPHARDNT_ADVERSARY_ZONE_ALARM, L"\\SystemRoot\\System32\\drivers\\vsdatant.sys" },
+ { SUPHARDNT_ADVERSARY_ZONE_ALARM, L"\\SystemRoot\\System32\\AntiTheftCredentialProvider.dll" },
+
+ { SUPHARDNT_ADVERSARY_DIGITAL_GUARDIAN_OLD, L"\\SystemRoot\\System32\\drivers\\dgmaster.sys" },
+
+ { SUPHARDNT_ADVERSARY_CYLANCE, L"\\SystemRoot\\System32\\drivers\\cyprotectdrv32.sys" },
+ { SUPHARDNT_ADVERSARY_CYLANCE, L"\\SystemRoot\\System32\\drivers\\cyprotectdrv64.sys" },
+
+ { SUPHARDNT_ADVERSARY_BEYONDTRUST, L"\\SystemRoot\\System32\\drivers\\privman.sys" },
+ { SUPHARDNT_ADVERSARY_BEYONDTRUST, L"\\SystemRoot\\System32\\drivers\\privmanfi.sys" },
+ { SUPHARDNT_ADVERSARY_BEYONDTRUST, L"\\SystemRoot\\System32\\privman64.dll" },
+ { SUPHARDNT_ADVERSARY_BEYONDTRUST, L"\\SystemRoot\\System32\\privman32.dll" },
+
+ { SUPHARDNT_ADVERSARY_AVECTO, L"\\SystemRoot\\System32\\drivers\\PGDriver.sys" },
+
+ { SUPHARDNT_ADVERSARY_SOPHOS, L"\\SystemRoot\\System32\\drivers\\SophosED.sys" }, // not verified
+
+ { SUPHARDNT_ADVERSARY_HORIZON_VIEW_AGENT, L"\\SystemRoot\\System32\\drivers\\vmwicpdr.sys" },
+ { SUPHARDNT_ADVERSARY_HORIZON_VIEW_AGENT, L"\\SystemRoot\\System32\\drivers\\ftsjail.sys" },
+ };
+
+ uint32_t fFound = 0;
+
+ /*
+ * Open the driver object directory.
+ */
+ UNICODE_STRING NtDirName = RTNT_CONSTANT_UNISTR(L"\\Driver");
+
+ OBJECT_ATTRIBUTES ObjAttr;
+ InitializeObjectAttributes(&ObjAttr, &NtDirName, OBJ_CASE_INSENSITIVE, NULL /*hRootDir*/, NULL /*pSecDesc*/);
+
+ HANDLE hDir;
+ NTSTATUS rcNt = NtOpenDirectoryObject(&hDir, DIRECTORY_QUERY | FILE_LIST_DIRECTORY, &ObjAttr);
+#ifdef VBOX_STRICT
+ if (rcNt != STATUS_ACCESS_DENIED) /* non-admin */
+ SUPR3HARDENED_ASSERT_NT_SUCCESS(rcNt);
+#endif
+ if (NT_SUCCESS(rcNt))
+ {
+ /*
+ * Enumerate it, looking for the driver.
+ */
+ ULONG uObjDirCtx = 0;
+ for (;;)
+ {
+ uint32_t abBuffer[_64K + _1K];
+ ULONG cbActual;
+ rcNt = NtQueryDirectoryObject(hDir,
+ abBuffer,
+ sizeof(abBuffer) - 4, /* minus four for string terminator space. */
+ FALSE /*ReturnSingleEntry */,
+ FALSE /*RestartScan*/,
+ &uObjDirCtx,
+ &cbActual);
+ if (!NT_SUCCESS(rcNt) || cbActual < sizeof(OBJECT_DIRECTORY_INFORMATION))
+ break;
+
+ POBJECT_DIRECTORY_INFORMATION pObjDir = (POBJECT_DIRECTORY_INFORMATION)abBuffer;
+ while (pObjDir->Name.Length != 0)
+ {
+ WCHAR wcSaved = pObjDir->Name.Buffer[pObjDir->Name.Length / sizeof(WCHAR)];
+ pObjDir->Name.Buffer[pObjDir->Name.Length / sizeof(WCHAR)] = '\0';
+
+ for (uint32_t i = 0; i < RT_ELEMENTS(s_aDrivers); i++)
+ if (RTUtf16ICmpAscii(pObjDir->Name.Buffer, s_aDrivers[i].pszDriver) == 0)
+ {
+ fFound |= s_aDrivers[i].fAdversary;
+ SUP_DPRINTF(("Found driver %s (%#x)\n", s_aDrivers[i].pszDriver, s_aDrivers[i].fAdversary));
+ break;
+ }
+
+ pObjDir->Name.Buffer[pObjDir->Name.Length / sizeof(WCHAR)] = wcSaved;
+
+ /* Next directory entry. */
+ pObjDir++;
+ }
+ }
+
+ NtClose(hDir);
+ }
+ else
+ SUP_DPRINTF(("NtOpenDirectoryObject failed on \\Driver: %#x\n", rcNt));
+
+ /*
+ * Look for files.
+ */
+ for (uint32_t i = 0; i < RT_ELEMENTS(s_aFiles); i++)
+ {
+ HANDLE hFile = RTNT_INVALID_HANDLE_VALUE;
+ IO_STATUS_BLOCK Ios = RTNT_IO_STATUS_BLOCK_INITIALIZER;
+ UNICODE_STRING UniStrName;
+ UniStrName.Buffer = (WCHAR *)s_aFiles[i].pwszFile;
+ UniStrName.Length = (USHORT)(RTUtf16Len(s_aFiles[i].pwszFile) * sizeof(WCHAR));
+ UniStrName.MaximumLength = UniStrName.Length + sizeof(WCHAR);
+ InitializeObjectAttributes(&ObjAttr, &UniStrName, OBJ_CASE_INSENSITIVE, NULL /*hRootDir*/, NULL /*pSecDesc*/);
+ rcNt = NtCreateFile(&hFile, GENERIC_READ | SYNCHRONIZE, &ObjAttr, &Ios, NULL /* Allocation Size*/,
+ FILE_ATTRIBUTE_NORMAL, FILE_SHARE_READ, FILE_OPEN,
+ FILE_NON_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT, NULL /*EaBuffer*/, 0 /*EaLength*/);
+ if (NT_SUCCESS(rcNt) && NT_SUCCESS(Ios.Status))
+ {
+ fFound |= s_aFiles[i].fAdversary;
+ NtClose(hFile);
+ }
+ }
+
+ /*
+ * Log details and upgrade select adversaries.
+ */
+ SUP_DPRINTF(("supR3HardenedWinFindAdversaries: %#x\n", fFound));
+ for (uint32_t i = 0; i < RT_ELEMENTS(s_aFiles); i++)
+ if (s_aFiles[i].fAdversary & fFound)
+ {
+ if (!(s_aFiles[i].fAdversary & SUPHARDNT_ADVERSARY_DIGITAL_GUARDIAN_OLD))
+ supR3HardenedLogFileInfo(s_aFiles[i].pwszFile, NULL, 0);
+ else
+ {
+ /*
+ * See if it's a newer version of the driver which doesn't BSODs when we free
+ * its memory. To use RTStrVersionCompare we do a rough UTF-16 -> ASCII conversion.
+ */
+ union
+ {
+ char szFileVersion[64];
+ RTUTF16 wszFileVersion[32];
+ } uBuf;
+ supR3HardenedLogFileInfo(s_aFiles[i].pwszFile, uBuf.wszFileVersion, RT_ELEMENTS(uBuf.wszFileVersion));
+ if (uBuf.wszFileVersion[0])
+ {
+ for (uint32_t off = 0; off < RT_ELEMENTS(uBuf.wszFileVersion); off++)
+ {
+ RTUTF16 wch = uBuf.wszFileVersion[off];
+ uBuf.szFileVersion[off] = (char)wch;
+ if (!wch)
+ break;
+ }
+ uBuf.szFileVersion[RT_ELEMENTS(uBuf.wszFileVersion)] = '\0';
+#define VER_IN_RANGE(a_pszFirst, a_pszLast) \
+ (RTStrVersionCompare(uBuf.szFileVersion, a_pszFirst) >= 0 && RTStrVersionCompare(uBuf.szFileVersion, a_pszLast) <= 0)
+ if ( VER_IN_RANGE("7.3.2.0000", "999999999.9.9.9999")
+ || VER_IN_RANGE("7.3.1.1000", "7.3.1.3000")
+ || VER_IN_RANGE("7.3.0.3000", "7.3.0.999999999")
+ || VER_IN_RANGE("7.2.1.3000", "7.2.999999999.999999999") )
+ {
+ uint32_t const fOldFound = fFound;
+ fFound = (fOldFound & ~SUPHARDNT_ADVERSARY_DIGITAL_GUARDIAN_OLD)
+ | SUPHARDNT_ADVERSARY_DIGITAL_GUARDIAN_NEW;
+ SUP_DPRINTF(("supR3HardenedWinFindAdversaries: Found newer version: %#x -> %#x\n", fOldFound, fFound));
+ }
+ }
+ }
+ }
+
+ return fFound;
+}
+
+
+extern "C" int main(int argc, char **argv, char **envp);
+
+/**
+ * The executable entry point.
+ *
+ * This is normally taken care of by the C runtime library, but we don't want to
+ * get involved with anything as complicated like the CRT in this setup. So, we
+ * it everything ourselves, including parameter parsing.
+ */
+extern "C" void __stdcall suplibHardenedWindowsMain(void)
+{
+ RTEXITCODE rcExit = RTEXITCODE_FAILURE;
+
+ g_cSuplibHardenedWindowsMainCalls++;
+ g_enmSupR3HardenedMainState = SUPR3HARDENEDMAINSTATE_WIN_EP_CALLED;
+
+ /*
+ * Initialize the NTDLL API wrappers. This aims at bypassing patched NTDLL
+ * in all the processes leading up the VM process.
+ */
+ supR3HardenedWinInitImports();
+ g_enmSupR3HardenedMainState = SUPR3HARDENEDMAINSTATE_WIN_IMPORTS_RESOLVED;
+
+ /*
+ * Notify the parent process that we're probably capable of reporting our
+ * own errors.
+ */
+ if (g_ProcParams.hEvtParent || g_ProcParams.hEvtChild)
+ {
+ SUPR3HARDENED_ASSERT(g_fSupEarlyProcessInit);
+
+ g_ProcParams.enmRequest = kSupR3WinChildReq_CloseEvents;
+ NtSetEvent(g_ProcParams.hEvtParent, NULL);
+
+ NtClose(g_ProcParams.hEvtParent);
+ NtClose(g_ProcParams.hEvtChild);
+ g_ProcParams.hEvtParent = NULL;
+ g_ProcParams.hEvtChild = NULL;
+ }
+ else
+ SUPR3HARDENED_ASSERT(!g_fSupEarlyProcessInit);
+
+ /*
+ * After having resolved imports we patch the LdrInitializeThunk code so
+ * that it's more difficult to invade our privacy by CreateRemoteThread.
+ * We'll re-enable this after opening the driver or temporarily while respawning.
+ */
+ supR3HardenedWinDisableThreadCreation();
+
+ /*
+ * Init g_uNtVerCombined. (The code is shared with SUPR3.lib and lives in
+ * SUPHardenedVerfiyImage-win.cpp.)
+ */
+ supR3HardenedWinInitVersion(false /*fEarly*/);
+ g_enmSupR3HardenedMainState = SUPR3HARDENEDMAINSTATE_WIN_VERSION_INITIALIZED;
+
+ /*
+ * Convert the arguments to UTF-8 and open the log file if specified.
+ * This must be done as early as possible since the code below may fail.
+ */
+ PUNICODE_STRING pCmdLineStr = &NtCurrentPeb()->ProcessParameters->CommandLine;
+ int cArgs;
+ char **papszArgs = suplibCommandLineToArgvWStub(pCmdLineStr->Buffer, pCmdLineStr->Length / sizeof(WCHAR), &cArgs);
+
+ supR3HardenedOpenLog(&cArgs, papszArgs);
+
+ /*
+ * Log information about important system files.
+ */
+ supR3HardenedLogFileInfo(L"\\SystemRoot\\System32\\ntdll.dll", NULL /*pwszFileVersion*/, 0 /*cwcFileVersion*/);
+ supR3HardenedLogFileInfo(L"\\SystemRoot\\System32\\kernel32.dll", NULL /*pwszFileVersion*/, 0 /*cwcFileVersion*/);
+ supR3HardenedLogFileInfo(L"\\SystemRoot\\System32\\KernelBase.dll", NULL /*pwszFileVersion*/, 0 /*cwcFileVersion*/);
+ supR3HardenedLogFileInfo(L"\\SystemRoot\\System32\\apisetschema.dll", NULL /*pwszFileVersion*/, 0 /*cwcFileVersion*/);
+
+ /*
+ * Scan the system for adversaries, logging information about them.
+ */
+ g_fSupAdversaries = supR3HardenedWinFindAdversaries();
+
+ /*
+ * Get the executable name, make sure it's the long version.
+ */
+ DWORD cwcExecName = GetModuleFileNameW(GetModuleHandleW(NULL), g_wszSupLibHardenedExePath,
+ RT_ELEMENTS(g_wszSupLibHardenedExePath));
+ if (cwcExecName >= RT_ELEMENTS(g_wszSupLibHardenedExePath))
+ supR3HardenedFatalMsg("suplibHardenedWindowsMain", kSupInitOp_Integrity, VERR_BUFFER_OVERFLOW,
+ "The executable path is too long.");
+
+ RTUTF16 wszLong[RT_ELEMENTS(g_wszSupLibHardenedExePath)];
+ DWORD cwcLong = GetLongPathNameW(g_wszSupLibHardenedExePath, wszLong, RT_ELEMENTS(wszLong));
+ if (cwcLong > 0)
+ {
+ memcpy(g_wszSupLibHardenedExePath, wszLong, (cwcLong + 1) * sizeof(RTUTF16));
+ cwcExecName = cwcLong;
+ }
+
+ /* The NT version of it. */
+ HANDLE hFile = CreateFileW(g_wszSupLibHardenedExePath, GENERIC_READ, FILE_SHARE_READ, NULL /*pSecurityAttributes*/,
+ OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL /*hTemplateFile*/);
+ if (hFile == NULL || hFile == INVALID_HANDLE_VALUE)
+ supR3HardenedFatalMsg("suplibHardenedWindowsMain", kSupInitOp_Integrity, RTErrConvertFromWin32(RtlGetLastWin32Error()),
+ "Error opening the executable: %u (%ls).", RtlGetLastWin32Error());
+ RT_ZERO(g_SupLibHardenedExeNtPath);
+ ULONG cbIgn;
+ NTSTATUS rcNt = NtQueryObject(hFile, ObjectNameInformation, &g_SupLibHardenedExeNtPath,
+ sizeof(g_SupLibHardenedExeNtPath) - sizeof(WCHAR), &cbIgn);
+ if (!NT_SUCCESS(rcNt))
+ supR3HardenedFatalMsg("suplibHardenedWindowsMain", kSupInitOp_Integrity, RTErrConvertFromNtStatus(rcNt),
+ "NtQueryObject -> %#x (on %ls)\n", rcNt, g_wszSupLibHardenedExePath);
+ NtClose(hFile);
+
+ /* The NT executable name offset / dir path length. */
+ g_offSupLibHardenedExeNtName = g_SupLibHardenedExeNtPath.UniStr.Length / sizeof(WCHAR);
+ while ( g_offSupLibHardenedExeNtName > 1
+ && g_SupLibHardenedExeNtPath.UniStr.Buffer[g_offSupLibHardenedExeNtName - 1] != '\\' )
+ g_offSupLibHardenedExeNtName--;
+
+ /*
+ * Preliminary app binary path init. May change when SUPR3HardenedMain is
+ * called (via main below).
+ */
+ supR3HardenedWinInitAppBin(SUPSECMAIN_FLAGS_LOC_APP_BIN);
+
+ /*
+ * If we've done early init already, register the DLL load notification
+ * callback and reinstall the NtDll patches.
+ */
+ if (g_fSupEarlyProcessInit)
+ {
+ supR3HardenedWinRegisterDllNotificationCallback();
+ supR3HardenedWinReInstallHooks(false /*fFirstCall */);
+
+ /*
+ * Flush user APCs before the g_enmSupR3HardenedMainState changes
+ * and disables the APC restrictions.
+ */
+ NtTestAlert();
+ }
+
+ /*
+ * Call the C/C++ main function.
+ */
+ SUP_DPRINTF(("Calling main()\n"));
+ rcExit = (RTEXITCODE)main(cArgs, papszArgs, NULL);
+
+ /*
+ * Exit the process (never return).
+ */
+ SUP_DPRINTF(("Terminating the normal way: rcExit=%d\n", rcExit));
+ suplibHardenedExit(rcExit);
+}
+
+
+/**
+ * Reports an error to the parent process via the process parameter structure.
+ *
+ * @param pszWhere Where this error occured, if fatal message. NULL
+ * if not message.
+ * @param enmWhat Which init operation went wrong if fatal
+ * message. kSupInitOp_Invalid if not message.
+ * @param rc The status code to report.
+ * @param pszFormat The format string.
+ * @param va The format arguments.
+ */
+DECLHIDDEN(void) supR3HardenedWinReportErrorToParent(const char *pszWhere, SUPINITOP enmWhat, int rc,
+ const char *pszFormat, va_list va)
+{
+ if (pszWhere)
+ RTStrCopy(g_ProcParams.szWhere, sizeof(g_ProcParams.szWhere), pszWhere);
+ else
+ g_ProcParams.szWhere[0] = '\0';
+ RTStrPrintfV(g_ProcParams.szErrorMsg, sizeof(g_ProcParams.szErrorMsg), pszFormat, va);
+ g_ProcParams.enmWhat = enmWhat;
+ g_ProcParams.rc = RT_SUCCESS(rc) ? VERR_INTERNAL_ERROR_2 : rc;
+ g_ProcParams.enmRequest = kSupR3WinChildReq_Error;
+
+ NtClearEvent(g_ProcParams.hEvtChild);
+ NTSTATUS rcNt = NtSetEvent(g_ProcParams.hEvtParent, NULL);
+ if (NT_SUCCESS(rcNt))
+ {
+ LARGE_INTEGER Timeout;
+ Timeout.QuadPart = -300000000; /* 30 second */
+ /*NTSTATUS rcNt =*/ NtWaitForSingleObject(g_ProcParams.hEvtChild, FALSE /*Alertable*/, &Timeout);
+ }
+}
+
+
+/**
+ * Routine called by the supR3HardenedEarlyProcessInitThunk assembly routine
+ * when LdrInitializeThunk is executed during process initialization.
+ *
+ * This initializes the Stub and VM processes, hooking NTDLL APIs and opening
+ * the device driver before any other DLLs gets loaded into the process. This
+ * greately reduces and controls the trusted code base of the process compared
+ * to opening the driver from SUPR3HardenedMain. It also avoids issues with so
+ * call protection software that is in the habit of patching half of the ntdll
+ * and kernel32 APIs in the process, making it almost indistinguishable from
+ * software that is up to no good. Once we've opened vboxdrv (renamed to
+ * vboxsup in 7.0 and 6.1.34), the process should be locked down so tightly
+ * that only kernel software and csrss can mess with the process.
+ */
+DECLASM(uintptr_t) supR3HardenedEarlyProcessInit(void)
+{
+ /*
+ * When the first thread gets here we wait for the parent to continue with
+ * the process purifications. The primary thread must execute for image
+ * load notifications to trigger, at least in more recent windows versions.
+ * The old trick of starting a different thread that terminates immediately
+ * thus doesn't work.
+ *
+ * We are not allowed to modify any data at this point because it will be
+ * reset by the child process purification the parent does when we stop. To
+ * sabotage thread creation during purification, and to avoid unnecessary
+ * work for the parent, we reset g_ProcParams before signalling the parent
+ * here.
+ */
+ if (g_enmSupR3HardenedMainState != SUPR3HARDENEDMAINSTATE_NOT_YET_CALLED)
+ {
+ NtTerminateThread(0, 0);
+ return 0x22; /* crash */
+ }
+
+ /* Retrieve the data we need. */
+ uintptr_t uNtDllAddr = ASMAtomicXchgPtrT(&g_ProcParams.uNtDllAddr, 0, uintptr_t);
+ if (!RT_VALID_PTR(uNtDllAddr))
+ {
+ NtTerminateThread(0, 0);
+ return 0x23; /* crash */
+ }
+
+ HANDLE hEvtChild = g_ProcParams.hEvtChild;
+ HANDLE hEvtParent = g_ProcParams.hEvtParent;
+ if ( hEvtChild == NULL
+ || hEvtChild == RTNT_INVALID_HANDLE_VALUE
+ || hEvtParent == NULL
+ || hEvtParent == RTNT_INVALID_HANDLE_VALUE)
+ {
+ NtTerminateThread(0, 0);
+ return 0x24; /* crash */
+ }
+
+ /* Resolve the APIs we need. */
+ PFNNTWAITFORSINGLEOBJECT pfnNtWaitForSingleObject;
+ PFNNTSETEVENT pfnNtSetEvent;
+ supR3HardenedWinGetVeryEarlyImports(uNtDllAddr, &pfnNtWaitForSingleObject, &pfnNtSetEvent);
+
+ /* Signal the parent that we're ready for purification. */
+ RT_ZERO(g_ProcParams);
+ g_ProcParams.enmRequest = kSupR3WinChildReq_PurifyChildAndCloseHandles;
+ NTSTATUS rcNt = pfnNtSetEvent(hEvtParent, NULL);
+ if (rcNt != STATUS_SUCCESS)
+ return 0x33; /* crash */
+
+ /* Wait up to 2 mins for the parent to exorcise evil. */
+ LARGE_INTEGER Timeout;
+ Timeout.QuadPart = -1200000000; /* 120 second */
+ rcNt = pfnNtWaitForSingleObject(hEvtChild, FALSE /*Alertable (never alertable before hooking!) */, &Timeout);
+ if (rcNt != STATUS_SUCCESS)
+ return 0x34; /* crash */
+
+ /*
+ * We're good to go, work global state and restore process parameters.
+ * Note that we will not restore uNtDllAddr since that is our first defence
+ * against unwanted threads (see above).
+ */
+ g_enmSupR3HardenedMainState = SUPR3HARDENEDMAINSTATE_WIN_EARLY_INIT_CALLED;
+ g_fSupEarlyProcessInit = true;
+
+ g_ProcParams.hEvtChild = hEvtChild;
+ g_ProcParams.hEvtParent = hEvtParent;
+ g_ProcParams.enmRequest = kSupR3WinChildReq_Error;
+ g_ProcParams.rc = VINF_SUCCESS;
+
+ /*
+ * Initialize the NTDLL imports that we consider usable before the
+ * process has been initialized.
+ */
+ supR3HardenedWinInitImportsEarly(uNtDllAddr);
+ g_enmSupR3HardenedMainState = SUPR3HARDENEDMAINSTATE_WIN_EARLY_IMPORTS_RESOLVED;
+
+ /*
+ * Init g_uNtVerCombined as well as we can at this point.
+ */
+ supR3HardenedWinInitVersion(true /*fEarly*/);
+
+ /*
+ * Convert the arguments to UTF-8 so we can open the log file if specified.
+ * We may have to normalize the pointer on older windows version (not w7/64 +).
+ * Note! This leaks memory at present.
+ */
+ PRTL_USER_PROCESS_PARAMETERS pUserProcParams = NtCurrentPeb()->ProcessParameters;
+ UNICODE_STRING CmdLineStr = pUserProcParams->CommandLine;
+ if ( CmdLineStr.Buffer != NULL
+ && !(pUserProcParams->Flags & RTL_USER_PROCESS_PARAMS_FLAG_NORMALIZED) )
+ CmdLineStr.Buffer = (WCHAR *)((uintptr_t)CmdLineStr.Buffer + (uintptr_t)pUserProcParams);
+ int cArgs;
+ char **papszArgs = suplibCommandLineToArgvWStub(CmdLineStr.Buffer, CmdLineStr.Length / sizeof(WCHAR), &cArgs);
+ supR3HardenedOpenLog(&cArgs, papszArgs);
+ SUP_DPRINTF(("supR3HardenedVmProcessInit: uNtDllAddr=%p g_uNtVerCombined=%#x (stack ~%p)\n",
+ uNtDllAddr, g_uNtVerCombined, &Timeout));
+
+ /*
+ * Set up the direct system calls so we can more easily hook NtCreateSection.
+ */
+ RTERRINFOSTATIC ErrInfo;
+ supR3HardenedWinInitSyscalls(true /*fReportErrors*/, RTErrInfoInitStatic(&ErrInfo));
+
+ /*
+ * Determine the executable path and name. Will NOT determine the windows style
+ * executable path here as we don't need it.
+ */
+ SIZE_T cbActual = 0;
+ rcNt = NtQueryVirtualMemory(NtCurrentProcess(), &g_ProcParams, MemorySectionName, &g_SupLibHardenedExeNtPath,
+ sizeof(g_SupLibHardenedExeNtPath) - sizeof(WCHAR), &cbActual);
+ if ( !NT_SUCCESS(rcNt)
+ || g_SupLibHardenedExeNtPath.UniStr.Length == 0
+ || g_SupLibHardenedExeNtPath.UniStr.Length & 1)
+ supR3HardenedFatal("NtQueryVirtualMemory/MemorySectionName failed in supR3HardenedVmProcessInit: %#x\n", rcNt);
+
+ /* The NT executable name offset / dir path length. */
+ g_offSupLibHardenedExeNtName = g_SupLibHardenedExeNtPath.UniStr.Length / sizeof(WCHAR);
+ while ( g_offSupLibHardenedExeNtName > 1
+ && g_SupLibHardenedExeNtPath.UniStr.Buffer[g_offSupLibHardenedExeNtName - 1] != '\\' )
+ g_offSupLibHardenedExeNtName--;
+
+ /*
+ * Preliminary app binary path init. May change when SUPR3HardenedMain is called.
+ */
+ supR3HardenedWinInitAppBin(SUPSECMAIN_FLAGS_LOC_APP_BIN);
+
+ /*
+ * Initialize the image verification stuff (hooks LdrLoadDll and NtCreateSection).
+ */
+ supR3HardenedWinInit(0, false /*fAvastKludge*/);
+
+ /*
+ * Open the driver.
+ */
+ if (cArgs >= 1 && suplibHardenedStrCmp(papszArgs[0], SUPR3_RESPAWN_1_ARG0) == 0)
+ {
+ SUP_DPRINTF(("supR3HardenedVmProcessInit: Opening vboxsup stub...\n"));
+ supR3HardenedWinOpenStubDevice();
+ g_enmSupR3HardenedMainState = SUPR3HARDENEDMAINSTATE_WIN_EARLY_STUB_DEVICE_OPENED;
+ }
+ else if (cArgs >= 1 && suplibHardenedStrCmp(papszArgs[0], SUPR3_RESPAWN_2_ARG0) == 0)
+ {
+ SUP_DPRINTF(("supR3HardenedVmProcessInit: Opening vboxsup...\n"));
+ supR3HardenedMainOpenDevice();
+ g_enmSupR3HardenedMainState = SUPR3HARDENEDMAINSTATE_WIN_EARLY_REAL_DEVICE_OPENED;
+ }
+ else
+ supR3HardenedFatal("Unexpected first argument '%s'!\n", papszArgs[0]);
+
+ /*
+ * Reinstall the NtDll patches since there is a slight possibility that
+ * someone undid them while we where busy opening the device.
+ */
+ supR3HardenedWinReInstallHooks(false /*fFirstCall */);
+
+ /*
+ * Restore the LdrInitializeThunk code so we can initialize the process
+ * normally when we return.
+ */
+ SUP_DPRINTF(("supR3HardenedVmProcessInit: Restoring LdrInitializeThunk...\n"));
+ PSUPHNTLDRCACHEENTRY pLdrEntry;
+ int rc = supHardNtLdrCacheOpen("ntdll.dll", &pLdrEntry, RTErrInfoInitStatic(&ErrInfo));
+ if (RT_FAILURE(rc))
+ supR3HardenedFatal("supR3HardenedVmProcessInit: supHardNtLdrCacheOpen failed on NTDLL: %Rrc %s\n",
+ rc, ErrInfo.Core.pszMsg);
+
+ uint8_t *pbBits;
+ rc = supHardNtLdrCacheEntryGetBits(pLdrEntry, &pbBits, uNtDllAddr, NULL, NULL, RTErrInfoInitStatic(&ErrInfo));
+ if (RT_FAILURE(rc))
+ supR3HardenedFatal("supR3HardenedVmProcessInit: supHardNtLdrCacheEntryGetBits failed on NTDLL: %Rrc %s\n",
+ rc, ErrInfo.Core.pszMsg);
+
+ RTLDRADDR uValue;
+ rc = RTLdrGetSymbolEx(pLdrEntry->hLdrMod, pbBits, uNtDllAddr, UINT32_MAX, "LdrInitializeThunk", &uValue);
+ if (RT_FAILURE(rc))
+ supR3HardenedFatal("supR3HardenedVmProcessInit: Failed to find LdrInitializeThunk (%Rrc).\n", rc);
+
+ PVOID pvLdrInitThunk = (PVOID)(uintptr_t)uValue;
+ SUPR3HARDENED_ASSERT_NT_SUCCESS(supR3HardenedWinProtectMemory(pvLdrInitThunk, 16, PAGE_EXECUTE_READWRITE));
+ memcpy(pvLdrInitThunk, pbBits + ((uintptr_t)uValue - uNtDllAddr), 16);
+ SUPR3HARDENED_ASSERT_NT_SUCCESS(supR3HardenedWinProtectMemory(pvLdrInitThunk, 16, PAGE_EXECUTE_READ));
+
+ SUP_DPRINTF(("supR3HardenedVmProcessInit: Returning to LdrInitializeThunk...\n"));
+ return (uintptr_t)pvLdrInitThunk;
+}
+
diff --git a/src/VBox/HostDrivers/Support/win/SUPR3HardenedMainA-win.asm b/src/VBox/HostDrivers/Support/win/SUPR3HardenedMainA-win.asm
new file mode 100644
index 00000000..752eddbe
--- /dev/null
+++ b/src/VBox/HostDrivers/Support/win/SUPR3HardenedMainA-win.asm
@@ -0,0 +1,404 @@
+; $Id: SUPR3HardenedMainA-win.asm $
+;; @file
+; VirtualBox Support Library - Hardened main(), Windows assembly bits.
+;
+
+;
+; Copyright (C) 2012-2023 Oracle and/or its affiliates.
+;
+; This file is part of VirtualBox base platform packages, as
+; available from https://www.virtualbox.org.
+;
+; This program is free software; you can redistribute it and/or
+; modify it under the terms of the GNU General Public License
+; as published by the Free Software Foundation, in version 3 of the
+; License.
+;
+; This program is distributed in the hope that it will be useful, but
+; WITHOUT ANY WARRANTY; without even the implied warranty of
+; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+; General Public License for more details.
+;
+; You should have received a copy of the GNU General Public License
+; along with this program; if not, see <https://www.gnu.org/licenses>.
+;
+; The contents of this file may alternatively be used under the terms
+; of the Common Development and Distribution License Version 1.0
+; (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+; in the VirtualBox distribution, in which case the provisions of the
+; CDDL are applicable instead of those of the GPL.
+;
+; You may elect to license modified versions of this file under the
+; terms and conditions of either the GPL or the CDDL or both.
+;
+; SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+;
+
+;*******************************************************************************
+;* Header Files *
+;*******************************************************************************
+%define RT_ASM_WITH_SEH64
+%include "iprt/asmdefs.mac"
+
+
+; External code.
+extern NAME(supR3HardenedEarlyProcessInit)
+extern NAME(supR3HardenedMonitor_KiUserApcDispatcher_C)
+%ifndef VBOX_WITHOUT_HARDENDED_XCPT_LOGGING
+extern NAME(supR3HardenedMonitor_KiUserExceptionDispatcher_C)
+%endif
+
+
+BEGINCODE
+
+
+;;
+; Alternative code for LdrInitializeThunk that performs the early process startup
+; for the Stub and VM processes.
+;
+; This does not concern itself with any arguments on stack or in registers that
+; may be passed to the LdrIntializeThunk routine as we just save and restore
+; them all before we restart the restored LdrInitializeThunk routine.
+;
+; @sa supR3HardenedEarlyProcessInit
+;
+BEGINPROC supR3HardenedEarlyProcessInitThunk
+ ;
+ ; Prologue.
+ ;
+
+ ; Reserve space for the "return" address.
+ push 0
+
+ ; Create a stack frame, saving xBP.
+ push xBP
+ SEH64_PUSH_xBP
+ mov xBP, xSP
+ SEH64_SET_FRAME_xBP 0 ; probably wrong...
+
+ ; Save all volatile registers.
+ push xAX
+ push xCX
+ push xDX
+%ifdef RT_ARCH_AMD64
+ push r8
+ push r9
+ push r10
+ push r11
+%endif
+
+ ; Reserve spill space and align the stack.
+ sub xSP, 20h
+ and xSP, ~0fh
+ SEH64_END_PROLOGUE
+
+ ;
+ ; Call the C/C++ code that does the actual work. This returns the
+ ; resume address in xAX, which we put in the "return" stack position.
+ ;
+ call NAME(supR3HardenedEarlyProcessInit)
+ mov [xBP + xCB], xAX
+
+ ;
+ ; Restore volatile registers.
+ ;
+ mov xAX, [xBP - xCB*1]
+ mov xCX, [xBP - xCB*2]
+ mov xDX, [xBP - xCB*3]
+%ifdef RT_ARCH_AMD64
+ mov r8, [xBP - xCB*4]
+ mov r9, [xBP - xCB*5]
+ mov r10, [xBP - xCB*6]
+ mov r11, [xBP - xCB*7]
+%endif
+ ;
+ ; Use the leave instruction to restore xBP and set up xSP to point at
+ ; the resume address. Then use the 'ret' instruction to resume process
+ ; initializaton.
+ ;
+ leave
+ ret
+ENDPROC supR3HardenedEarlyProcessInitThunk
+
+
+;;
+; Hook for KiUserApcDispatcher that validates user APC calls during early process
+; init to prevent calls going to or referring to executable memory we've freed
+; already.
+;
+; We just call C code here, just like supR3HardenedEarlyProcessInitThunk does.
+;
+; @sa supR3HardenedMonitor_KiUserApcDispatcher_C
+;
+BEGINPROC supR3HardenedMonitor_KiUserApcDispatcher
+ ;
+ ; Prologue.
+ ;
+
+ ; Reserve space for the "return" address.
+ push 0
+
+ ; Create a stack frame, saving xBP.
+ push xBP
+ SEH64_PUSH_xBP
+ mov xBP, xSP
+ SEH64_SET_FRAME_xBP 0 ; probably wrong...
+
+ ; Save all volatile registers.
+ push xAX
+ push xCX
+ push xDX
+%ifdef RT_ARCH_AMD64
+ push r8
+ push r9
+ push r10
+ push r11
+%endif
+
+ ; Reserve spill space and align the stack.
+ sub xSP, 20h
+ and xSP, ~0fh
+ SEH64_END_PROLOGUE
+
+ ;
+ ; Call the C/C++ code that does the actual work. This returns the
+ ; resume address in xAX, which we put in the "return" stack position.
+ ;
+ ; On AMD64, a CONTEXT structure is found at our RSP address when we're called.
+ ; On x86, there a 16 byte structure containing the two routines and their
+ ; arguments followed by a CONTEXT structure.
+ ;
+ lea xCX, [xBP + xCB + xCB]
+%ifdef RT_ARCH_X86
+ mov [xSP], xCX
+%endif
+ call NAME(supR3HardenedMonitor_KiUserApcDispatcher_C)
+ mov [xBP + xCB], xAX
+
+ ;
+ ; Restore volatile registers.
+ ;
+ mov xAX, [xBP - xCB*1]
+ mov xCX, [xBP - xCB*2]
+ mov xDX, [xBP - xCB*3]
+%ifdef RT_ARCH_AMD64
+ mov r8, [xBP - xCB*4]
+ mov r9, [xBP - xCB*5]
+ mov r10, [xBP - xCB*6]
+ mov r11, [xBP - xCB*7]
+%endif
+ ;
+ ; Use the leave instruction to restore xBP and set up xSP to point at
+ ; the resume address. Then use the 'ret' instruction to execute the
+ ; original KiUserApcDispatcher code as if we've never been here...
+ ;
+ leave
+ ret
+ENDPROC supR3HardenedMonitor_KiUserApcDispatcher
+
+
+%ifndef VBOX_WITHOUT_HARDENDED_XCPT_LOGGING
+;;
+; Hook for KiUserExceptionDispatcher that logs exceptions.
+;
+; For the AMD64 variant, we're not directly intercepting the function itself, but
+; patching into a Wow64 callout that's done at the very start of the routine. RCX
+; and RDX are set to PEXCEPTION_RECORD and PCONTEXT respectively and there is a
+; return address. Also, we don't need to do any return-via-copied-out-code stuff.
+;
+; For X86 we hook the function and have PEXCEPTION_RECORD and PCONTEXT pointers on
+; the stack, but no return address.
+
+; We just call C code here, just like supR3HardenedEarlyProcessInitThunk and
+; supR3HardenedMonitor_KiUserApcDispatcher does.
+;
+; @sa supR3HardenedMonitor_KiUserExceptionDispatcher_C
+;
+BEGINPROC supR3HardenedMonitor_KiUserExceptionDispatcher
+ ;
+ ; Prologue.
+ ;
+
+ %ifndef RT_ARCH_AMD64
+ ; Reserve space for the "return" address.
+ push 0
+ %endif
+
+ ; Create a stack frame, saving xBP.
+ push xBP
+ SEH64_PUSH_xBP
+ mov xBP, xSP
+ SEH64_SET_FRAME_xBP 0 ; probably wrong...
+
+ ; Save all volatile registers.
+ push xAX
+ push xCX
+ push xDX
+ %ifdef RT_ARCH_AMD64
+ push r8
+ push r9
+ push r10
+ push r11
+ %endif
+
+ ; Reserve spill space and align the stack.
+ sub xSP, 20h
+ and xSP, ~0fh
+ SEH64_END_PROLOGUE
+
+ ;
+ ; Call the C/C++ code that does the actual work. For x86 this returns
+ ; the resume address in xAX, which we put in the "return" stack position.
+ ;
+ ; On both AMD64 and X86 we have two parameters on the stack that we
+ ; passes along to the C code (see function description for details).
+ ;
+ %ifdef RT_ARCH_X86
+ mov xCX, [xBP + xCB*2]
+ mov xDX, [xBP + xCB*3]
+ mov [xSP], xCX
+ mov [xSP+4], xDX
+ %endif
+ call NAME(supR3HardenedMonitor_KiUserExceptionDispatcher_C)
+ %ifdef RT_ARCH_X86
+ mov [xBP + xCB], xAX
+ %endif
+
+ ;
+ ; Restore volatile registers.
+ ;
+ mov xAX, [xBP - xCB*1]
+ mov xCX, [xBP - xCB*2]
+ mov xDX, [xBP - xCB*3]
+ %ifdef RT_ARCH_AMD64
+ mov r8, [xBP - xCB*4]
+ mov r9, [xBP - xCB*5]
+ mov r10, [xBP - xCB*6]
+ mov r11, [xBP - xCB*7]
+ %endif
+ ;
+ ; Use the leave instruction to restore xBP and set up xSP to point at
+ ; the resume address. Then use the 'ret' instruction to execute the
+ ; original KiUserExceptionDispatcher code as if we've never been here...
+ ;
+ leave
+ ret
+ENDPROC supR3HardenedMonitor_KiUserExceptionDispatcher
+%endif ; !VBOX_WITHOUT_HARDENDED_XCPT_LOGGING
+
+;;
+; Composes a standard call name.
+%ifdef RT_ARCH_X86
+ %define SUPHNTIMP_STDCALL_NAME(a,b) _ %+ a %+ @ %+ b
+%else
+ %define SUPHNTIMP_STDCALL_NAME(a,b) NAME(a)
+%endif
+
+;; Concats two litterals.
+%define SUPHNTIMP_CONCAT(a,b) a %+ b
+
+
+;;
+; Import data and code for an API call.
+;
+; @param 1 The plain API name.
+; @param 2 The parameter frame size on x86. Multiple of dword.
+; @param 3 Non-zero expression if system call.
+; @param 4 Non-zero expression if early available call
+;
+%define SUPHNTIMP_SYSCALL 1
+%macro SupHardNtImport 4
+ ;
+ ; The data.
+ ;
+BEGINDATA
+global __imp_ %+ SUPHNTIMP_STDCALL_NAME(%1,%2) ; The import name used via dllimport.
+__imp_ %+ SUPHNTIMP_STDCALL_NAME(%1,%2):
+GLOBALNAME g_pfn %+ %1 ; The name we like to refer to.
+ RTCCPTR_DEF 0
+%if %3
+GLOBALNAME g_uApiNo %+ %1
+ RTCCPTR_DEF 0
+%endif
+
+ ;
+ ; The code: First a call stub.
+ ;
+BEGINCODE
+global SUPHNTIMP_STDCALL_NAME(%1, %2)
+SUPHNTIMP_STDCALL_NAME(%1, %2):
+ jmp RTCCPTR_PRE [NAME(g_pfn %+ %1) xWrtRIP]
+
+%if %3
+ ;
+ ; Make system calls.
+ ;
+ %ifdef RT_ARCH_AMD64
+BEGINPROC %1 %+ _SyscallType1
+ SEH64_END_PROLOGUE
+ mov eax, [NAME(g_uApiNo %+ %1) xWrtRIP]
+ mov r10, rcx
+ syscall
+ ret
+ENDPROC %1 %+ _SyscallType1
+BEGINPROC %1 %+ _SyscallType2 ; Introduced with build 10525
+ SEH64_END_PROLOGUE
+ mov eax, [NAME(g_uApiNo %+ %1) xWrtRIP]
+ test byte [07ffe0308h], 1 ; SharedUserData!Something
+ mov r10, rcx
+ jnz .int_alternative
+ syscall
+ ret
+.int_alternative:
+ int 2eh
+ ret
+ENDPROC %1 %+ _SyscallType2
+ %else
+BEGINPROC %1 %+ _SyscallType1
+ mov edx, 07ffe0300h ; SharedUserData!SystemCallStub
+ mov eax, [NAME(g_uApiNo %+ %1) xWrtRIP]
+ call dword [edx]
+ ret %2
+ENDPROC %1 %+ _SyscallType1
+BEGINPROC %1 %+ _SyscallType2
+ push .return
+ mov edx, esp
+ mov eax, [NAME(g_uApiNo %+ %1) xWrtRIP]
+ sysenter
+ add esp, 4
+.return:
+ ret %2
+ENDPROC %1 %+ _SyscallType2
+ %endif
+%endif
+
+%if %4 == 0
+global NAME(SUPHNTIMP_CONCAT(%1,_Early))
+NAME(SUPHNTIMP_CONCAT(%1,_Early)):
+ int3
+ %ifdef RT_ARCH_AMD64
+ ret
+ %else
+ ret %2
+ %endif
+%endif
+%endmacro
+
+%define SUPHARNT_COMMENT(a_Comment)
+%define SUPHARNT_IMPORT_SYSCALL(a_Name, a_cbParamsX86) SupHardNtImport a_Name, a_cbParamsX86, SUPHNTIMP_SYSCALL, 1
+%define SUPHARNT_IMPORT_STDCALL(a_Name, a_cbParamsX86) SupHardNtImport a_Name, a_cbParamsX86, 0, 0
+%define SUPHARNT_IMPORT_STDCALL_OPTIONAL(a_Name, a_cbParamsX86) SUPHARNT_IMPORT_STDCALL(a_Name, a_cbParamsX86)
+%define SUPHARNT_IMPORT_STDCALL_EARLY(a_Name, a_cbParamsX86) SupHardNtImport a_Name, a_cbParamsX86, 0, 1
+%define SUPHARNT_IMPORT_STDCALL_EARLY_OPTIONAL(a_Name, a_cbParamsX86) SUPHARNT_IMPORT_STDCALL_EARLY(a_Name, a_cbParamsX86)
+%include "import-template-ntdll.h"
+%include "import-template-kernel32.h"
+
+
+;
+; For simplified LdrLoadDll patching we define a special writable, readable and
+; exectuable section of 4KB where we can put jump back code.
+;
+section .rwxpg bss execute read write align=4096
+GLOBALNAME g_abSupHardReadWriteExecPage
+ resb 4096
+
diff --git a/src/VBox/HostDrivers/Support/win/SUPR3HardenedMainImports-win.cpp b/src/VBox/HostDrivers/Support/win/SUPR3HardenedMainImports-win.cpp
new file mode 100644
index 00000000..414188a3
--- /dev/null
+++ b/src/VBox/HostDrivers/Support/win/SUPR3HardenedMainImports-win.cpp
@@ -0,0 +1,873 @@
+/* $Id: SUPR3HardenedMainImports-win.cpp $ */
+/** @file
+ * VirtualBox Support Library - Hardened Main, Windows Import Trickery.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include <iprt/nt/nt-and-windows.h>
+
+#include <VBox/sup.h>
+#include <VBox/err.h>
+#include <iprt/ctype.h>
+#include <iprt/initterm.h>
+#include <iprt/param.h>
+#include <iprt/string.h>
+#include <iprt/utf16.h>
+
+#include "SUPLibInternal.h"
+#include "SUPHardenedVerify-win.h"
+
+
+/*********************************************************************************************************************************
+* Defined Constants And Macros *
+*********************************************************************************************************************************/
+#define SUPHARNT_COMMENT(a_Blah) /* nothing */
+
+#define VBOX_HARDENED_STUB_WITHOUT_IMPORTS
+#ifdef VBOX_HARDENED_STUB_WITHOUT_IMPORTS
+# define SUPHNTIMP_ERROR(a_fReportErrors, a_id, a_szWhere, a_enmOp, a_rc, ...) \
+ do { \
+ if (a_fReportErrors) supR3HardenedFatalMsg(a_szWhere, a_enmOp, a_rc, __VA_ARGS__); \
+ else { static const char s_szWhere[] = a_szWhere; *(char *)(uintptr_t)(a_id) += 1; __debugbreak(); } \
+ } while (0)
+#else
+# define SUPHNTIMP_ERROR(a_fReportErrors, a_id, a_szWhere, a_enmOp, a_rc, ...) \
+ supR3HardenedFatalMsg(a_szWhere, a_enmOp, a_rc, __VA_ARGS__)
+
+#endif
+
+
+/*********************************************************************************************************************************
+* Defined Constants And Macros *
+*********************************************************************************************************************************/
+/**
+ * Import function entry.
+ */
+typedef struct SUPHNTIMPFUNC
+{
+ /** The name of the function we're importing. */
+ const char *pszName;
+ /** Where to store the function address (think __imp_ApiName). */
+ PFNRT *ppfnImport;
+ /** Pointer to an early dummy function for imports that aren't available
+ * during early process initialization. */
+ PFNRT pfnEarlyDummy;
+ /** Indicates whether this is an optional import and failure to locate it
+ * should set it to NULL instead of freaking out. */
+ bool fOptional;
+} SUPHNTIMPFUNC;
+/** Pointer to an import table entry. */
+typedef SUPHNTIMPFUNC const *PCSUPHNTIMPFUNC;
+
+/**
+ * Information for constructing a direct system call.
+ */
+typedef struct SUPHNTIMPSYSCALL
+{
+ /** Where to store the system call number.
+ * NULL if this import doesn't stupport direct system call. */
+ uint32_t *puApiNo;
+ /** Assembly system call routine, type 1. */
+ PFNRT pfnType1;
+ /** Assembly system call routine, type 2. */
+ PFNRT pfnType2;
+#ifdef RT_ARCH_X86
+ /** The parameter size in bytes for a standard call. */
+ uint32_t cbParams;
+#endif
+} SUPHNTIMPSYSCALL;
+/** Pointer to a system call entry. */
+typedef SUPHNTIMPSYSCALL const *PCSUPHNTIMPSYSCALL;
+
+/**
+ * Import DLL.
+ *
+ * This contains both static (like name & imports) and runtime information (like
+ * load and export table locations).
+ *
+ * @sa RTDBGNTKRNLMODINFO
+ */
+typedef struct SUPHNTIMPDLL
+{
+ /** @name Static data.
+ * @{ */
+ const wchar_t *pwszName;
+ const char *pszName;
+ size_t cImports;
+ PCSUPHNTIMPFUNC paImports;
+ /** Array running parallel to paImports if present. */
+ PCSUPHNTIMPSYSCALL paSyscalls;
+ /** @} */
+
+
+ /** The image base. */
+ uint8_t const *pbImageBase;
+ /** The NT headers. */
+ PIMAGE_NT_HEADERS pNtHdrs;
+ /** The NT header offset/RVA. */
+ uint32_t offNtHdrs;
+ /** The end of the section headers. */
+ uint32_t offEndSectHdrs;
+ /** The end of the image. */
+ uint32_t cbImage;
+ /** Offset of the export directory. */
+ uint32_t offExportDir;
+ /** Size of the export directory. */
+ uint32_t cbExportDir;
+
+ /** Exported functions and data by ordinal (RVAs). */
+ uint32_t const *paoffExports;
+ /** The number of exports. */
+ uint32_t cExports;
+ /** The number of exported names. */
+ uint32_t cNamedExports;
+ /** Pointer to the array of exported names (RVAs to strings). */
+ uint32_t const *paoffNamedExports;
+ /** Array parallel to paoffNamedExports with the corresponding ordinals
+ * (indexes into paoffExports). */
+ uint16_t const *pau16NameOrdinals;
+
+ /** Number of patched export table entries. */
+ uint32_t cPatchedExports;
+
+} SUPHNTIMPDLL;
+/** Pointer to an import DLL entry. */
+typedef SUPHNTIMPDLL *PSUPHNTIMPDLL;
+
+
+
+/*
+ * Declare assembly symbols.
+ */
+#define SUPHARNT_IMPORT_STDCALL_EARLY(a_Name, a_cbParamsX86) \
+ extern PFNRT RT_CONCAT(g_pfn, a_Name);
+#define SUPHARNT_IMPORT_STDCALL_EARLY_OPTIONAL(a_Name, a_cbParamsX86) SUPHARNT_IMPORT_STDCALL_EARLY(a_Name, a_cbParamsX86)
+#define SUPHARNT_IMPORT_SYSCALL(a_Name, a_cbParamsX86) \
+ SUPHARNT_IMPORT_STDCALL_EARLY(a_Name, a_cbParamsX86) \
+ extern uint32_t RT_CONCAT(g_uApiNo, a_Name); \
+ extern FNRT RT_CONCAT(a_Name, _SyscallType1); \
+ extern FNRT RT_CONCAT(a_Name, _SyscallType2);
+#define SUPHARNT_IMPORT_STDCALL(a_Name, a_cbParamsX86) \
+ extern PFNRT RT_CONCAT(g_pfn, a_Name); \
+ extern FNRT RT_CONCAT(a_Name, _Early);
+#define SUPHARNT_IMPORT_STDCALL_OPTIONAL(a_Name, a_cbParamsX86) SUPHARNT_IMPORT_STDCALL(a_Name, a_cbParamsX86)
+
+RT_C_DECLS_BEGIN
+#include "import-template-ntdll.h"
+#include "import-template-kernel32.h"
+RT_C_DECLS_END
+
+/*
+ * Import functions.
+ */
+#undef SUPHARNT_IMPORT_SYSCALL
+#undef SUPHARNT_IMPORT_STDCALL_EARLY
+#undef SUPHARNT_IMPORT_STDCALL_EARLY_OPTIONAL
+#undef SUPHARNT_IMPORT_STDCALL
+#undef SUPHARNT_IMPORT_STDCALL_OPTIONAL
+#define SUPHARNT_IMPORT_SYSCALL(a_Name, a_cbParamsX86) \
+ { #a_Name, &RT_CONCAT(g_pfn, a_Name), NULL, false },
+#define SUPHARNT_IMPORT_STDCALL_EARLY(a_Name, a_cbParamsX86) \
+ { #a_Name, &RT_CONCAT(g_pfn, a_Name), NULL, false },
+#define SUPHARNT_IMPORT_STDCALL_EARLY_OPTIONAL(a_Name, a_cbParamsX86) \
+ { #a_Name, &RT_CONCAT(g_pfn, a_Name), NULL, true },
+#define SUPHARNT_IMPORT_STDCALL(a_Name, a_cbParamsX86) \
+ { #a_Name, &RT_CONCAT(g_pfn, a_Name), RT_CONCAT(a_Name,_Early), false },
+#define SUPHARNT_IMPORT_STDCALL_OPTIONAL(a_Name, a_cbParamsX86) \
+ { #a_Name, &RT_CONCAT(g_pfn, a_Name), RT_CONCAT(a_Name,_Early), true },
+static const SUPHNTIMPFUNC g_aSupNtImpNtDllFunctions[] =
+{
+#include "import-template-ntdll.h"
+};
+
+static const SUPHNTIMPFUNC g_aSupNtImpKernel32Functions[] =
+{
+#include "import-template-kernel32.h"
+};
+
+
+
+/*
+ * Syscalls in ntdll.
+ */
+#undef SUPHARNT_IMPORT_SYSCALL
+#undef SUPHARNT_IMPORT_STDCALL_EARLY
+#undef SUPHARNT_IMPORT_STDCALL_EARLY_OPTIONAL
+#undef SUPHARNT_IMPORT_STDCALL
+#undef SUPHARNT_IMPORT_STDCALL_OPTIONAL
+#ifdef RT_ARCH_AMD64
+# define SUPHARNT_IMPORT_STDCALL(a_Name, a_cbParamsX86) \
+ { NULL, NULL },
+# define SUPHARNT_IMPORT_SYSCALL(a_Name, a_cbParamsX86) \
+ { &RT_CONCAT(g_uApiNo, a_Name), &RT_CONCAT(a_Name, _SyscallType1), &RT_CONCAT(a_Name, _SyscallType2) },
+#elif defined(RT_ARCH_X86)
+# define SUPHARNT_IMPORT_STDCALL(a_Name, a_cbParamsX86) \
+ { NULL, NULL, NULL, 0 },
+# define SUPHARNT_IMPORT_SYSCALL(a_Name, a_cbParamsX86) \
+ { &RT_CONCAT(g_uApiNo, a_Name), &RT_CONCAT(a_Name,_SyscallType1), &RT_CONCAT(a_Name, _SyscallType2), a_cbParamsX86 },
+#endif
+#define SUPHARNT_IMPORT_STDCALL_OPTIONAL(a_Name, a_cbParamsX86) SUPHARNT_IMPORT_STDCALL(a_Name, a_cbParamsX86)
+#define SUPHARNT_IMPORT_STDCALL_EARLY(a_Name, a_cbParamsX86) SUPHARNT_IMPORT_STDCALL(a_Name, a_cbParamsX86)
+#define SUPHARNT_IMPORT_STDCALL_EARLY_OPTIONAL(a_Name, a_cbParamsX86) SUPHARNT_IMPORT_STDCALL(a_Name, a_cbParamsX86)
+static const SUPHNTIMPSYSCALL g_aSupNtImpNtDllSyscalls[] =
+{
+#include "import-template-ntdll.h"
+};
+
+
+/**
+ * All the DLLs we import from.
+ * @remarks Code ASSUMES that ntdll is the first entry.
+ */
+static SUPHNTIMPDLL g_aSupNtImpDlls[] =
+{
+ { L"ntdll.dll", "ntdll.dll", RT_ELEMENTS(g_aSupNtImpNtDllFunctions), g_aSupNtImpNtDllFunctions, g_aSupNtImpNtDllSyscalls },
+ { L"kernelbase.dll", "kernelbase.dll", 0 /* optional module, forwarders only */, NULL, NULL },
+ { L"kernel32.dll", "kernel32.dll", RT_ELEMENTS(g_aSupNtImpKernel32Functions), g_aSupNtImpKernel32Functions, NULL },
+};
+
+
+static void supR3HardenedFindOrLoadModule(PSUPHNTIMPDLL pDll)
+{
+#ifdef VBOX_HARDENED_STUB_WITHOUT_IMPORTS
+ uint32_t const cbName = (uint32_t)RTUtf16Len(pDll->pwszName) * sizeof(WCHAR);
+ PPEB_LDR_DATA pLdrData = NtCurrentPeb()->Ldr;
+ LIST_ENTRY *pList = &pLdrData->InMemoryOrderModuleList;
+ LIST_ENTRY *pListEntry = pList->Flink;
+ uint32_t cLoops = 0;
+ while (pListEntry != pList && cLoops < 1024)
+ {
+ PLDR_DATA_TABLE_ENTRY pLdrEntry = RT_FROM_MEMBER(pListEntry, LDR_DATA_TABLE_ENTRY, InMemoryOrderLinks);
+
+ if ( pLdrEntry->FullDllName.Length > cbName + sizeof(WCHAR)
+ && ( pLdrEntry->FullDllName.Buffer[(pLdrEntry->FullDllName.Length - cbName) / sizeof(WCHAR) - 1] == '\\'
+ || pLdrEntry->FullDllName.Buffer[(pLdrEntry->FullDllName.Length - cbName) / sizeof(WCHAR) - 1] == '/')
+ && RTUtf16ICmpAscii(&pLdrEntry->FullDllName.Buffer[(pLdrEntry->FullDllName.Length - cbName) / sizeof(WCHAR)],
+ pDll->pszName) == 0)
+ {
+ pDll->pbImageBase = (uint8_t *)pLdrEntry->DllBase;
+ return;
+ }
+
+ pListEntry = pListEntry->Flink;
+ cLoops++;
+ }
+
+ if (!pDll->cImports)
+ pDll->pbImageBase = NULL; /* optional */
+ else
+ SUPHNTIMP_ERROR(false, 1, "supR3HardenedFindOrLoadModule", kSupInitOp_Misc, VERR_MODULE_NOT_FOUND,
+ "Failed to locate %ls", pDll->pwszName);
+#else
+ HMODULE hmod = GetModuleHandleW(pDll->pwszName);
+ if (RT_UNLIKELY(!hmod && pDll->cImports))
+ SUPHNTIMP_ERROR(true, 1, "supR3HardenedWinInitImports", kSupInitOp_Misc, VERR_MODULE_NOT_FOUND,
+ "Failed to locate %ls", pDll->pwszName);
+ pDll->pbImageBase = (uint8_t *)hmod;
+#endif
+}
+
+
+/** @sa rtR0DbgKrnlNtParseModule */
+static void supR3HardenedParseModule(PSUPHNTIMPDLL pDll)
+{
+ /*
+ * Locate the PE header, do some basic validations.
+ */
+ IMAGE_DOS_HEADER const *pMzHdr = (IMAGE_DOS_HEADER const *)pDll->pbImageBase;
+ uint32_t offNtHdrs = 0;
+ PIMAGE_NT_HEADERS pNtHdrs;
+ if (pMzHdr->e_magic == IMAGE_DOS_SIGNATURE)
+ {
+ offNtHdrs = pMzHdr->e_lfanew;
+ if (offNtHdrs > _2K)
+ SUPHNTIMP_ERROR(false, 2, "supR3HardenedParseModule", kSupInitOp_Misc, VERR_MODULE_NOT_FOUND,
+ "%ls: e_lfanew=%#x, expected a lower value", pDll->pwszName, offNtHdrs);
+ }
+ pDll->pNtHdrs = pNtHdrs = (PIMAGE_NT_HEADERS)&pDll->pbImageBase[offNtHdrs];
+
+ if (pNtHdrs->Signature != IMAGE_NT_SIGNATURE)
+ SUPHNTIMP_ERROR(false, 3, "supR3HardenedParseModule", kSupInitOp_Misc, VERR_INVALID_EXE_SIGNATURE,
+ "%ls: Invalid PE signature: %#x", pDll->pwszName, pNtHdrs->Signature);
+ if (pNtHdrs->FileHeader.SizeOfOptionalHeader != sizeof(pNtHdrs->OptionalHeader))
+ SUPHNTIMP_ERROR(false, 4, "supR3HardenedParseModule", kSupInitOp_Misc, VERR_INVALID_EXE_SIGNATURE,
+ "%ls: Unexpected optional header size: %#x", pDll->pwszName, pNtHdrs->FileHeader.SizeOfOptionalHeader);
+ if (pNtHdrs->OptionalHeader.Magic != RT_CONCAT3(IMAGE_NT_OPTIONAL_HDR,ARCH_BITS,_MAGIC))
+ SUPHNTIMP_ERROR(false, 5, "supR3HardenedParseModule", kSupInitOp_Misc, VERR_INVALID_EXE_SIGNATURE,
+ "%ls: Unexpected optional header magic: %#x", pDll->pwszName, pNtHdrs->OptionalHeader.Magic);
+ if (pNtHdrs->OptionalHeader.NumberOfRvaAndSizes != IMAGE_NUMBEROF_DIRECTORY_ENTRIES)
+ SUPHNTIMP_ERROR(false, 6, "supR3HardenedParseModule", kSupInitOp_Misc, VERR_INVALID_EXE_SIGNATURE,
+ "%ls: Unexpected number of RVA and sizes: %#x", pDll->pwszName, pNtHdrs->OptionalHeader.NumberOfRvaAndSizes);
+
+ pDll->offNtHdrs = offNtHdrs;
+ pDll->offEndSectHdrs = offNtHdrs
+ + sizeof(*pNtHdrs)
+ + pNtHdrs->FileHeader.NumberOfSections * sizeof(IMAGE_SECTION_HEADER);
+ pDll->cbImage = pNtHdrs->OptionalHeader.SizeOfImage;
+
+ /*
+ * Find the export directory.
+ */
+ IMAGE_DATA_DIRECTORY ExpDir = pNtHdrs->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT];
+ if ( ExpDir.Size < sizeof(IMAGE_EXPORT_DIRECTORY)
+ || ExpDir.VirtualAddress < pDll->offEndSectHdrs
+ || ExpDir.VirtualAddress >= pNtHdrs->OptionalHeader.SizeOfImage
+ || ExpDir.VirtualAddress + ExpDir.Size > pNtHdrs->OptionalHeader.SizeOfImage)
+ SUPHNTIMP_ERROR(false, 7, "supR3HardenedParseModule", kSupInitOp_Misc, VERR_INVALID_EXE_SIGNATURE,
+ "%ls: Missing or invalid export directory: %#lx LB %#x", pDll->pwszName, ExpDir.VirtualAddress, ExpDir.Size);
+ pDll->offExportDir = ExpDir.VirtualAddress;
+ pDll->cbExportDir = ExpDir.Size;
+
+ IMAGE_EXPORT_DIRECTORY const *pExpDir = (IMAGE_EXPORT_DIRECTORY const *)&pDll->pbImageBase[ExpDir.VirtualAddress];
+
+ if ( pExpDir->NumberOfFunctions >= _1M
+ || pExpDir->NumberOfFunctions < 1
+ || pExpDir->NumberOfNames >= _1M
+ || pExpDir->NumberOfNames < 1)
+ SUPHNTIMP_ERROR(false, 8, "supR3HardenedParseModule", kSupInitOp_Misc, VERR_INVALID_EXE_SIGNATURE,
+ "%ls: NumberOfNames or/and NumberOfFunctions are outside the expected range: nof=%#x non=%#x\n",
+ pDll->pwszName, pExpDir->NumberOfFunctions, pExpDir->NumberOfNames);
+ pDll->cNamedExports = pExpDir->NumberOfNames;
+ pDll->cExports = RT_MAX(pExpDir->NumberOfNames, pExpDir->NumberOfFunctions);
+
+ if ( pExpDir->AddressOfFunctions < pDll->offEndSectHdrs
+ || pExpDir->AddressOfFunctions >= pNtHdrs->OptionalHeader.SizeOfImage
+ || pExpDir->AddressOfFunctions + pDll->cExports * sizeof(uint32_t) > pNtHdrs->OptionalHeader.SizeOfImage)
+ SUPHNTIMP_ERROR(false, 9, "supR3HardenedParseModule", kSupInitOp_Misc, VERR_INVALID_EXE_SIGNATURE,
+ "%ls: Bad AddressOfFunctions: %#x\n", pDll->pwszName, pExpDir->AddressOfFunctions);
+ pDll->paoffExports = (uint32_t const *)&pDll->pbImageBase[pExpDir->AddressOfFunctions];
+
+ if ( pExpDir->AddressOfNames < pDll->offEndSectHdrs
+ || pExpDir->AddressOfNames >= pNtHdrs->OptionalHeader.SizeOfImage
+ || pExpDir->AddressOfNames + pExpDir->NumberOfNames * sizeof(uint32_t) > pNtHdrs->OptionalHeader.SizeOfImage)
+ SUPHNTIMP_ERROR(false, 10, "supR3HardenedParseModule", kSupInitOp_Misc, VERR_INVALID_EXE_SIGNATURE,
+ "%ls: Bad AddressOfNames: %#x\n", pDll->pwszName, pExpDir->AddressOfNames);
+ pDll->paoffNamedExports = (uint32_t const *)&pDll->pbImageBase[pExpDir->AddressOfNames];
+
+ if ( pExpDir->AddressOfNameOrdinals < pDll->offEndSectHdrs
+ || pExpDir->AddressOfNameOrdinals >= pNtHdrs->OptionalHeader.SizeOfImage
+ || pExpDir->AddressOfNameOrdinals + pExpDir->NumberOfNames * sizeof(uint32_t) > pNtHdrs->OptionalHeader.SizeOfImage)
+ SUPHNTIMP_ERROR(false, 11, "supR3HardenedParseModule", kSupInitOp_Misc, VERR_INVALID_EXE_SIGNATURE,
+ "%ls: Bad AddressOfNameOrdinals: %#x\n", pDll->pwszName, pExpDir->AddressOfNameOrdinals);
+ pDll->pau16NameOrdinals = (uint16_t const *)&pDll->pbImageBase[pExpDir->AddressOfNameOrdinals];
+}
+
+
+/** @sa rtR0DbgKrnlInfoLookupSymbol */
+static const char *supR3HardenedResolveImport(PSUPHNTIMPDLL pDll, PCSUPHNTIMPFUNC pImport, bool fReportErrors)
+{
+ /*
+ * Binary search.
+ */
+ uint32_t iStart = 0;
+ uint32_t iEnd = pDll->cNamedExports;
+ while (iStart < iEnd)
+ {
+ uint32_t iCur = iStart + (iEnd - iStart) / 2;
+ uint32_t offExpName = pDll->paoffNamedExports[iCur];
+ if (RT_UNLIKELY(offExpName < pDll->offEndSectHdrs || offExpName >= pDll->cbImage))
+ SUPHNTIMP_ERROR(fReportErrors, 12, "supR3HardenedResolveImport", kSupInitOp_Misc, VERR_SYMBOL_NOT_FOUND,
+ "%ls: Bad export name entry: %#x (iCur=%#x)", pDll->pwszName, offExpName, iCur);
+
+ const char *pszExpName = (const char *)&pDll->pbImageBase[offExpName];
+ int iDiff = strcmp(pszExpName, pImport->pszName);
+ if (iDiff > 0) /* pszExpName > pszSymbol: search chunck before i */
+ iEnd = iCur;
+ else if (iDiff < 0) /* pszExpName < pszSymbol: search chunk after i */
+ iStart = iCur + 1;
+ else /* pszExpName == pszSymbol */
+ {
+ uint16_t iExpOrdinal = pDll->pau16NameOrdinals[iCur];
+ if (iExpOrdinal < pDll->cExports)
+ {
+ uint32_t offExport = pDll->paoffExports[iExpOrdinal];
+
+ /* detect export table patching. */
+ if (offExport >= pDll->cbImage)
+ pDll->cPatchedExports++;
+
+ if (offExport - pDll->offExportDir >= pDll->cbExportDir)
+ {
+ *pImport->ppfnImport = (PFNRT)&pDll->pbImageBase[offExport];
+ return NULL;
+ }
+
+ /* Forwarder. */
+ return (const char *)&pDll->pbImageBase[offExport];
+ }
+ SUPHNTIMP_ERROR(fReportErrors, 14, "supR3HardenedResolveImport", kSupInitOp_Misc, VERR_BAD_EXE_FORMAT,
+ "%ls: Name ordinal for '%s' is out of bounds: %#x (max %#x)",
+ pDll->pwszName, iExpOrdinal, pDll->cExports);
+ return NULL;
+ }
+ }
+
+ if (!pImport->fOptional)
+ SUPHNTIMP_ERROR(fReportErrors, 15, "supR3HardenedResolveImport", kSupInitOp_Misc, VERR_SYMBOL_NOT_FOUND,
+ "%ls: Failed to resolve '%s'.", pDll->pwszName, pImport->pszName);
+ *pImport->ppfnImport = NULL;
+ return NULL;
+}
+
+
+static void supR3HardenedDirectSyscall(PSUPHNTIMPDLL pDll, PCSUPHNTIMPFUNC pImport, PCSUPHNTIMPSYSCALL pSyscall,
+ PSUPHNTLDRCACHEENTRY pLdrEntry, uint8_t *pbBits, bool fReportErrors)
+{
+ /*
+ * Skip non-syscall entries.
+ */
+ if (!pSyscall->puApiNo)
+ return;
+
+ /*
+ * Locate the virgin bits.
+ */
+ RTLDRADDR uValue;
+ int rc = RTLdrGetSymbolEx(pLdrEntry->hLdrMod, pbBits, (uintptr_t)pDll->pbImageBase, UINT32_MAX, pImport->pszName, &uValue);
+ if (RT_FAILURE(rc))
+ {
+ SUPHNTIMP_ERROR(fReportErrors, 16, "supR3HardenedDirectSyscall", kSupInitOp_Misc, rc,
+ "%s: RTLdrGetSymbolEx failed on %s: %Rrc", pDll->pszName, pImport->pszName, rc);
+ return;
+ }
+ uintptr_t offSymbol = (uintptr_t)uValue - (uintptr_t)pDll->pbImageBase;
+ uint8_t const *pbFunction = &pbBits[offSymbol];
+
+ /*
+ * Parse the code and extract the API call number.
+ */
+#ifdef RT_ARCH_AMD64
+ /* Pattern #1: XP64/W2K3-64 thru Windows 10 build 10240.
+ 0:000> u ntdll!NtCreateSection
+ ntdll!NtCreateSection:
+ 00000000`779f1750 4c8bd1 mov r10,rcx
+ 00000000`779f1753 b847000000 mov eax,47h
+ 00000000`779f1758 0f05 syscall
+ 00000000`779f175a c3 ret
+ 00000000`779f175b 0f1f440000 nop dword ptr [rax+rax]
+
+ Pattern #2: Windows 10 build 10525+.
+ 0:000> u ntdll_7ffc26300000!NtCreateSection
+ ntdll_7ffc26300000!ZwCreateSection:
+ 00007ffc`263943e0 4c8bd1 mov r10,rcx
+ 00007ffc`263943e3 b84a000000 mov eax,4Ah
+ 00007ffc`263943e8 f604250803fe7f01 test byte ptr [SharedUserData+0x308 (00000000`7ffe0308)],1
+ 00007ffc`263943f0 7503 jne ntdll_7ffc26300000!ZwCreateSection+0x15 (00007ffc`263943f5)
+ 00007ffc`263943f2 0f05 syscall
+ 00007ffc`263943f4 c3 ret
+ 00007ffc`263943f5 cd2e int 2Eh
+ 00007ffc`263943f7 c3 ret
+ */
+ if ( pbFunction[ 0] == 0x4c /* mov r10, rcx */
+ && pbFunction[ 1] == 0x8b
+ && pbFunction[ 2] == 0xd1
+ && pbFunction[ 3] == 0xb8 /* mov eax, 0000yyzzh */
+ //&& pbFunction[ 4] == 0xZZ
+ //&& pbFunction[ 5] == 0xYY
+ && pbFunction[ 6] == 0x00
+ && pbFunction[ 7] == 0x00)
+ {
+ if ( pbFunction[ 8] == 0x0f /* syscall */
+ && pbFunction[ 9] == 0x05
+ && pbFunction[10] == 0xc3 /* ret */ )
+ {
+ *pSyscall->puApiNo = RT_MAKE_U16(pbFunction[4], pbFunction[5]);
+ *pImport->ppfnImport = pSyscall->pfnType1;
+ return;
+ }
+ if ( pbFunction[ 8] == 0xf6 /* test byte ptr [SharedUserData+0x308 (00000000`7ffe0308)],1 */
+ && pbFunction[ 9] == 0x04
+ && pbFunction[10] == 0x25
+ && pbFunction[11] == 0x08
+ && pbFunction[12] == 0x03
+ && pbFunction[13] == 0xfe
+ && pbFunction[14] == 0x7f
+ && pbFunction[15] == 0x01
+ && pbFunction[16] == 0x75 /* jnz +3 */
+ && pbFunction[17] == 0x03
+ && pbFunction[18] == 0x0f /* syscall*/
+ && pbFunction[19] == 0x05
+ && pbFunction[20] == 0xc3 /* ret */
+ && pbFunction[21] == 0xcd /* int 2eh */
+ && pbFunction[22] == 0x2e
+ && pbFunction[23] == 0xc3 /* ret */ )
+ {
+ *pSyscall->puApiNo = RT_MAKE_U16(pbFunction[4], pbFunction[5]);
+ *pImport->ppfnImport = pSyscall->pfnType2;
+ return;
+ }
+ }
+#else
+ /* Pattern #1: XP thru Windows 7
+ kd> u ntdll!NtCreateSection
+ ntdll!NtCreateSection:
+ 7c90d160 b832000000 mov eax,32h
+ 7c90d165 ba0003fe7f mov edx,offset SharedUserData!SystemCallStub (7ffe0300)
+ 7c90d16a ff12 call dword ptr [edx]
+ 7c90d16c c21c00 ret 1Ch
+ 7c90d16f 90 nop
+ The variable bit is the value loaded into eax: XP=32h, W2K3=34h, Vista=4bh, W7=54h
+
+ Pattern #2: Windows 8.1
+ 0:000:x86> u ntdll_6a0f0000!NtCreateSection
+ ntdll_6a0f0000!NtCreateSection:
+ 6a15eabc b854010000 mov eax,154h
+ 6a15eac1 e803000000 call ntdll_6a0f0000!NtCreateSection+0xd (6a15eac9)
+ 6a15eac6 c21c00 ret 1Ch
+ 6a15eac9 8bd4 mov edx,esp
+ 6a15eacb 0f34 sysenter
+ 6a15eacd c3 ret
+ The variable bit is the value loaded into eax: W81=154h
+ Note! One nice thing here is that we can share code pattern #1. */
+
+ if ( pbFunction[ 0] == 0xb8 /* mov eax, 0000yyzzh*/
+ //&& pbFunction[ 1] <= 0xZZ
+ //&& pbFunction[ 2] <= 0xYY
+ && pbFunction[ 3] == 0x00
+ && pbFunction[ 4] == 0x00)
+ {
+ *pSyscall->puApiNo = RT_MAKE_U16(pbFunction[1], pbFunction[2]);
+ if ( pbFunction[5] == 0xba /* mov edx, offset SharedUserData!SystemCallStub */
+ && pbFunction[ 6] == 0x00
+ && pbFunction[ 7] == 0x03
+ && pbFunction[ 8] == 0xfe
+ && pbFunction[ 9] == 0x7f
+ && pbFunction[10] == 0xff /* call [edx] */
+ && pbFunction[11] == 0x12
+ && ( ( pbFunction[12] == 0xc2 /* ret 1ch */
+ && pbFunction[13] == pSyscall->cbParams
+ && pbFunction[14] == 0x00)
+ || ( pbFunction[12] == 0xc3 /* ret */
+ && pSyscall->cbParams == 0)
+ )
+ )
+ {
+ *pImport->ppfnImport = pSyscall->pfnType1;
+ return;
+ }
+
+ if ( pbFunction[ 5] == 0xe8 /* call [$+3] */
+ && RT_ABS(*(int32_t *)&pbFunction[6]) < 0x10
+ && ( ( pbFunction[10] == 0xc2 /* ret 1ch */
+ && pbFunction[11] == pSyscall->cbParams
+ && pbFunction[12] == 0x00)
+ || ( pbFunction[10] == 0xc3 /* ret */
+ && pSyscall->cbParams == 0)
+ )
+ )
+ {
+ *pImport->ppfnImport = pSyscall->pfnType2;
+ return;
+ }
+ }
+#endif
+
+ /*
+ * Failed to parse it.
+ */
+ volatile uint8_t abCopy[16];
+ memcpy((void *)&abCopy[0], pbFunction, sizeof(abCopy));
+ SUPHNTIMP_ERROR(fReportErrors, 17, "supR3HardenedWinInitImports", kSupInitOp_Misc, rc,
+ "%ls: failed to parse syscall: '%s': %.16Rhxs",
+ pDll->pwszName, pImport->pszName, &abCopy[0]);
+}
+
+
+/**
+ * Check out system calls and do the directly instead of via NtDll.
+ *
+ * We need to have access to the on disk NTDLL.DLL file as we do not trust the
+ * stuff we find in memory. Too early to verify signatures though.
+ *
+ * @param fReportErrors Whether we've got the machinery for reporting
+ * errors going already.
+ * @param pErrInfo Buffer for gathering additional error info. This
+ * is mainly to avoid consuming lots of stacks with
+ * RTERRINFOSTATIC structures.
+ */
+DECLHIDDEN(void) supR3HardenedWinInitSyscalls(bool fReportErrors, PRTERRINFO pErrInfo)
+{
+ for (uint32_t iDll = 0; iDll < RT_ELEMENTS(g_aSupNtImpDlls); iDll++)
+ if (g_aSupNtImpDlls[iDll].paSyscalls)
+ {
+ PSUPHNTLDRCACHEENTRY pLdrEntry;
+ int rc = supHardNtLdrCacheOpen(g_aSupNtImpDlls[iDll].pszName, &pLdrEntry, pErrInfo);
+ if (RT_SUCCESS(rc))
+ {
+ uint8_t *pbBits;
+ rc = supHardNtLdrCacheEntryGetBits(pLdrEntry, &pbBits, (uintptr_t)g_aSupNtImpDlls[iDll].pbImageBase,
+ NULL, NULL, pErrInfo);
+ if (RT_SUCCESS(rc))
+ {
+ for (uint32_t i = 0; i < g_aSupNtImpDlls[iDll].cImports; i++)
+ supR3HardenedDirectSyscall(&g_aSupNtImpDlls[iDll], &g_aSupNtImpDlls[iDll].paImports[i],
+ &g_aSupNtImpDlls[iDll].paSyscalls[i], pLdrEntry, pbBits, fReportErrors);
+ }
+ else
+ SUPHNTIMP_ERROR(fReportErrors, 20, "supR3HardenedWinInitImports", kSupInitOp_Misc, rc,
+ "%ls: supHardNtLdrCacheEntryGetBits failed: %Rrc %s",
+ g_aSupNtImpDlls[iDll].pwszName, rc, pErrInfo ? pErrInfo->pszMsg : "");
+ }
+ else
+ SUPHNTIMP_ERROR(fReportErrors, 21, "supR3HardenedWinInitImports", kSupInitOp_Misc, rc,
+ "%ls: supHardNtLdrCacheOpen failed: %Rrc %s",
+ g_aSupNtImpDlls[iDll].pwszName, rc, pErrInfo ? pErrInfo->pszMsg : "");
+ }
+}
+
+
+/**
+ * Resolves a few NtDll functions we need before child purification is executed.
+ *
+ * We must not permanently modify any global data here.
+ *
+ * @param uNtDllAddr The address of the NTDLL.
+ * @param ppfnNtWaitForSingleObject Where to store the NtWaitForSingleObject
+ * address.
+ * @param ppfnNtSetEvent Where to store the NtSetEvent address.
+ */
+DECLHIDDEN(void) supR3HardenedWinGetVeryEarlyImports(uintptr_t uNtDllAddr,
+ PFNNTWAITFORSINGLEOBJECT *ppfnNtWaitForSingleObject,
+ PFNNTSETEVENT *ppfnNtSetEvent)
+{
+ /*
+ * NTDLL is the first entry in the list. Save it and do the parsing.
+ */
+ SUPHNTIMPDLL SavedDllEntry = g_aSupNtImpDlls[0];
+
+ g_aSupNtImpDlls[0].pbImageBase = (uint8_t const *)uNtDllAddr;
+ supR3HardenedParseModule(&g_aSupNtImpDlls[0]);
+
+ /*
+ * Create a temporary import table for the requested APIs and resolve them.
+ */
+ SUPHNTIMPFUNC aImports[] =
+ {
+ { "NtWaitForSingleObject", (PFNRT *)ppfnNtWaitForSingleObject, NULL, false },
+ { "NtSetEvent", (PFNRT *)ppfnNtSetEvent, NULL, false },
+ };
+
+ for (uint32_t i = 0; i < RT_ELEMENTS(aImports); i++)
+ {
+ const char *pszForwarder = supR3HardenedResolveImport(&g_aSupNtImpDlls[0], &aImports[i], false);
+ if (pszForwarder)
+ SUPHNTIMP_ERROR(false, 31, "supR3HardenedWinGetVeryEarlyImports", kSupInitOp_Misc, VERR_MODULE_NOT_FOUND,
+ "ntdll: Failed to resolve forwarder '%s'.", pszForwarder);
+ }
+
+ /*
+ * Restore the NtDll entry.
+ */
+ g_aSupNtImpDlls[0] = SavedDllEntry;
+}
+
+
+/**
+ * Resolves NtDll functions we can trust calling before process init.
+ *
+ * @param uNtDllAddr The address of the NTDLL.
+ */
+DECLHIDDEN(void) supR3HardenedWinInitImportsEarly(uintptr_t uNtDllAddr)
+{
+ /*
+ * NTDLL is the first entry in the list.
+ */
+ g_aSupNtImpDlls[0].pbImageBase = (uint8_t const *)uNtDllAddr;
+ supR3HardenedParseModule(&g_aSupNtImpDlls[0]);
+ for (uint32_t i = 0; i < g_aSupNtImpDlls[0].cImports; i++)
+ if (!g_aSupNtImpDlls[0].paImports[i].pfnEarlyDummy)
+ {
+ const char *pszForwarder = supR3HardenedResolveImport(&g_aSupNtImpDlls[0], &g_aSupNtImpDlls[0].paImports[i], false);
+ if (pszForwarder)
+ SUPHNTIMP_ERROR(false, 32, "supR3HardenedWinInitImports", kSupInitOp_Misc, VERR_MODULE_NOT_FOUND,
+ "ntdll: Failed to resolve forwarder '%s'.", pszForwarder);
+ }
+ else
+ *g_aSupNtImpDlls[0].paImports[i].ppfnImport = g_aSupNtImpDlls[0].paImports[i].pfnEarlyDummy;
+
+ /*
+ * Point the other imports at the early init stubs.
+ */
+ for (uint32_t iDll = 1; iDll < RT_ELEMENTS(g_aSupNtImpDlls); iDll++)
+ for (uint32_t i = 0; i < g_aSupNtImpDlls[iDll].cImports; i++)
+ if (!g_aSupNtImpDlls[iDll].paImports[i].fOptional)
+ *g_aSupNtImpDlls[iDll].paImports[i].ppfnImport = g_aSupNtImpDlls[iDll].paImports[i].pfnEarlyDummy;
+ else
+ *g_aSupNtImpDlls[iDll].paImports[i].ppfnImport = NULL;
+}
+
+
+/**
+ * Resolves imported functions, esp. system calls from NTDLL.
+ *
+ * This crap is necessary because there are sandboxing products out there that
+ * will mess with system calls we make, just like any other wannabe userland
+ * rootkit. Kudos to microsoft for not providing a generic system call hook API
+ * in the kernel mode, which I guess is what forcing these kind of products to
+ * do ugly userland hacks that doesn't really hold water.
+ */
+DECLHIDDEN(void) supR3HardenedWinInitImports(void)
+{
+ RTERRINFOSTATIC ErrInfo;
+
+ /*
+ * Find the DLLs we will be needing first (forwarders).
+ */
+ for (uint32_t iDll = 0; iDll < RT_ELEMENTS(g_aSupNtImpDlls); iDll++)
+ {
+ supR3HardenedFindOrLoadModule(&g_aSupNtImpDlls[iDll]);
+ if (g_aSupNtImpDlls[iDll].pbImageBase)
+ supR3HardenedParseModule(&g_aSupNtImpDlls[iDll]);
+ }
+
+ /*
+ * Resolve the functions.
+ */
+ for (uint32_t iDll = 0; iDll < RT_ELEMENTS(g_aSupNtImpDlls); iDll++)
+ for (uint32_t i = 0; i < g_aSupNtImpDlls[iDll].cImports; i++)
+ {
+ const char *pszForwarder = supR3HardenedResolveImport(&g_aSupNtImpDlls[iDll], &g_aSupNtImpDlls[iDll].paImports[i],
+ false);
+ if (pszForwarder)
+ {
+ const char *pszDot = strchr(pszForwarder, '.');
+ size_t cchDllName = pszDot - pszForwarder;
+ SUPHNTIMPFUNC Tmp = g_aSupNtImpDlls[iDll].paImports[i];
+ Tmp.pszName = pszDot + 1;
+ if (cchDllName == sizeof("ntdll") - 1 && RTStrNICmp(pszForwarder, RT_STR_TUPLE("ntdll")) == 0)
+ supR3HardenedResolveImport(&g_aSupNtImpDlls[0], &Tmp, false);
+ else if (cchDllName == sizeof("kernelbase") - 1 && RTStrNICmp(pszForwarder, RT_STR_TUPLE("kernelbase")) == 0)
+ supR3HardenedResolveImport(&g_aSupNtImpDlls[1], &Tmp, false);
+ else
+ SUPHNTIMP_ERROR(false, 18, "supR3HardenedWinInitImports", kSupInitOp_Misc, VERR_MODULE_NOT_FOUND,
+ "%ls: Failed to resolve forwarder '%s'.", g_aSupNtImpDlls[iDll].pwszName, pszForwarder);
+ }
+ }
+
+ /*
+ * Do system calls directly.
+ */
+ supR3HardenedWinInitSyscalls(false, RTErrInfoInitStatic(&ErrInfo));
+
+ /*
+ * Use the on disk image to avoid export table patching. Currently
+ * ignoring errors here as can live normally without this step.
+ */
+ for (uint32_t iDll = 0; iDll < RT_ELEMENTS(g_aSupNtImpDlls); iDll++)
+ if (g_aSupNtImpDlls[iDll].cPatchedExports > 0)
+ {
+ PSUPHNTLDRCACHEENTRY pLdrEntry;
+ int rc = supHardNtLdrCacheOpen(g_aSupNtImpDlls[iDll].pszName, &pLdrEntry, RTErrInfoInitStatic(&ErrInfo));
+ if (RT_SUCCESS(rc))
+ {
+ uint8_t *pbBits;
+ rc = supHardNtLdrCacheEntryGetBits(pLdrEntry, &pbBits, (uintptr_t)g_aSupNtImpDlls[iDll].pbImageBase, NULL, NULL,
+ RTErrInfoInitStatic(&ErrInfo));
+ if (RT_SUCCESS(rc))
+ for (uint32_t i = 0; i < g_aSupNtImpDlls[iDll].cImports; i++)
+ {
+ RTLDRADDR uValue;
+ rc = RTLdrGetSymbolEx(pLdrEntry->hLdrMod, pbBits, (uintptr_t)g_aSupNtImpDlls[iDll].pbImageBase,
+ UINT32_MAX, g_aSupNtImpDlls[iDll].paImports[i].pszName, &uValue);
+ if (RT_SUCCESS(rc))
+ *g_aSupNtImpDlls[iDll].paImports[i].ppfnImport = (PFNRT)(uintptr_t)uValue;
+ }
+ }
+ }
+
+
+#if 0 /* Win7/32 ntdll!LdrpDebugFlags. */
+ *(uint8_t *)&g_aSupNtImpDlls[0].pbImageBase[0xdd770] = 0x3;
+#endif
+}
+
+
+/**
+ * Gets the address of a procedure in a DLL, ignoring our own syscall
+ * implementations.
+ *
+ * Currently restricted to NTDLL and KERNEL32
+ *
+ * @returns The procedure address.
+ * @param pszDll The DLL name.
+ * @param pszProcedure The procedure name.
+ */
+DECLHIDDEN(PFNRT) supR3HardenedWinGetRealDllSymbol(const char *pszDll, const char *pszProcedure)
+{
+ RTERRINFOSTATIC ErrInfo;
+
+ /*
+ * Look the DLL up in the import DLL table.
+ */
+ for (uint32_t iDll = 0; iDll < RT_ELEMENTS(g_aSupNtImpDlls); iDll++)
+ if (RTStrICmp(g_aSupNtImpDlls[iDll].pszName, pszDll) == 0)
+ {
+
+ PSUPHNTLDRCACHEENTRY pLdrEntry;
+ int rc = supHardNtLdrCacheOpen(g_aSupNtImpDlls[iDll].pszName, &pLdrEntry, RTErrInfoInitStatic(&ErrInfo));
+ if (RT_SUCCESS(rc))
+ {
+ uint8_t *pbBits;
+ rc = supHardNtLdrCacheEntryGetBits(pLdrEntry, &pbBits, (uintptr_t)g_aSupNtImpDlls[iDll].pbImageBase, NULL, NULL,
+ RTErrInfoInitStatic(&ErrInfo));
+ if (RT_SUCCESS(rc))
+ {
+ RTLDRADDR uValue;
+ rc = RTLdrGetSymbolEx(pLdrEntry->hLdrMod, pbBits, (uintptr_t)g_aSupNtImpDlls[iDll].pbImageBase,
+ UINT32_MAX, pszProcedure, &uValue);
+ if (RT_SUCCESS(rc))
+ return (PFNRT)(uintptr_t)uValue;
+ SUP_DPRINTF(("supR3HardenedWinGetRealDllSymbol: Error getting %s in %s -> %Rrc\n", pszProcedure, pszDll, rc));
+ }
+ else
+ SUP_DPRINTF(("supR3HardenedWinGetRealDllSymbol: supHardNtLdrCacheEntryAllocBits failed on %s: %Rrc %s\n",
+ pszDll, rc, ErrInfo.Core.pszMsg));
+ }
+ else
+ SUP_DPRINTF(("supR3HardenedWinGetRealDllSymbol: supHardNtLdrCacheOpen failed on %s: %Rrc %s\n",
+ pszDll, rc, ErrInfo.Core.pszMsg));
+
+ /* Complications, just call GetProcAddress. */
+ if (g_enmSupR3HardenedMainState >= SUPR3HARDENEDMAINSTATE_WIN_IMPORTS_RESOLVED)
+ return (PFNRT)GetProcAddress(GetModuleHandleW(g_aSupNtImpDlls[iDll].pwszName), pszProcedure);
+ return NULL;
+ }
+
+ supR3HardenedFatal("supR3HardenedWinGetRealDllSymbol: Unknown DLL %s (proc: %s)\n", pszDll, pszProcedure);
+ /* not reached */
+}
+
diff --git a/src/VBox/HostDrivers/Support/win/SUPR3HardenedNoCrt-win.cpp b/src/VBox/HostDrivers/Support/win/SUPR3HardenedNoCrt-win.cpp
new file mode 100644
index 00000000..711c6077
--- /dev/null
+++ b/src/VBox/HostDrivers/Support/win/SUPR3HardenedNoCrt-win.cpp
@@ -0,0 +1,501 @@
+/* $Id: SUPR3HardenedNoCrt-win.cpp $ */
+/** @file
+ * VirtualBox Support Library - Hardened main(), windows bits.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include <iprt/nt/nt-and-windows.h>
+#include <AccCtrl.h>
+#include <AclApi.h>
+#ifndef PROCESS_SET_LIMITED_INFORMATION
+# define PROCESS_SET_LIMITED_INFORMATION 0x2000
+#endif
+
+#include <VBox/sup.h>
+#include <iprt/errcore.h>
+#include <iprt/assert.h>
+#include <iprt/ctype.h>
+#include <iprt/heap.h>
+#include <iprt/string.h>
+#include <iprt/initterm.h>
+#include <iprt/param.h>
+#include <iprt/path.h>
+#include <iprt/mem.h>
+#include <iprt/utf16.h>
+
+#include "SUPLibInternal.h"
+#include "win/SUPHardenedVerify-win.h"
+
+
+/*
+ * assert.cpp
+ */
+
+RTDATADECL(char) g_szRTAssertMsg1[1024];
+RTDATADECL(char) g_szRTAssertMsg2[4096];
+RTDATADECL(const char * volatile) g_pszRTAssertExpr;
+RTDATADECL(const char * volatile) g_pszRTAssertFile;
+RTDATADECL(uint32_t volatile) g_u32RTAssertLine;
+RTDATADECL(const char * volatile) g_pszRTAssertFunction;
+
+
+RTDECL(bool) RTAssertMayPanic(void)
+{
+ return true;
+}
+
+
+RTDECL(void) RTAssertMsg1(const char *pszExpr, unsigned uLine, const char *pszFile, const char *pszFunction)
+{
+ /*
+ * Fill in the globals.
+ */
+ g_pszRTAssertExpr = pszExpr;
+ g_pszRTAssertFile = pszFile;
+ g_pszRTAssertFunction = pszFunction;
+ g_u32RTAssertLine = uLine;
+ RTStrPrintf(g_szRTAssertMsg1, sizeof(g_szRTAssertMsg1),
+ "\n!!Assertion Failed!!\n"
+ "Expression: %s\n"
+ "Location : %s(%d) %s\n",
+ pszExpr, pszFile, uLine, pszFunction);
+}
+
+
+RTDECL(void) RTAssertMsg2V(const char *pszFormat, va_list va)
+{
+ RTStrPrintfV(g_szRTAssertMsg2, sizeof(g_szRTAssertMsg2), pszFormat, va);
+ if (g_enmSupR3HardenedMainState < SUPR3HARDENEDMAINSTATE_CALLED_TRUSTED_MAIN)
+ supR3HardenedFatalMsg(g_pszRTAssertExpr, kSupInitOp_Misc, VERR_INTERNAL_ERROR,
+ "%s%s", g_szRTAssertMsg1, g_szRTAssertMsg2);
+ else
+ supR3HardenedError(VERR_INTERNAL_ERROR, false/*fFatal*/, "%s%s", g_szRTAssertMsg1, g_szRTAssertMsg2);
+}
+
+
+/*
+ * Memory allocator.
+ */
+
+/** The handle of the heap we're using. */
+static HANDLE g_hSupR3HardenedHeap = NULL;
+/** Number of heaps used during early process init. */
+static uint32_t g_cSupR3HardenedEarlyHeaps = 0;
+/** Early process init heaps. */
+static struct
+{
+ /** The heap handle. */
+ RTHEAPSIMPLE hHeap;
+ /** The heap block pointer. */
+ void *pvBlock;
+ /** The size of the heap block. */
+ size_t cbBlock;
+ /** Number of active allocations on this heap. */
+ size_t cAllocations;
+} g_aSupR3HardenedEarlyHeaps[8];
+
+
+static uint32_t supR3HardenedEarlyFind(void *pv) RT_NOTHROW_DEF
+{
+ uint32_t iHeap = g_cSupR3HardenedEarlyHeaps;
+ while (iHeap-- > 0)
+ if ((uintptr_t)pv - (uintptr_t)g_aSupR3HardenedEarlyHeaps[iHeap].pvBlock < g_aSupR3HardenedEarlyHeaps[iHeap].cbBlock)
+ return iHeap;
+ return UINT32_MAX;
+}
+
+
+static void supR3HardenedEarlyCompact(void) RT_NOTHROW_DEF
+{
+ uint32_t iHeap = g_cSupR3HardenedEarlyHeaps;
+ while (iHeap-- > 0)
+ if (g_aSupR3HardenedEarlyHeaps[iHeap].cAllocations == 0)
+ {
+ PVOID pvMem = g_aSupR3HardenedEarlyHeaps[iHeap].pvBlock;
+ SIZE_T cbMem = g_aSupR3HardenedEarlyHeaps[iHeap].cbBlock;
+ if (iHeap + 1 < g_cSupR3HardenedEarlyHeaps)
+ g_aSupR3HardenedEarlyHeaps[iHeap] = g_aSupR3HardenedEarlyHeaps[g_cSupR3HardenedEarlyHeaps - 1];
+ g_cSupR3HardenedEarlyHeaps--;
+
+ NTSTATUS rcNt = NtFreeVirtualMemory(NtCurrentProcess(), &pvMem, &cbMem, MEM_RELEASE);
+ Assert(NT_SUCCESS(rcNt)); RT_NOREF_PV(rcNt);
+ SUP_DPRINTF(("supR3HardenedEarlyCompact: Removed heap %#u (%#p LB %#zx)\n", iHeap, pvMem, cbMem));
+ }
+}
+
+
+static void *supR3HardenedEarlyAlloc(size_t cb, bool fZero) RT_NOTHROW_DEF
+{
+ /*
+ * Try allocate on existing heaps.
+ */
+ void *pv;
+ uint32_t iHeap = 0;
+ while (iHeap < g_cSupR3HardenedEarlyHeaps)
+ {
+ if (fZero)
+ pv = RTHeapSimpleAllocZ(g_aSupR3HardenedEarlyHeaps[iHeap].hHeap, cb, 0);
+ else
+ pv = RTHeapSimpleAlloc(g_aSupR3HardenedEarlyHeaps[iHeap].hHeap, cb, 0);
+ if (pv)
+ {
+ g_aSupR3HardenedEarlyHeaps[iHeap].cAllocations++;
+#ifdef SUPR3HARDENED_EARLY_HEAP_TRACE
+ SUP_DPRINTF(("Early heap: %p LB %#zx - alloc\n", pv, cb));
+#endif
+ return pv;
+ }
+ iHeap++;
+ }
+
+ /*
+ * Add another heap.
+ */
+ if (iHeap == RT_ELEMENTS(g_aSupR3HardenedEarlyHeaps))
+ supR3HardenedFatal("Early heap table is full (cb=%#zx).\n", cb);
+ SIZE_T cbBlock = iHeap == 0 ? _1M : g_aSupR3HardenedEarlyHeaps[iHeap - 1].cbBlock * 2;
+ while (cbBlock <= cb * 2)
+ cbBlock *= 2;
+
+ PVOID pvBlock = NULL;
+ NTSTATUS rcNt = NtAllocateVirtualMemory(NtCurrentProcess(), &pvBlock, 0 /*ZeroBits*/, &cbBlock, MEM_COMMIT, PAGE_READWRITE);
+ if (!NT_SUCCESS(rcNt))
+ supR3HardenedFatal("NtAllocateVirtualMemory(,,,%#zx,,) failed: rcNt=%#x\n", cbBlock, rcNt);
+ SUP_DPRINTF(("New simple heap: #%u %p LB %#zx (for %zu allocation)\n", iHeap, pvBlock, cbBlock, cb));
+
+ RTHEAPSIMPLE hHeap;
+ int rc = RTHeapSimpleInit(&hHeap, pvBlock, cbBlock);
+ if (RT_FAILURE(rc))
+ supR3HardenedFatal("RTHeapSimpleInit(,%p,%#zx) failed: rc=%#x\n", pvBlock, cbBlock, rc);
+
+ if (fZero)
+ pv = RTHeapSimpleAllocZ(hHeap, cb, 0);
+ else
+ pv = RTHeapSimpleAlloc(hHeap, cb, 0);
+ if (!pv)
+ supR3HardenedFatal("RTHeapSimpleAlloc[Z] failed allocating %#zx bytes on a %#zu heap.\n", cb, cbBlock);
+
+ g_aSupR3HardenedEarlyHeaps[iHeap].pvBlock = pvBlock;
+ g_aSupR3HardenedEarlyHeaps[iHeap].cbBlock = cbBlock;
+ g_aSupR3HardenedEarlyHeaps[iHeap].cAllocations = 1;
+ g_aSupR3HardenedEarlyHeaps[iHeap].hHeap = hHeap;
+
+ Assert(g_cSupR3HardenedEarlyHeaps == iHeap);
+ g_cSupR3HardenedEarlyHeaps = iHeap + 1;
+
+#ifdef SUPR3HARDENED_EARLY_HEAP_TRACE
+ SUP_DPRINTF(("Early heap: %p LB %#zx - alloc\n", pv, cb));
+#endif
+ return pv;
+}
+
+
+/**
+ * Lazy heap initialization function.
+ *
+ * @returns Heap handle.
+ */
+static HANDLE supR3HardenedHeapInit(void) RT_NOTHROW_DEF
+{
+ Assert(g_enmSupR3HardenedMainState >= SUPR3HARDENEDMAINSTATE_WIN_EP_CALLED);
+ HANDLE hHeap = RtlCreateHeap(HEAP_GROWABLE | HEAP_CLASS_PRIVATE, NULL /*HeapBase*/,
+ 0 /*ReserveSize*/, 0 /*CommitSize*/, NULL /*Lock*/, NULL /*Parameters*/);
+ if (hHeap)
+ {
+ g_hSupR3HardenedHeap = hHeap;
+ return hHeap;
+ }
+
+ supR3HardenedFatal("RtlCreateHeap failed.\n");
+ /* not reached */
+}
+
+
+/**
+ * Compacts the heaps before enter wait for parent/child.
+ */
+DECLHIDDEN(void) supR3HardenedWinCompactHeaps(void)
+{
+ if (g_hSupR3HardenedHeap)
+ RtlCompactHeap(g_hSupR3HardenedHeap, 0 /*dwFlags*/);
+ RtlCompactHeap(GetProcessHeap(), 0 /*dwFlags*/);
+ supR3HardenedEarlyCompact();
+}
+
+
+
+#undef RTMemTmpAllocTag
+RTDECL(void *) RTMemTmpAllocTag(size_t cb, const char *pszTag) RT_NO_THROW_DEF
+{
+ return RTMemAllocTag(cb, pszTag);
+}
+
+
+#undef RTMemTmpAllocZTag
+RTDECL(void *) RTMemTmpAllocZTag(size_t cb, const char *pszTag) RT_NO_THROW_DEF
+{
+ return RTMemAllocZTag(cb, pszTag);
+}
+
+
+#undef RTMemTmpFree
+RTDECL(void) RTMemTmpFree(void *pv) RT_NO_THROW_DEF
+{
+ RTMemFree(pv);
+}
+
+
+#undef RTMemAllocTag
+RTDECL(void *) RTMemAllocTag(size_t cb, const char *pszTag) RT_NO_THROW_DEF
+{
+ RT_NOREF1(pszTag);
+ HANDLE hHeap = g_hSupR3HardenedHeap;
+ if (!hHeap)
+ {
+ if ( g_fSupEarlyProcessInit
+ && g_enmSupR3HardenedMainState <= SUPR3HARDENEDMAINSTATE_WIN_EP_CALLED)
+ return supR3HardenedEarlyAlloc(cb, false /*fZero*/);
+ hHeap = supR3HardenedHeapInit();
+ }
+
+ void *pv = RtlAllocateHeap(hHeap, 0 /*fFlags*/, cb);
+ if (!pv)
+ supR3HardenedFatal("RtlAllocateHeap failed to allocate %zu bytes.\n", cb);
+ return pv;
+}
+
+
+#undef RTMemAllocZTag
+RTDECL(void *) RTMemAllocZTag(size_t cb, const char *pszTag) RT_NO_THROW_DEF
+{
+ RT_NOREF1(pszTag);
+ HANDLE hHeap = g_hSupR3HardenedHeap;
+ if (!hHeap)
+ {
+ if ( g_fSupEarlyProcessInit
+ && g_enmSupR3HardenedMainState <= SUPR3HARDENEDMAINSTATE_WIN_EP_CALLED)
+ return supR3HardenedEarlyAlloc(cb, true /*fZero*/);
+ hHeap = supR3HardenedHeapInit();
+ }
+
+ void *pv = RtlAllocateHeap(hHeap, HEAP_ZERO_MEMORY, cb);
+ if (!pv)
+ supR3HardenedFatal("RtlAllocateHeap failed to allocate %zu bytes.\n", cb);
+ return pv;
+}
+
+
+#undef RTMemAllocVarTag
+RTDECL(void *) RTMemAllocVarTag(size_t cbUnaligned, const char *pszTag) RT_NO_THROW_DEF
+{
+ size_t cbAligned;
+ if (cbUnaligned >= 16)
+ cbAligned = RT_ALIGN_Z(cbUnaligned, 16);
+ else
+ cbAligned = RT_ALIGN_Z(cbUnaligned, sizeof(void *));
+ return RTMemAllocTag(cbAligned, pszTag);
+}
+
+
+#undef RTMemAllocZVarTag
+RTDECL(void *) RTMemAllocZVarTag(size_t cbUnaligned, const char *pszTag) RT_NO_THROW_DEF
+{
+ size_t cbAligned;
+ if (cbUnaligned >= 16)
+ cbAligned = RT_ALIGN_Z(cbUnaligned, 16);
+ else
+ cbAligned = RT_ALIGN_Z(cbUnaligned, sizeof(void *));
+ return RTMemAllocZTag(cbAligned, pszTag);
+}
+
+
+#undef RTMemReallocTag
+RTDECL(void *) RTMemReallocTag(void *pvOld, size_t cbNew, const char *pszTag) RT_NO_THROW_DEF
+{
+ if (!pvOld)
+ return RTMemAllocZTag(cbNew, pszTag);
+
+ void *pv;
+ if (g_fSupEarlyProcessInit)
+ {
+ uint32_t iHeap = supR3HardenedEarlyFind(pvOld);
+ if (iHeap != UINT32_MAX)
+ {
+#if 0 /* RTHeapSimpleRealloc is not implemented */
+ /* If this is before we can use a regular heap, we try resize
+ within the simple heap. (There are a lot of array growing in
+ the ASN.1 code.) */
+ if (g_enmSupR3HardenedMainState < SUPR3HARDENEDMAINSTATE_WIN_IMPORTS_RESOLVED)
+ {
+ pv = RTHeapSimpleRealloc(g_aSupR3HardenedEarlyHeaps[iHeap].hHeap, pvOld, cbNew, 0);
+ if (pv)
+ {
+# ifdef SUPR3HARDENED_EARLY_HEAP_TRACE
+ SUP_DPRINTF(("Early heap: %p LB %#zx, was %p - realloc\n", pvNew, cbNew, pvOld));
+# endif
+ return pv;
+ }
+ }
+#endif
+
+ /* Either we can't reallocate it on the same simple heap, or we're
+ past hardened main and wish to migrate everything over on the
+ real heap. */
+ size_t cbOld = RTHeapSimpleSize(g_aSupR3HardenedEarlyHeaps[iHeap].hHeap, pvOld);
+ pv = RTMemAllocTag(cbNew, pszTag);
+ if (pv)
+ {
+ memcpy(pv, pvOld, RT_MIN(cbOld, cbNew));
+ RTHeapSimpleFree(g_aSupR3HardenedEarlyHeaps[iHeap].hHeap, pvOld);
+ if (g_aSupR3HardenedEarlyHeaps[iHeap].cAllocations)
+ g_aSupR3HardenedEarlyHeaps[iHeap].cAllocations--;
+ if ( !g_aSupR3HardenedEarlyHeaps[iHeap].cAllocations
+ && g_enmSupR3HardenedMainState >= SUPR3HARDENEDMAINSTATE_WIN_IMPORTS_RESOLVED)
+ supR3HardenedEarlyCompact();
+ }
+# ifdef SUPR3HARDENED_EARLY_HEAP_TRACE
+ SUP_DPRINTF(("Early heap: %p LB %#zx, was %p %LB %#zx - realloc\n", pv, cbNew, pvOld, cbOld));
+# endif
+ return pv;
+ }
+ Assert(g_enmSupR3HardenedMainState >= SUPR3HARDENEDMAINSTATE_WIN_IMPORTS_RESOLVED);
+ }
+
+ /* Allocate from the regular heap. */
+ HANDLE hHeap = g_hSupR3HardenedHeap;
+ Assert(hHeap != NULL);
+ pv = RtlReAllocateHeap(hHeap, 0 /*dwFlags*/, pvOld, cbNew);
+ if (!pv)
+ supR3HardenedFatal("RtlReAllocateHeap failed to allocate %zu bytes.\n", cbNew);
+ return pv;
+}
+
+
+#undef RTMemFree
+RTDECL(void) RTMemFree(void *pv) RT_NO_THROW_DEF
+{
+ if (pv)
+ {
+ if (g_fSupEarlyProcessInit)
+ {
+ uint32_t iHeap = supR3HardenedEarlyFind(pv);
+ if (iHeap != UINT32_MAX)
+ {
+#ifdef SUPR3HARDENED_EARLY_HEAP_TRACE
+ SUP_DPRINTF(("Early heap: %p - free\n", pv));
+#endif
+ RTHeapSimpleFree(g_aSupR3HardenedEarlyHeaps[iHeap].hHeap, pv);
+ if (g_aSupR3HardenedEarlyHeaps[iHeap].cAllocations)
+ g_aSupR3HardenedEarlyHeaps[iHeap].cAllocations--;
+ if ( !g_aSupR3HardenedEarlyHeaps[iHeap].cAllocations
+ && g_enmSupR3HardenedMainState >= SUPR3HARDENEDMAINSTATE_WIN_IMPORTS_RESOLVED)
+ supR3HardenedEarlyCompact();
+ return;
+ }
+ Assert(g_enmSupR3HardenedMainState >= SUPR3HARDENEDMAINSTATE_WIN_IMPORTS_RESOLVED);
+ }
+
+ HANDLE hHeap = g_hSupR3HardenedHeap;
+ Assert(hHeap != NULL);
+ RtlFreeHeap(hHeap, 0 /* dwFlags*/, pv);
+ }
+}
+
+
+/*
+ * Simplified version of RTMemWipeThoroughly that avoids dragging in the
+ * random number code.
+ */
+
+RTDECL(void) RTMemWipeThoroughly(void *pv, size_t cb, size_t cMinPasses) RT_NO_THROW_DEF
+{
+ size_t cPasses = RT_MIN(cMinPasses, 6);
+ static const uint32_t s_aPatterns[] = { 0x00, 0xaa, 0x55, 0xff, 0xf0, 0x0f, 0xcc, 0x3c, 0xc3 };
+ uint32_t iPattern = 0;
+ do
+ {
+ memset(pv, s_aPatterns[iPattern], cb);
+ iPattern = (iPattern + 1) % RT_ELEMENTS(s_aPatterns);
+ ASMMemoryFence();
+
+ memset(pv, s_aPatterns[iPattern], cb);
+ iPattern = (iPattern + 1) % RT_ELEMENTS(s_aPatterns);
+ ASMMemoryFence();
+
+ memset(pv, s_aPatterns[iPattern], cb);
+ iPattern = (iPattern + 1) % RT_ELEMENTS(s_aPatterns);
+ ASMMemoryFence();
+ } while (cPasses-- > 0);
+
+ memset(pv, 0xff, cb);
+ ASMMemoryFence();
+}
+
+
+
+/*
+ * path-win.cpp
+ */
+
+RTDECL(int) RTPathGetCurrent(char *pszPath, size_t cbPath)
+{
+ int rc;
+ if (g_enmSupR3HardenedMainState < SUPR3HARDENEDMAINSTATE_WIN_IMPORTS_RESOLVED)
+/** @todo Rainy day: improve this by checking the process parameter block
+ * (needs to be normalized). */
+ rc = RTStrCopy(pszPath, cbPath, "C:\\");
+ else
+ {
+ /*
+ * GetCurrentDirectory may in some cases omit the drive letter, according
+ * to MSDN, thus the GetFullPathName call.
+ */
+ RTUTF16 wszCurPath[RTPATH_MAX];
+ if (GetCurrentDirectoryW(RTPATH_MAX, wszCurPath))
+ {
+ RTUTF16 wszFullPath[RTPATH_MAX];
+ if (GetFullPathNameW(wszCurPath, RTPATH_MAX, wszFullPath, NULL))
+ rc = RTUtf16ToUtf8Ex(&wszFullPath[0], RTSTR_MAX, &pszPath, cbPath, NULL);
+ else
+ rc = RTErrConvertFromWin32(RtlGetLastWin32Error());
+ }
+ else
+ rc = RTErrConvertFromWin32(RtlGetLastWin32Error());
+ }
+ return rc;
+}
+
diff --git a/src/VBox/HostDrivers/Support/win/SUPSvc-win.cpp b/src/VBox/HostDrivers/Support/win/SUPSvc-win.cpp
new file mode 100644
index 00000000..8eb05451
--- /dev/null
+++ b/src/VBox/HostDrivers/Support/win/SUPSvc-win.cpp
@@ -0,0 +1,908 @@
+/* $Id: SUPSvc-win.cpp $ */
+/** @file
+ * VirtualBox Support Service - Windows Specific Code.
+ */
+
+/*
+ * Copyright (C) 2008-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#define LOG_GROUP LOG_GROUP_SUP
+#include <iprt/win/windows.h>
+
+#include <VBox/log.h>
+#include <VBox/version.h>
+#include <iprt/string.h>
+#include <iprt/mem.h>
+#include <iprt/initterm.h>
+#include <iprt/stream.h>
+#include <iprt/getopt.h>
+#include <iprt/semaphore.h>
+#ifdef DEBUG_bird
+# include <iprt/env.h>
+#endif
+
+#include "../SUPSvcInternal.h"
+
+
+/*********************************************************************************************************************************
+* Defined Constants And Macros *
+*********************************************************************************************************************************/
+/** The service name. */
+#define SUPSVC_SERVICE_NAME "VBoxSupSvc"
+/** The service display name. */
+#define SUPSVC_SERVICE_DISPLAY_NAME "VirtualBox Support Service"
+
+
+/*********************************************************************************************************************************
+* Global Variables *
+*********************************************************************************************************************************/
+/** The service control handler handle. */
+static SERVICE_STATUS_HANDLE g_hSupSvcWinCtrlHandler = NULL;
+/** The service status. */
+static uint32_t volatile g_u32SupSvcWinStatus = SERVICE_STOPPED;
+/** The semaphore the main service thread is waiting on in supSvcWinServiceMain. */
+static RTSEMEVENTMULTI g_hSupSvcWinEvent = NIL_RTSEMEVENTMULTI;
+
+
+/*********************************************************************************************************************************
+* Internal Functions *
+*********************************************************************************************************************************/
+static SC_HANDLE supSvcWinOpenSCManager(const char *pszAction, DWORD dwAccess);
+
+
+/**
+ * Opens the service control manager.
+ *
+ * When this fails, an error message will be displayed.
+ *
+ * @returns Valid handle on success.
+ * NULL on failure, will display an error message.
+ *
+ * @param pszAction The action which is requesting access to SCM.
+ * @param dwAccess The desired access.
+ */
+static SC_HANDLE supSvcWinOpenSCManager(const char *pszAction, DWORD dwAccess)
+{
+ SC_HANDLE hSCM = OpenSCManager(NULL /* lpMachineName*/, NULL /* lpDatabaseName */, dwAccess);
+ if (hSCM == NULL)
+ {
+ DWORD err = GetLastError();
+ switch (err)
+ {
+ case ERROR_ACCESS_DENIED:
+ supSvcDisplayError("%s - OpenSCManager failure: access denied\n", pszAction);
+ break;
+ default:
+ supSvcDisplayError("%s - OpenSCManager failure: %d\n", pszAction, err);
+ break;
+ }
+ }
+ return hSCM;
+}
+
+
+/**
+ * Opens the service.
+ *
+ * Last error is preserved on failure and set to 0 on success.
+ *
+ * @returns Valid service handle on success.
+ * NULL on failure, will display an error message unless it's ignored.
+ *
+ * @param pszAction The action which is requesting access to the service.
+ * @param dwSCMAccess The service control manager access.
+ * @param dwSVCAccess The desired service access.
+ * @param cIgnoredErrors The number of ignored errors.
+ * @param ... Errors codes that should not cause a message to be displayed.
+ */
+static SC_HANDLE supSvcWinOpenService(const char *pszAction, DWORD dwSCMAccess, DWORD dwSVCAccess,
+ unsigned cIgnoredErrors, ...)
+{
+ SC_HANDLE hSCM = supSvcWinOpenSCManager(pszAction, dwSCMAccess);
+ if (!hSCM)
+ return NULL;
+
+ SC_HANDLE hSvc = OpenService(hSCM, SUPSVC_SERVICE_NAME, dwSVCAccess);
+ if (hSvc)
+ {
+ CloseServiceHandle(hSCM);
+ SetLastError(0);
+ }
+ else
+ {
+ DWORD err = GetLastError();
+ bool fIgnored = false;
+ va_list va;
+ va_start(va, cIgnoredErrors);
+ while (!fIgnored && cIgnoredErrors-- > 0)
+ fIgnored = va_arg(va, long) == err;
+ va_end(va);
+ if (!fIgnored)
+ {
+ switch (err)
+ {
+ case ERROR_ACCESS_DENIED:
+ supSvcDisplayError("%s - OpenService failure: access denied\n", pszAction);
+ break;
+ case ERROR_SERVICE_DOES_NOT_EXIST:
+ supSvcDisplayError("%s - OpenService failure: The service does not exist. Reinstall it.\n", pszAction);
+ break;
+ default:
+ supSvcDisplayError("%s - OpenService failure: %d\n", pszAction, err);
+ break;
+ }
+ }
+
+ CloseServiceHandle(hSCM);
+ SetLastError(err);
+ }
+ return hSvc;
+}
+
+
+
+void supSvcOsLogErrorStr(const char *pszMsg)
+{
+ HANDLE hEventLog = RegisterEventSource(NULL /* local computer */, "VBoxSupSvc");
+ AssertReturnVoid(hEventLog != NULL);
+ const char *apsz[2];
+ apsz[0] = "VBoxSupSvc";
+ apsz[1] = pszMsg;
+ BOOL fRc = ReportEvent(hEventLog, /* hEventLog */
+ EVENTLOG_ERROR_TYPE, /* wType */
+ 0, /* wCategory */
+ 0 /** @todo mc */, /* dwEventID */
+ NULL, /* lpUserSid */
+ RT_ELEMENTS(apsz), /* wNumStrings */
+ 0, /* dwDataSize */
+ apsz, /* lpStrings */
+ NULL); /* lpRawData */
+ AssertMsg(fRc, ("%d\n", GetLastError()));
+ DeregisterEventSource(hEventLog);
+}
+
+
+static int supSvcWinInterrogate(int argc, char **argv)
+{
+ RTPrintf("VBoxSupSvc: The \"interrogate\" action is not implemented.\n");
+ return 1;
+}
+
+
+static int supSvcWinStop(int argc, char **argv)
+{
+ RTPrintf("VBoxSupSvc: The \"stop\" action is not implemented.\n");
+ return 1;
+}
+
+
+static int supSvcWinContinue(int argc, char **argv)
+{
+ RTPrintf("VBoxSupSvc: The \"continue\" action is not implemented.\n");
+ return 1;
+}
+
+
+static int supSvcWinPause(int argc, char **argv)
+{
+ RTPrintf("VBoxSupSvc: The \"pause\" action is not implemented.\n");
+ return 1;
+}
+
+
+static int supSvcWinStart(int argc, char **argv)
+{
+ RTPrintf("VBoxSupSvc: The \"start\" action is not implemented.\n");
+ return 1;
+}
+
+
+static int supSvcWinQueryDescription(int argc, char **argv)
+{
+ RTPrintf("VBoxSupSvc: The \"qdescription\" action is not implemented.\n");
+ return 1;
+}
+
+
+static int supSvcWinQueryConfig(int argc, char **argv)
+{
+ RTPrintf("VBoxSupSvc: The \"qconfig\" action is not implemented.\n");
+ return 1;
+}
+
+
+static int supSvcWinDisable(int argc, char **argv)
+{
+ RTPrintf("VBoxSupSvc: The \"disable\" action is not implemented.\n");
+ return 1;
+}
+
+static int supSvcWinEnable(int argc, char **argv)
+{
+ RTPrintf("VBoxSupSvc: The \"enable\" action is not implemented.\n");
+ return 1;
+}
+
+
+/**
+ * Handle the 'delete' action.
+ *
+ * @returns 0 or 1.
+ * @param argc The action argument count.
+ * @param argv The action argument vector.
+ */
+static int supSvcWinDelete(int argc, char **argv)
+{
+ /*
+ * Parse the arguments.
+ */
+ bool fVerbose = false;
+ static const RTGETOPTDEF s_aOptions[] =
+ {
+ { "--verbose", 'v', RTGETOPT_REQ_NOTHING }
+ };
+ int ch;
+ RTGETOPTUNION Value;
+ RTGETOPTSTATE GetState;
+ RTGetOptInit(&GetState, argc, argv, s_aOptions, RT_ELEMENTS(s_aOptions), 0, RTGETOPTINIT_FLAGS_NO_STD_OPTS);
+ while ((ch = RTGetOpt(&GetState, &Value)))
+ switch (ch)
+ {
+ case 'v':
+ fVerbose = true;
+ break;
+ case VINF_GETOPT_NOT_OPTION:
+ return supSvcDisplayTooManyArgsError("delete", argc, argv, iArg);
+ default:
+ return supSvcDisplayGetOptError("delete", ch, argc, argv, iArg, &Value);
+ }
+
+ /*
+ * Create the service.
+ */
+ int rc = 1;
+ SC_HANDLE hSvc = supSvcWinOpenService("delete", SERVICE_CHANGE_CONFIG, DELETE,
+ 1, ERROR_SERVICE_DOES_NOT_EXIST);
+ if (hSvc)
+ {
+ if (DeleteService(hSvc))
+ {
+ RTPrintf("Successfully deleted the %s service.\n", SUPSVC_SERVICE_NAME);
+ rc = 0;
+ }
+ else
+ supSvcDisplayError("delete - DeleteService failed, err=%d.\n", GetLastError());
+ CloseServiceHandle(hSvc);
+ }
+ else if (GetLastError() == ERROR_SERVICE_DOES_NOT_EXIST)
+ {
+
+ if (fVerbose)
+ RTPrintf("The service %s was not installed, nothing to be done.", SUPSVC_SERVICE_NAME);
+ else
+ RTPrintf("Successfully deleted the %s service.\n", SUPSVC_SERVICE_NAME);
+ rc = 0;
+ }
+ return rc;
+}
+
+
+/**
+ * Handle the 'create' action.
+ *
+ * @returns 0 or 1.
+ * @param argc The action argument count.
+ * @param argv The action argument vector.
+ */
+static int supSvcWinCreate(int argc, char **argv)
+{
+ /*
+ * Parse the arguments.
+ */
+ bool fVerbose = false;
+ static const RTOPTIONDEF s_aOptions[] =
+ {
+ { "--verbose", 'v', RTGETOPT_REQ_NOTHING }
+ };
+ int iArg = 0;
+ int ch;
+ RTGETOPTUNION Value;
+ while ((ch = RTGetOpt(argc, argv, s_aOptions, RT_ELEMENTS(s_aOptions), &iArg, &Value)))
+ switch (ch)
+ {
+ case 'v': fVerbose = true; break;
+ default: return supSvcDisplayGetOptError("create", ch, argc, argv, iArg, &Value);
+ }
+ if (iArg != argc)
+ return supSvcDisplayTooManyArgsError("create", argc, argv, iArg);
+
+ /*
+ * Create the service.
+ */
+ int rc = 1;
+ SC_HANDLE hSCM = supSvcWinOpenSCManager("create", SC_MANAGER_CREATE_SERVICE); /*SC_MANAGER_ALL_ACCESS*/
+ if (hSCM)
+ {
+ char szExecPath[MAX_PATH];
+ if (GetModuleFileName(NULL /* the executable */, szExecPath, sizeof(szExecPath)))
+ {
+ if (fVerbose)
+ RTPrintf("Creating the %s service, binary \"%s\"...\n",
+ SUPSVC_SERVICE_NAME, szExecPath); /* yea, the binary name isn't UTF-8, but wtf. */
+
+ SC_HANDLE hSvc = CreateService(hSCM, /* hSCManager */
+ SUPSVC_SERVICE_NAME, /* lpServiceName */
+ SUPSVC_SERVICE_DISPLAY_NAME, /* lpDisplayName */
+ SERVICE_CHANGE_CONFIG | SERVICE_QUERY_STATUS | SERVICE_QUERY_CONFIG, /* dwDesiredAccess */
+ SERVICE_WIN32_OWN_PROCESS, /* dwServiceType ( | SERVICE_INTERACTIVE_PROCESS? ) */
+ SERVICE_DEMAND_START/*_AUTO*/, /* dwStartType */
+ SERVICE_ERROR_NORMAL, /* dwErrorControl */
+ szExecPath, /* lpBinaryPathName */
+ NULL, /* lpLoadOrderGroup */
+ NULL, /* lpdwTagId */
+ NULL, /* lpDependencies */
+ NULL, /* lpServiceStartName (=> LocalSystem) */
+ NULL); /* lpPassword */
+ if (hSvc)
+ {
+ RTPrintf("Successfully created the %s service.\n", SUPSVC_SERVICE_NAME);
+ /** @todo Set the service description or it'll look weird in the vista service manager.
+ * Anything else that should be configured? Start access or something? */
+ rc = 0;
+ CloseServiceHandle(hSvc);
+ }
+ else
+ {
+ DWORD err = GetLastError();
+ switch (err)
+ {
+ case ERROR_SERVICE_EXISTS:
+ supSvcDisplayError("create - The service already exists.\n");
+ break;
+ default:
+ supSvcDisplayError("create - CreateService failed, err=%d.\n", GetLastError());
+ break;
+ }
+ }
+ CloseServiceHandle(hSvc);
+ }
+ else
+ supSvcDisplayError("create - Failed to obtain the executable path: %d\n", GetLastError());
+ }
+ return rc;
+}
+
+
+/**
+ * Sets the service status, just a SetServiceStatus Wrapper.
+ *
+ * @returns See SetServiceStatus.
+ * @param dwStatus The current status.
+ * @param iWaitHint The wait hint, if < 0 then supply a default.
+ * @param dwExitCode The service exit code.
+ */
+static bool supSvcWinSetServiceStatus(DWORD dwStatus, int iWaitHint, DWORD dwExitCode)
+{
+ SERVICE_STATUS SvcStatus;
+ SvcStatus.dwServiceType = SERVICE_WIN32_OWN_PROCESS;
+ SvcStatus.dwWin32ExitCode = dwExitCode;
+ SvcStatus.dwServiceSpecificExitCode = 0;
+ SvcStatus.dwWaitHint = iWaitHint >= 0 ? iWaitHint : 3000;
+ SvcStatus.dwCurrentState = dwStatus;
+ LogFlow(("supSvcWinSetServiceStatus: %d -> %d\n", g_u32SupSvcWinStatus, dwStatus));
+ g_u32SupSvcWinStatus = dwStatus;
+ switch (dwStatus)
+ {
+ case SERVICE_START_PENDING:
+ SvcStatus.dwControlsAccepted = 0;
+ break;
+ default:
+ SvcStatus.dwControlsAccepted = SERVICE_ACCEPT_STOP;
+ break;
+ }
+
+ static DWORD dwCheckPoint = 0;
+ switch (dwStatus)
+ {
+ case SERVICE_RUNNING:
+ case SERVICE_STOPPED:
+ SvcStatus.dwCheckPoint = 0;
+ default:
+ SvcStatus.dwCheckPoint = ++dwCheckPoint;
+ break;
+ }
+ return SetServiceStatus(g_hSupSvcWinCtrlHandler, &SvcStatus) != FALSE;
+}
+
+
+/**
+ * Service control handler (extended).
+ *
+ * @returns Windows status (see HandlerEx).
+ * @retval NO_ERROR if handled.
+ * @retval ERROR_CALL_NOT_IMPLEMENTED if not handled.
+ *
+ * @param dwControl The control code.
+ * @param dwEventType Event type. (specific to the control?)
+ * @param pvEventData Event data, specific to the event.
+ * @param pvContext The context pointer registered with the handler.
+ * Currently not used.
+ */
+static DWORD WINAPI supSvcWinServiceCtrlHandlerEx(DWORD dwControl, DWORD dwEventType, LPVOID pvEventData, LPVOID pvContext)
+{
+ LogFlow(("supSvcWinServiceCtrlHandlerEx: dwControl=%#x dwEventType=%#x pvEventData=%p\n",
+ dwControl, dwEventType, pvEventData));
+
+ switch (dwControl)
+ {
+ /*
+ * Interrogate the service about it's current status.
+ * MSDN says that this should just return NO_ERROR and does
+ * not need to set the status again.
+ */
+ case SERVICE_CONTROL_INTERROGATE:
+ return NO_ERROR;
+
+ /*
+ * Request to stop the service.
+ */
+ case SERVICE_CONTROL_STOP:
+ {
+ /*
+ * Check if the real services can be stopped and then tell them to stop.
+ */
+ supSvcWinSetServiceStatus(SERVICE_STOP_PENDING, 3000, NO_ERROR);
+ int rc = supSvcTryStopServices();
+ if (RT_SUCCESS(rc))
+ {
+ /*
+ * Notify the main thread that we're done, it will wait for the
+ * real services to stop, destroy them, and finally set the windows
+ * service status to SERVICE_STOPPED and return.
+ */
+ rc = RTSemEventMultiSignal(g_hSupSvcWinEvent);
+ if (RT_FAILURE(rc))
+ supSvcLogError("SERVICE_CONTROL_STOP: RTSemEventMultiSignal failed, %Rrc\n", rc);
+ }
+ return NO_ERROR;
+ }
+
+ case SERVICE_CONTROL_PAUSE:
+ case SERVICE_CONTROL_CONTINUE:
+ case SERVICE_CONTROL_SHUTDOWN:
+ case SERVICE_CONTROL_PARAMCHANGE:
+ case SERVICE_CONTROL_NETBINDADD:
+ case SERVICE_CONTROL_NETBINDREMOVE:
+ case SERVICE_CONTROL_NETBINDENABLE:
+ case SERVICE_CONTROL_NETBINDDISABLE:
+ case SERVICE_CONTROL_DEVICEEVENT:
+ case SERVICE_CONTROL_HARDWAREPROFILECHANGE:
+ case SERVICE_CONTROL_POWEREVENT:
+ case SERVICE_CONTROL_SESSIONCHANGE:
+#ifdef SERVICE_CONTROL_PRESHUTDOWN /* vista */
+ case SERVICE_CONTROL_PRESHUTDOWN:
+#endif
+ default:
+ return ERROR_CALL_NOT_IMPLEMENTED;
+ }
+
+ NOREF(dwEventType);
+ NOREF(pvEventData);
+ NOREF(pvContext);
+ return NO_ERROR;
+}
+
+
+/**
+ * Windows Service Main.
+ *
+ * This is invoked when the service is started and should not return until
+ * the service has been stopped.
+ *
+ * @param cArgs Argument count.
+ * @param papszArgs Argument vector.
+ */
+static VOID WINAPI supSvcWinServiceMain(DWORD cArgs, LPSTR *papszArgs)
+{
+ LogFlowFuncEnter();
+
+ /*
+ * Register the control handler function for the service and report to SCM.
+ */
+ Assert(g_u32SupSvcWinStatus == SERVICE_STOPPED);
+ g_hSupSvcWinCtrlHandler = RegisterServiceCtrlHandlerEx(SUPSVC_SERVICE_NAME, supSvcWinServiceCtrlHandlerEx, NULL);
+ if (g_hSupSvcWinCtrlHandler)
+ {
+ DWORD err = ERROR_GEN_FAILURE;
+ if (supSvcWinSetServiceStatus(SERVICE_START_PENDING, 3000, NO_ERROR))
+ {
+ /*
+ * Parse arguments.
+ */
+ static const RTOPTIONDEF s_aOptions[] =
+ {
+ { "--dummy", 'd', RTGETOPT_REQ_NOTHING }
+ };
+ int iArg = 1; /* the first arg is the service name */
+ int ch;
+ int rc = 0;
+ RTGETOPTUNION Value;
+ while ( !rc
+ && (ch = RTGetOpt(cArgs, papszArgs, s_aOptions, RT_ELEMENTS(s_aOptions), &iArg, &Value)))
+ switch (ch)
+ {
+ default: rc = supSvcLogGetOptError("main", ch, cArgs, papszArgs, iArg, &Value); break;
+ }
+ if (iArg != cArgs)
+ rc = supSvcLogTooManyArgsError("main", cArgs, papszArgs, iArg);
+ if (!rc)
+ {
+ /*
+ * Create the event semaphore we'll be waiting on and
+ * then instantiate the actual services.
+ */
+ int rc = RTSemEventMultiCreate(&g_hSupSvcWinEvent);
+ if (RT_SUCCESS(rc))
+ {
+ rc = supSvcCreateAndStartServices();
+ if (RT_SUCCESS(rc))
+ {
+ /*
+ * Update the status and enter the work loop.
+ *
+ * The work loop is just a dummy wait here as the services run
+ * in independent threads.
+ */
+ if (supSvcWinSetServiceStatus(SERVICE_RUNNING, 0, 0))
+ {
+ LogFlow(("supSvcWinServiceMain: calling RTSemEventMultiWait\n"));
+ rc = RTSemEventMultiWait(g_hSupSvcWinEvent, RT_INDEFINITE_WAIT);
+ if (RT_SUCCESS(rc))
+ {
+ LogFlow(("supSvcWinServiceMain: woke up\n"));
+ err = NO_ERROR;
+ }
+ else
+ supSvcLogError("RTSemEventWait failed, rc=%Rrc", rc);
+ }
+ else
+ {
+ err = GetLastError();
+ supSvcLogError("SetServiceStatus failed, err=%d", err);
+ }
+
+ /*
+ * Destroy the service instances, stopping them if
+ * they're still running (weird failure cause).
+ */
+ supSvcStopAndDestroyServices();
+ }
+
+ RTSemEventMultiDestroy(g_hSupSvcWinEvent);
+ g_hSupSvcWinEvent = NIL_RTSEMEVENTMULTI;
+ }
+ else
+ supSvcLogError("RTSemEventMultiCreate failed, rc=%Rrc", rc);
+ }
+ /* else: bad args */
+ }
+ else
+ {
+ err = GetLastError();
+ supSvcLogError("SetServiceStatus failed, err=%d", err);
+ }
+ supSvcWinSetServiceStatus(SERVICE_STOPPED, 0, err);
+ }
+ else
+ supSvcLogError("RegisterServiceCtrlHandlerEx failed, err=%d", GetLastError());
+ LogFlowFuncLeave();
+}
+
+
+/**
+ * Handle the 'create' action.
+ *
+ * @returns 0 or 1.
+ * @param argc The action argument count.
+ * @param argv The action argument vector.
+ */
+static int supSvcWinRunIt(int argc, char **argv)
+{
+ LogFlowFuncEnter();
+
+ /*
+ * Initialize release logging.
+ */
+ /** @todo release logging of the system-wide service. */
+
+ /*
+ * Parse the arguments.
+ */
+ static const RTOPTIONDEF s_aOptions[] =
+ {
+ { "--dummy", 'd', RTGETOPT_REQ_NOTHING }
+ };
+ int iArg = 0;
+ int ch;
+ RTGETOPTUNION Value;
+ while ((ch = RTGetOpt(argc, argv, s_aOptions, RT_ELEMENTS(s_aOptions), &iArg, &Value)))
+ switch (ch)
+ {
+ default: return supSvcDisplayGetOptError("runit", ch, argc, argv, iArg, &Value);
+ }
+ if (iArg != argc)
+ return supSvcDisplayTooManyArgsError("runit", argc, argv, iArg);
+
+ /*
+ * Register the service with the service control manager
+ * and start dispatching requests from it (all done by the API).
+ */
+ static SERVICE_TABLE_ENTRY const s_aServiceStartTable[] =
+ {
+ { SUPSVC_SERVICE_NAME, supSvcWinServiceMain },
+ { NULL, NULL}
+ };
+ if (StartServiceCtrlDispatcher(&s_aServiceStartTable[0]))
+ {
+ LogFlowFuncLeave();
+ return 0; /* told to quit, so quit. */
+ }
+
+ DWORD err = GetLastError();
+ switch (err)
+ {
+ case ERROR_FAILED_SERVICE_CONTROLLER_CONNECT:
+ supSvcDisplayError("Cannot run a service from the command line. Use the 'start' action to start it the right way.\n");
+ break;
+ default:
+ supSvcLogError("StartServiceCtrlDispatcher failed, err=%d", err);
+ break;
+ }
+ return 1;
+}
+
+
+/**
+ * Show the version info.
+ *
+ * @returns 0.
+ */
+static int supSvcWinShowVersion(int argc, char **argv)
+{
+ /*
+ * Parse the arguments.
+ */
+ bool fBrief = false;
+ static const RTOPTIONDEF s_aOptions[] =
+ {
+ { "--brief", 'b', RTGETOPT_REQ_NOTHING }
+ };
+ int iArg = 0;
+ int ch;
+ RTGETOPTUNION Value;
+ while ((ch = RTGetOpt(argc, argv, s_aOptions, RT_ELEMENTS(s_aOptions), &iArg, &Value)))
+ switch (ch)
+ {
+ case 'b': fBrief = true; break;
+ default: return supSvcDisplayGetOptError("version", ch, argc, argv, iArg, &Value);
+
+ }
+ if (iArg != argc)
+ return supSvcDisplayTooManyArgsError("version", argc, argv, iArg);
+
+ /*
+ * Do the printing.
+ */
+ if (fBrief)
+ RTPrintf("%s\n", VBOX_VERSION_STRING);
+ else
+ RTPrintf("VirtualBox System Service Version %s\n"
+ "Copyright (C) 2008-" VBOX_C_YEAR " Oracle and/or its affiliates\n\n",
+ VBOX_VERSION_STRING);
+ return 0;
+}
+
+
+/**
+ * Show the usage help screen.
+ *
+ * @returns 0.
+ */
+static int supSvcWinShowHelp(void)
+{
+ RTPrintf("VirtualBox System Service Version %s\n"
+ "Copyright (C) 2008-" VBOX_C_YEAR " Oracle and/or its affiliates\n\n",
+ VBOX_VERSION_STRING);
+ RTPrintf("Usage:\n"
+ "\n"
+ "VBoxSupSvc\n"
+ " Runs the service.\n"
+ "VBoxSupSvc <version|-v|--version> [-brief]\n"
+ " Displays the version.\n"
+ "VBoxSupSvc <help|-?|-h|--help> [...]\n"
+ " Displays this help screen.\n"
+ "\n"
+ "VBoxSupSvc <install|/RegServer|/i>\n"
+ " Installs the service.\n"
+ "VBoxSupSvc <install|delete|/UnregServer|/u>\n"
+ " Uninstalls the service.\n"
+ );
+ return 0;
+}
+
+
+/**
+ * VBoxSUPSvc main(), Windows edition.
+ *
+ *
+ * @returns 0 on success.
+ *
+ * @param argc Number of arguments in argv.
+ * @param argv Argument vector.
+ */
+int main(int argc, char **argv)
+{
+ /*
+ * Initialize the IPRT first of all.
+ */
+#ifdef DEBUG_bird
+ RTEnvSet("VBOX_LOG", "sup=~0");
+ RTEnvSet("VBOX_LOG_DEST", "file=E:\\temp\\VBoxSupSvc.log");
+ RTEnvSet("VBOX_LOG_FLAGS", "unbuffered thread msprog");
+#endif
+ int rc = RTR3InitExe(argc, &argv, 0);
+ if (RT_FAILURE(rc))
+ {
+ supSvcLogError("RTR3InitExe failed with rc=%Rrc", rc);
+ return 1;
+ }
+
+ /*
+ * Parse the initial arguments to determine the desired action.
+ */
+ enum
+ {
+ kSupSvcAction_RunIt,
+
+ kSupSvcAction_Create,
+ kSupSvcAction_Delete,
+
+ kSupSvcAction_Enable,
+ kSupSvcAction_Disable,
+ kSupSvcAction_QueryConfig,
+ kSupSvcAction_QueryDescription,
+
+ kSupSvcAction_Start,
+ kSupSvcAction_Pause,
+ kSupSvcAction_Continue,
+ kSupSvcAction_Stop,
+ kSupSvcAction_Interrogate,
+
+ kSupSvcAction_End
+ } enmAction = kSupSvcAction_RunIt;
+ int iArg = 1;
+ if (argc > 1)
+ {
+ if ( !stricmp(argv[iArg], "/RegServer")
+ || !stricmp(argv[iArg], "install")
+ || !stricmp(argv[iArg], "/i"))
+ enmAction = kSupSvcAction_Create;
+ else if ( !stricmp(argv[iArg], "/UnregServer")
+ || !stricmp(argv[iArg], "/u")
+ || !stricmp(argv[iArg], "uninstall")
+ || !stricmp(argv[iArg], "delete"))
+ enmAction = kSupSvcAction_Delete;
+
+ else if (!stricmp(argv[iArg], "enable"))
+ enmAction = kSupSvcAction_Enable;
+ else if (!stricmp(argv[iArg], "disable"))
+ enmAction = kSupSvcAction_Disable;
+ else if (!stricmp(argv[iArg], "qconfig"))
+ enmAction = kSupSvcAction_QueryConfig;
+ else if (!stricmp(argv[iArg], "qdescription"))
+ enmAction = kSupSvcAction_QueryDescription;
+
+ else if ( !stricmp(argv[iArg], "start")
+ || !stricmp(argv[iArg], "/t"))
+ enmAction = kSupSvcAction_Start;
+ else if (!stricmp(argv[iArg], "pause"))
+ enmAction = kSupSvcAction_Start;
+ else if (!stricmp(argv[iArg], "continue"))
+ enmAction = kSupSvcAction_Continue;
+ else if (!stricmp(argv[iArg], "stop"))
+ enmAction = kSupSvcAction_Stop;
+ else if (!stricmp(argv[iArg], "interrogate"))
+ enmAction = kSupSvcAction_Interrogate;
+ else if ( !stricmp(argv[iArg], "help")
+ || !stricmp(argv[iArg], "?")
+ || !stricmp(argv[iArg], "/?")
+ || !stricmp(argv[iArg], "-?")
+ || !stricmp(argv[iArg], "/h")
+ || !stricmp(argv[iArg], "-h")
+ || !stricmp(argv[iArg], "/help")
+ || !stricmp(argv[iArg], "-help")
+ || !stricmp(argv[iArg], "--help"))
+ return supSvcWinShowHelp();
+ else if ( !stricmp(argv[iArg], "version")
+ || !stricmp(argv[iArg], "/v")
+ || !stricmp(argv[iArg], "-v")
+ || !stricmp(argv[iArg], "/version")
+ || !stricmp(argv[iArg], "-version")
+ || !stricmp(argv[iArg], "--version"))
+ return supSvcWinShowVersion(argc - iArg - 1, argv + iArg + 1);
+ else
+ iArg--;
+ iArg++;
+ }
+
+ /*
+ * Dispatch it.
+ */
+ switch (enmAction)
+ {
+ case kSupSvcAction_RunIt:
+ return supSvcWinRunIt(argc - iArg, argv + iArg);
+
+ case kSupSvcAction_Create:
+ return supSvcWinCreate(argc - iArg, argv + iArg);
+ case kSupSvcAction_Delete:
+ return supSvcWinDelete(argc - iArg, argv + iArg);
+
+ case kSupSvcAction_Enable:
+ return supSvcWinEnable(argc - iArg, argv + iArg);
+ case kSupSvcAction_Disable:
+ return supSvcWinDisable(argc - iArg, argv + iArg);
+ case kSupSvcAction_QueryConfig:
+ return supSvcWinQueryConfig(argc - iArg, argv + iArg);
+ case kSupSvcAction_QueryDescription:
+ return supSvcWinQueryDescription(argc - iArg, argv + iArg);
+
+ case kSupSvcAction_Start:
+ return supSvcWinStart(argc - iArg, argv + iArg);
+ case kSupSvcAction_Pause:
+ return supSvcWinPause(argc - iArg, argv + iArg);
+ case kSupSvcAction_Continue:
+ return supSvcWinContinue(argc - iArg, argv + iArg);
+ case kSupSvcAction_Stop:
+ return supSvcWinStop(argc - iArg, argv + iArg);
+ case kSupSvcAction_Interrogate:
+ return supSvcWinInterrogate(argc - iArg, argv + iArg);
+
+ default:
+ AssertMsgFailed(("enmAction=%d\n", enmAction));
+ return 1;
+ }
+}
+
diff --git a/src/VBox/HostDrivers/Support/win/VBoxDrv.rc b/src/VBox/HostDrivers/Support/win/VBoxDrv.rc
new file mode 100644
index 00000000..5600f4db
--- /dev/null
+++ b/src/VBox/HostDrivers/Support/win/VBoxDrv.rc
@@ -0,0 +1,71 @@
+/* $Id: VBoxDrv.rc $ */
+/** @file
+ * VBoxDrv - Resource file containing version info and icon.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+#include <windows.h>
+#include <VBox/version.h>
+
+LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
+
+VS_VERSION_INFO VERSIONINFO
+ FILEVERSION VBOX_RC_FILE_VERSION
+ PRODUCTVERSION VBOX_RC_FILE_VERSION
+ FILEFLAGSMASK VS_FFI_FILEFLAGSMASK
+ FILEFLAGS VBOX_RC_FILE_FLAGS
+ FILEOS VBOX_RC_FILE_OS
+ FILETYPE VBOX_RC_TYPE_DRV
+ FILESUBTYPE VFT2_DRV_SYSTEM
+BEGIN
+ BLOCK "StringFileInfo"
+ BEGIN
+ BLOCK "040904b0" // Lang=US English, CharSet=Unicode
+ BEGIN
+ VALUE "FileDescription", "VirtualBox Support Driver\0"
+ VALUE "InternalName", "VBoxSup\0"
+ VALUE "OriginalFilename", "VBoxSup.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_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/HostDrivers/Support/win/VBoxSup.inf b/src/VBox/HostDrivers/Support/win/VBoxSup.inf
new file mode 100644
index 00000000..2309fb06
--- /dev/null
+++ b/src/VBox/HostDrivers/Support/win/VBoxSup.inf
@@ -0,0 +1,100 @@
+; $Id: VBoxSup.inf $
+;; @file
+; VirtualBox Support Driver - Windows Driver INF file.
+;
+
+;
+; Copyright (C) 2006-2023 Oracle and/or its affiliates.
+;
+; This file is part of VirtualBox base platform packages, as
+; available from https://www.virtualbox.org.
+;
+; This program is free software; you can redistribute it and/or
+; modify it under the terms of the GNU General Public License
+; as published by the Free Software Foundation, in version 3 of the
+; License.
+;
+; This program is distributed in the hope that it will be useful, but
+; WITHOUT ANY WARRANTY; without even the implied warranty of
+; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+; General Public License for more details.
+;
+; You should have received a copy of the GNU General Public License
+; along with this program; if not, see <https://www.gnu.org/licenses>.
+;
+; The contents of this file may alternatively be used under the terms
+; of the Common Development and Distribution License Version 1.0
+; (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+; in the VirtualBox distribution, in which case the provisions of the
+; CDDL are applicable instead of those of the GPL.
+;
+; You may elect to license modified versions of this file under the
+; terms and conditions of either the GPL or the CDDL or both.
+;
+; SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+;
+
+[Version]
+Signature="$Windows NT$"
+Class=System
+ClassGuid={4d36e97d-e325-11ce-bfc1-08002be10318}
+Provider=%ORACLE%
+;edit-DriverVer=08/26/2008,2.00.0000
+DriverPackageType=KernelService
+;cat CatalogFile=VBoxSup.cat
+
+[DestinationDirs]
+DefaultDestDir = 12
+
+[DefaultInstall@DOT-NT-ARCH@]
+CopyFiles=VBoxSup_CopyFiles
+
+[DefaultInstall@DOT-NT-ARCH@.Services]
+AddService=VBoxSup,0x00000002,VBoxSup_Service
+
+[DefaultUninstall@DOT-NT-ARCH@]
+DefFiles=VBoxSup_CopyFiles
+LegacyUninstall=1
+
+[DefaultUninstall@DOT-NT-ARCH@.Services]
+DelService=VBoxSup,0x00000200
+LegacyUninstall=1
+
+;; This does not actually work either (see VBoxUSBMon), though it triggers in the
+;; installer rather at manual installation on my test system (could be polluted & confused).
+;; This may apparently also causes unloading trouble due to 'root\VBoxSup' or something related to that.
+;; [Manufacturer]
+;; %ORACLE%=VBoxSup@COMMA-NT-ARCH@
+;;
+;; ; Models section (referenced by [Manufacturer]).
+;; [VBoxSup@DOT-NT-ARCH@]
+;; %VBoxSup.DRVDESC%=VBoxSup_Install,root\VBoxSup
+;;
+;; [VBoxSup_Install@DOT-NT-ARCH@]
+;; CopyFiles=VBoxSup_CopyFiles
+;;
+;; [VBoxSup_Install@DOT-NT-ARCH@.Services]
+;; AddService=VBoxSup,0x00000002,VBoxSup_Service
+
+[SourceDisksFiles]
+VBoxSup.sys=1
+
+[SourceDisksNames]
+1=%VBoxSup.DSKDESC%,
+
+[VBoxSup_CopyFiles]
+VBoxSup.sys
+
+[VBoxSup_Service]
+DisplayName = %VBoxSup.SVCDESC%
+ServiceType = 1 ; SERVICE_KERNEL_DRIVER
+;StartType = 3 ; SERVICE_DEMAND_START
+StartType = 1 ; autostart to fix Vista problem
+ErrorControl = 1 ; SERVICE_ERROR_NORMAL
+ServiceBinary = %12%\VBoxSup.sys
+
+[Strings]
+ORACLE = "Oracle Corporation"
+VBoxSup.SVCDESC = "VirtualBox Service"
+VBoxSup.DRVDESC = "VirtualBox Support Driver"
+VBoxSup.DSKDESC = "VirtualBox Support Driver Installation Disk"
diff --git a/src/VBox/HostDrivers/Support/win/VBoxSupLib-win.cpp b/src/VBox/HostDrivers/Support/win/VBoxSupLib-win.cpp
new file mode 100644
index 00000000..f50cf9c5
--- /dev/null
+++ b/src/VBox/HostDrivers/Support/win/VBoxSupLib-win.cpp
@@ -0,0 +1,108 @@
+/* $Id: VBoxSupLib-win.cpp $ */
+/** @file
+ * IPRT - VBoxSupLib.dll, Windows.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include <iprt/nt/nt-and-windows.h>
+
+#include <iprt/path.h>
+
+
+/**
+ * The Dll main entry point.
+ * @remarks The dllexport is for forcing the linker to generate an import
+ * library, so the build system doesn't get confused.
+ */
+extern "C" __declspec(dllexport)
+BOOL __stdcall DllMainEntrypoint(HANDLE hModule, DWORD dwReason, PVOID pvReserved)
+{
+ RT_NOREF1(pvReserved);
+
+ switch (dwReason)
+ {
+ /*
+ * Make sure the DLL isn't ever unloaded.
+ */
+ case DLL_PROCESS_ATTACH:
+ {
+ WCHAR wszName[RTPATH_MAX];
+ SetLastError(NO_ERROR);
+ if ( GetModuleFileNameW((HMODULE)hModule, wszName, RT_ELEMENTS(wszName)) > 0
+ && RtlGetLastWin32Error() == NO_ERROR)
+ {
+ int cExtraLoads = 2;
+ while (cExtraLoads-- > 0)
+ LoadLibraryW(wszName);
+ }
+ break;
+ }
+
+ case DLL_THREAD_ATTACH:
+ {
+#ifdef VBOX_WITH_HARDENING
+# ifndef VBOX_WITHOUT_DEBUGGER_CHECKS
+ /*
+ * Anti debugging hack that prevents most debug notifications from
+ * ending up in the debugger.
+ */
+ NTSTATUS rcNt = NtSetInformationThread(GetCurrentThread(), ThreadHideFromDebugger, NULL, 0);
+ if (!NT_SUCCESS(rcNt))
+ {
+ __debugbreak();
+ return FALSE;
+ }
+# endif
+#endif
+ break;
+ }
+
+ case DLL_THREAD_DETACH:
+ /* Nothing to do. */
+ break;
+
+ case DLL_PROCESS_DETACH:
+ /* Nothing to do. */
+ break;
+
+ default:
+ /* ignore */
+ break;
+ }
+ return TRUE;
+}
+
diff --git a/src/VBox/HostDrivers/Support/win/VBoxSupLib.rc b/src/VBox/HostDrivers/Support/win/VBoxSupLib.rc
new file mode 100644
index 00000000..6e3d3525
--- /dev/null
+++ b/src/VBox/HostDrivers/Support/win/VBoxSupLib.rc
@@ -0,0 +1,70 @@
+/* $Id: VBoxSupLib.rc $ */
+/** @file
+ * VBoxSupLib - Resource file containing version info and icon.
+ */
+
+/*
+ * Copyright (C) 2015-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+#include <windows.h>
+#include <VBox/version.h>
+
+LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
+
+VS_VERSION_INFO VERSIONINFO
+ FILEVERSION VBOX_RC_FILE_VERSION
+ PRODUCTVERSION VBOX_RC_FILE_VERSION
+ FILEFLAGSMASK VS_FFI_FILEFLAGSMASK
+ FILEFLAGS VBOX_RC_FILE_FLAGS
+ FILEOS VBOX_RC_FILE_OS
+ FILETYPE VBOX_RC_TYPE_DLL
+ FILESUBTYPE VFT2_UNKNOWN
+BEGIN
+ BLOCK "StringFileInfo"
+ BEGIN
+ BLOCK "040904b0" // Lang=US English, CharSet=Unicode
+ BEGIN
+ VALUE "FileDescription", "VirtualBox Kernel Support\0"
+ VALUE "InternalName", "VBoxSupLib\0"
+ VALUE "OriginalFilename", "VBoxSupLib.dll\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_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/HostDrivers/Support/win/import-template-kernel32.h b/src/VBox/HostDrivers/Support/win/import-template-kernel32.h
new file mode 100644
index 00000000..f761d734
--- /dev/null
+++ b/src/VBox/HostDrivers/Support/win/import-template-kernel32.h
@@ -0,0 +1,21 @@
+SUPHARNT_IMPORT_STDCALL(CreateFileW, 28)
+SUPHARNT_IMPORT_STDCALL(CreateProcessW, 40)
+SUPHARNT_IMPORT_STDCALL(ExitProcess, 4)
+SUPHARNT_IMPORT_STDCALL(GetFullPathNameA, 16)
+SUPHARNT_IMPORT_STDCALL(GetFullPathNameW, 16)
+SUPHARNT_IMPORT_STDCALL(GetCurrentDirectoryW, 8)
+SUPHARNT_IMPORT_STDCALL(GetLongPathNameW, 12)
+SUPHARNT_IMPORT_STDCALL(GetModuleFileNameW, 12)
+SUPHARNT_IMPORT_STDCALL(GetModuleHandleA, 4)
+SUPHARNT_IMPORT_STDCALL(GetModuleHandleW, 4)
+SUPHARNT_IMPORT_STDCALL(GetProcAddress, 8)
+SUPHARNT_IMPORT_STDCALL(GetProcessHeap, 0)
+SUPHARNT_IMPORT_STDCALL(GetSystemDirectoryW, 8)
+SUPHARNT_IMPORT_STDCALL(GetTickCount, 0)
+SUPHARNT_IMPORT_STDCALL(LoadLibraryExW, 12)
+SUPHARNT_IMPORT_STDCALL(OutputDebugStringA, 4)
+SUPHARNT_IMPORT_STDCALL(TlsAlloc, 0)
+SUPHARNT_IMPORT_STDCALL(TlsGetValue, 4)
+SUPHARNT_IMPORT_STDCALL(TlsSetValue, 8)
+SUPHARNT_IMPORT_STDCALL(WriteFile, 20)
+
diff --git a/src/VBox/HostDrivers/Support/win/import-template-ntdll.h b/src/VBox/HostDrivers/Support/win/import-template-ntdll.h
new file mode 100644
index 00000000..fcd7a24b
--- /dev/null
+++ b/src/VBox/HostDrivers/Support/win/import-template-ntdll.h
@@ -0,0 +1,99 @@
+SUPHARNT_IMPORT_SYSCALL(NtAllocateVirtualMemory, 24)
+SUPHARNT_IMPORT_SYSCALL(NtClearEvent, 4)
+SUPHARNT_IMPORT_SYSCALL(NtClose, 4)
+SUPHARNT_IMPORT_SYSCALL(NtCreateEvent, 20)
+SUPHARNT_IMPORT_SYSCALL(NtCreateFile, 44)
+SUPHARNT_IMPORT_SYSCALL(NtCreateSymbolicLinkObject, 16)
+SUPHARNT_IMPORT_SYSCALL(NtDelayExecution, 8)
+SUPHARNT_IMPORT_SYSCALL(NtDeviceIoControlFile, 40)
+SUPHARNT_IMPORT_SYSCALL(NtDuplicateObject, 28)
+SUPHARNT_IMPORT_SYSCALL(NtFlushBuffersFile, 8)
+SUPHARNT_IMPORT_SYSCALL(NtFreeVirtualMemory, 16)
+SUPHARNT_IMPORT_SYSCALL(NtGetContextThread, 8)
+SUPHARNT_IMPORT_SYSCALL(NtMapViewOfSection, 40)
+SUPHARNT_IMPORT_SYSCALL(NtOpenDirectoryObject, 12)
+SUPHARNT_IMPORT_SYSCALL(NtOpenEvent, 12)
+SUPHARNT_IMPORT_SYSCALL(NtOpenKey, 12)
+SUPHARNT_IMPORT_SYSCALL(NtOpenProcess, 16)
+SUPHARNT_IMPORT_SYSCALL(NtOpenProcessToken, 12)
+SUPHARNT_IMPORT_SYSCALL(NtOpenSymbolicLinkObject, 12)
+SUPHARNT_IMPORT_SYSCALL(NtOpenThread, 16)
+SUPHARNT_IMPORT_SYSCALL(NtOpenThreadToken, 16)
+SUPHARNT_IMPORT_SYSCALL(NtProtectVirtualMemory, 20)
+SUPHARNT_IMPORT_SYSCALL(NtQueryDirectoryFile, 44)
+SUPHARNT_IMPORT_SYSCALL(NtQueryDirectoryObject, 28)
+SUPHARNT_IMPORT_SYSCALL(NtQueryEvent, 20)
+SUPHARNT_IMPORT_SYSCALL(NtQueryInformationFile, 20)
+SUPHARNT_IMPORT_SYSCALL(NtQueryInformationProcess, 20)
+SUPHARNT_IMPORT_SYSCALL(NtQueryInformationThread, 20)
+SUPHARNT_IMPORT_SYSCALL(NtQueryInformationToken, 20)
+SUPHARNT_IMPORT_SYSCALL(NtQueryObject, 20)
+SUPHARNT_IMPORT_SYSCALL(NtQuerySecurityObject, 20)
+SUPHARNT_IMPORT_SYSCALL(NtQuerySymbolicLinkObject, 12)
+SUPHARNT_IMPORT_SYSCALL(NtQuerySystemInformation, 16)
+SUPHARNT_IMPORT_SYSCALL(NtQueryTimerResolution, 12)
+SUPHARNT_IMPORT_SYSCALL(NtQueryValueKey, 24)
+SUPHARNT_IMPORT_SYSCALL(NtQueryVirtualMemory, 24)
+SUPHARNT_IMPORT_SYSCALL(NtReadFile, 36)
+SUPHARNT_IMPORT_SYSCALL(NtReadVirtualMemory, 20)
+SUPHARNT_IMPORT_SYSCALL(NtResetEvent, 8)
+SUPHARNT_IMPORT_SYSCALL(NtResumeProcess, 4)
+SUPHARNT_IMPORT_SYSCALL(NtResumeThread, 8)
+SUPHARNT_IMPORT_SYSCALL(NtSetContextThread, 8)
+SUPHARNT_IMPORT_SYSCALL(NtSetEvent, 8)
+SUPHARNT_IMPORT_SYSCALL(NtSetInformationFile, 20)
+SUPHARNT_IMPORT_SYSCALL(NtSetInformationObject, 16)
+SUPHARNT_IMPORT_SYSCALL(NtSetInformationProcess, 16)
+SUPHARNT_IMPORT_SYSCALL(NtSetInformationThread, 16)
+SUPHARNT_IMPORT_SYSCALL(NtSetTimerResolution, 12)
+SUPHARNT_IMPORT_SYSCALL(NtSuspendProcess, 4)
+SUPHARNT_IMPORT_SYSCALL(NtSuspendThread, 8)
+SUPHARNT_IMPORT_SYSCALL(NtTerminateProcess, 8)
+SUPHARNT_IMPORT_SYSCALL(NtTerminateThread, 8)
+SUPHARNT_IMPORT_SYSCALL(NtTestAlert, 0)
+SUPHARNT_IMPORT_SYSCALL(NtUnmapViewOfSection, 8)
+SUPHARNT_IMPORT_SYSCALL(NtWaitForMultipleObjects, 20)
+SUPHARNT_IMPORT_SYSCALL(NtWaitForSingleObject, 12)
+SUPHARNT_IMPORT_SYSCALL(NtWriteFile, 36)
+SUPHARNT_IMPORT_SYSCALL(NtWriteVirtualMemory, 20)
+SUPHARNT_IMPORT_SYSCALL(NtYieldExecution, 0)
+SUPHARNT_IMPORT_SYSCALL(NtCreateSection, 28)
+SUPHARNT_IMPORT_SYSCALL(NtQueryVolumeInformationFile, 20)
+
+SUPHARNT_IMPORT_STDCALL_EARLY(LdrInitializeThunk, 12)
+SUPHARNT_IMPORT_STDCALL_EARLY_OPTIONAL(LdrRegisterDllNotification, 16)
+SUPHARNT_IMPORT_STDCALL_EARLY(LdrGetDllHandle, 16)
+SUPHARNT_IMPORT_STDCALL_EARLY_OPTIONAL(ApiSetQueryApiSetPresence, 8)
+
+SUPHARNT_IMPORT_STDCALL(RtlAddAccessAllowedAce, 16)
+SUPHARNT_IMPORT_STDCALL(RtlAddAccessDeniedAce, 16)
+SUPHARNT_IMPORT_STDCALL(RtlAllocateHeap, 12)
+SUPHARNT_IMPORT_STDCALL(RtlCompactHeap, 8)
+SUPHARNT_IMPORT_STDCALL(RtlCopySid, 12)
+SUPHARNT_IMPORT_STDCALL(RtlCreateAcl, 12)
+SUPHARNT_IMPORT_STDCALL(RtlCreateHeap, 24)
+SUPHARNT_IMPORT_STDCALL(RtlCreateProcessParameters, 40)
+SUPHARNT_IMPORT_STDCALL(RtlCreateSecurityDescriptor, 8)
+SUPHARNT_IMPORT_STDCALL(RtlCreateUserProcess, 40)
+SUPHARNT_IMPORT_STDCALL(RtlCreateUserThread, 40)
+SUPHARNT_IMPORT_STDCALL(RtlDestroyProcessParameters, 4)
+SUPHARNT_IMPORT_STDCALL_EARLY(RtlDosApplyFileIsolationRedirection_Ustr, 36)
+SUPHARNT_IMPORT_STDCALL_EARLY(RtlEqualSid, 8)
+SUPHARNT_IMPORT_STDCALL_EARLY_OPTIONAL(RtlExitUserProcess, 4)
+SUPHARNT_IMPORT_STDCALL_EARLY(RtlExitUserThread, 4)
+SUPHARNT_IMPORT_STDCALL(RtlExpandEnvironmentStrings_U, 16)
+SUPHARNT_IMPORT_STDCALL(RtlFreeHeap, 12)
+SUPHARNT_IMPORT_STDCALL_EARLY(RtlFreeUnicodeString, 4)
+SUPHARNT_IMPORT_STDCALL_EARLY(RtlGetLastNtStatus, 0)
+SUPHARNT_IMPORT_STDCALL_EARLY(RtlGetLastWin32Error, 0)
+SUPHARNT_IMPORT_STDCALL_EARLY(RtlGetVersion, 4)
+SUPHARNT_IMPORT_STDCALL_EARLY(RtlInitializeSid, 12)
+SUPHARNT_IMPORT_STDCALL_EARLY(RtlNtStatusToDosError, 4)
+SUPHARNT_IMPORT_STDCALL_EARLY(RtlReAllocateHeap, 16)
+SUPHARNT_IMPORT_STDCALL_EARLY(RtlRestoreLastWin32Error, 4)
+SUPHARNT_IMPORT_STDCALL(RtlSetDaclSecurityDescriptor, 16)
+SUPHARNT_IMPORT_STDCALL_EARLY(RtlSetLastWin32Error, 4)
+SUPHARNT_IMPORT_STDCALL_EARLY(RtlSetLastWin32ErrorAndNtStatusFromNtStatus, 4)
+SUPHARNT_IMPORT_STDCALL(RtlSizeHeap, 12)
+SUPHARNT_IMPORT_STDCALL_EARLY(RtlSubAuthoritySid, 8)
+
diff --git a/src/VBox/HostDrivers/VBoxNetAdp/Makefile.kmk b/src/VBox/HostDrivers/VBoxNetAdp/Makefile.kmk
new file mode 100644
index 00000000..62c8baf4
--- /dev/null
+++ b/src/VBox/HostDrivers/VBoxNetAdp/Makefile.kmk
@@ -0,0 +1,228 @@
+# $Id: Makefile.kmk $
+## @file
+# Sub-Makefile for the Network Adapter Driver (VBoxNetAdp).
+#
+
+#
+# Copyright (C) 2009-2023 Oracle and/or its affiliates.
+#
+# This file is part of VirtualBox base platform packages, as
+# available from https://www.virtualbox.org.
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation, in version 3 of the
+# License.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, see <https://www.gnu.org/licenses>.
+#
+# The contents of this file may alternatively be used under the terms
+# of the Common Development and Distribution License Version 1.0
+# (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+# in the VirtualBox distribution, in which case the provisions of the
+# CDDL are applicable instead of those of the GPL.
+#
+# You may elect to license modified versions of this file under the
+# terms and conditions of either the GPL or the CDDL or both.
+#
+# SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+#
+
+SUB_DEPTH = ../../../..
+include $(KBUILD_PATH)/subheader.kmk
+
+if1of ($(KBUILD_TARGET), solaris darwin freebsd)
+ #
+ # VBoxNetAdp - Virtual Network Adapter
+ # Note! On Solaris the name has to be <= 8 chars long.
+ #
+ ifdef VBOX_WITH_VBOXDRV
+ SYSMODS += VBoxNetAdp
+ VBoxNetAdp_TEMPLATE = VBoxR0Drv
+ VBoxNetAdp_INST = $(INST_VBOXNETADP)$(if $(eq $(KBUILD_TARGET),darwin),Contents/MacOS/)
+ VBoxNetAdp_DEBUG_INST.darwin = $(patsubst %/,%,$(INST_VBOXNETADP))
+ VBoxNetAdp_NAME.solaris = vboxnet
+ VBoxNetAdp_NAME.freebsd = vboxnetadp
+ VBoxNetAdp_DEPS.solaris += $(VBOX_SVN_REV_KMK)
+ VBoxNetAdp_DEFS = IN_RT_R0 VBOX_SVN_REV=$(VBOX_SVN_REV) IN_SUP_STATIC
+ #VBoxNetAdp_LDFLAGS.darwin = -v -Wl,-whyload -Wl,-v -Wl,-whatsloaded
+ VBoxNetAdp_LDFLAGS.solaris += -N misc/gld -N drv/vboxdrv
+ VBoxNetAdp_INCS = \
+ .
+ VBoxNetAdp_SOURCES.darwin = \
+ darwin/VBoxNetAdp-darwin.cpp \
+ VBoxNetAdp.c
+ VBoxNetAdp_SOURCES.solaris = \
+ solaris/VBoxNetAdp-solaris.c
+ VBoxNetAdp_SOURCES.freebsd = \
+ freebsd/VBoxNetAdp-freebsd.c \
+ VBoxNetAdp.c
+ VBoxNetAdp_SOURCES =
+ #VBoxNetAdp_SOURCES = \
+ # VBoxNetAdp.c
+ VBoxNetAdp_LIBS += \
+ $(PATH_STAGE_LIB)/SUPR0IdcClient$(VBOX_SUFF_LIB)
+ endif
+endif
+
+#
+# Darwin extras.
+#
+if "$(KBUILD_TARGET)" == "darwin" && defined(VBOX_WITH_VBOXDRV)
+ INSTALLS += VBoxNetAdp.kext
+ VBoxNetAdp.kext_INST = $(INST_VBOXNETADP)Contents/
+ VBoxNetAdp.kext_SOURCES = $(VBoxNetAdp.kext_0_OUTDIR)/Contents/Info.plist
+ VBoxNetAdp.kext_CLEAN = $(VBoxNetAdp.kext_0_OUTDIR)/Contents/Info.plist
+ VBoxNetAdp.kext_BLDDIRS = $(VBoxNetAdp.kext_0_OUTDIR)/Contents/
+
+ $$(VBoxNetAdp.kext_0_OUTDIR)/Contents/Info.plist: $(PATH_SUB_CURRENT)/darwin/Info.plist $(VBOX_VERSION_MK) | $$(dir $$@)
+ $(call MSG_GENERATE,VBoxNetAdp,$@,$<)
+ $(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,VBoxNetAdp)
+
+ INSTALLS.darwin += Scripts-darwin-adp
+ Scripts-darwin-adp_INST = $(INST_DIST)
+ Scripts-darwin-adp_EXEC_SOURCES = \
+ darwin/loadnetadp.sh
+endif # darwin && host-drivers
+
+ifeq ($(KBUILD_TARGET),win)
+ #
+ # VBoxNetAdp6.sys - The VirtualBox Adapter miniport driver.
+ #
+ SYSMODS.win += VBoxNetAdp6
+ VBoxNetAdp6_TEMPLATE = VBoxR0Drv
+ if defined(VBOX_SIGNING_MODE)
+ VBoxNetAdp6_INSTTYPE.win = none
+ VBoxNetAdp6_DEBUG_INSTTYPE.win = both
+ endif
+ VBoxNetAdp6_DEFS = IN_RT_R0 IN_SUP_STATIC
+ VBoxNetAdp6_INCS := $(PATH_SUB_CURRENT)
+ VBoxNetAdp6_SDKS = ReorderCompilerIncs $(VBOX_WINDDK_WLH) $(VBOX_WINPSDK_INCS)
+ VBoxNetAdp6_SOURCES = \
+ win/VBoxNetAdp-win.cpp \
+ win/VBoxNetAdp-win.rc
+ VBoxNetAdp6_DEFS += NDIS_MINIPORT_DRIVER NDIS_WDM=1 BINARY_COMPATIBLE=0
+ VBoxNetAdp6_DEFS += NDIS60_MINIPORT=1 NDIS60=1
+ VBoxNetAdp6_LDFLAGS.win.x86 = -Entry:DriverEntry@8
+ VBoxNetAdp6_LDFLAGS.win.amd64 = -Entry:DriverEntry
+ VBoxNetAdp6_LIBS.win = \
+ $(PATH_SDK_$(VBOX_WINDDK)_LIB)/ntoskrnl.lib \
+ $(PATH_SDK_$(VBOX_WINDDK)_LIB)/hal.lib \
+ $(PATH_SDK_$(VBOX_WINDDK)_LIB)/ndis.lib \
+ $(PATH_STAGE_LIB)/RuntimeR0Drv$(VBOX_SUFF_LIB)
+ VBoxNetAdp6_LIBS = \
+ $(PATH_STAGE_LIB)/SUPR0IdcClient$(VBOX_SUFF_LIB)
+
+
+ INSTALLS.win += VBoxNetAdp6-inf
+ VBoxNetAdp6-inf_TEMPLATE = VBoxR0DrvInfCat
+ VBoxNetAdp6-inf_SOURCES = \
+ $(PATH_TARGET)/VBoxNetAdp6Cat.dir/VBoxNetAdp6.inf
+ VBoxNetAdp6-inf_CLEAN = $(VBoxNetAdp6-inf_SOURCES)
+ VBoxNetAdp6-inf_BLDDIRS = $(PATH_TARGET)/VBoxNetAdp6Cat.dir
+
+ $(PATH_TARGET)/VBoxNetAdp6Cat.dir/VBoxNetAdp6.inf: $(PATH_SUB_CURRENT)/win/VBoxNetAdp6.inf $(MAKEFILE_CURRENT) | $$(dir $$@)
+ $(call MSG_GENERATE,VBoxNetAdp6-inf,$@,$<)
+ $(call VBOX_EDIT_INF_FN,$<,$@)
+
+ ifdef VBOX_SIGNING_MODE
+ VBoxNetAdp6-inf_SOURCES += \
+ $(PATH_TARGET)/VBoxNetAdp6Cat.dir/VBoxNetAdp6.sys \
+ $(PATH_TARGET)/VBoxNetAdp6Cat.dir/VBoxNetAdp6.cat \
+ $(PATH_TARGET)/VBoxNetAdp6Cat.dir/VBoxNetAdp6.cat=>VBoxNetAdp6-PreW10.cat
+
+ $(PATH_TARGET)/VBoxNetAdp6Cat.dir/VBoxNetAdp6.sys: $$(VBoxNetAdp6_1_TARGET) | $$(dir $$@)
+ $(INSTALL) -m 644 $< $(@D)
+
+ $(PATH_TARGET)/VBoxNetAdp6Cat.dir/VBoxNetAdp6.cat: \
+ $(PATH_TARGET)/VBoxNetAdp6Cat.dir/VBoxNetAdp6.sys \
+ $(PATH_TARGET)/VBoxNetAdp6Cat.dir/VBoxNetAdp6.inf
+ $(call MSG_TOOL,Inf2Cat,VBoxNetFlt-inf,$@,$<)
+ $(call VBOX_MAKE_CAT_FN, $(@D),$@)
+
+ endif # ifdef VBOX_SIGNING_MODE
+
+endif #ifeq ($(KBUILD_TARGET), win)
+
+ifeq ($(KBUILD_TARGET),linux)
+ #
+ # Install source files for compilation on Linux.
+ # files_vboxnetadp defines VBOX_VBOXNETADP_SOURCES.
+ #
+ INSTALLS += VBoxNetAdp-src
+ include $(PATH_SUB_CURRENT)/linux/files_vboxnetadp
+ VBoxNetAdp-src_INST = bin/src/vboxnetadp/
+ VBoxNetAdp-src_SOURCES = \
+ $(subst $(DQUOTE),,$(VBOX_VBOXNETADP_SOURCES)) \
+ $(VBoxNetAdp-src_0_OUTDIR)/Makefile
+ VBoxNetAdp-src_CLEAN = \
+ $(VBoxNetAdp-src_0_OUTDIR)/Makefile \
+ $(PATH_TARGET)/VBoxNetAdp-src-1.dep
+
+ # Scripts needed for building the kernel modules
+ includedep $(PATH_TARGET)/VBoxNetAdp-src-1.dep
+ $$(VBoxNetAdp-src_0_OUTDIR)/Makefile: \
+ $(PATH_SUB_CURRENT)/linux/Makefile \
+ $$(if $$(eq $$(VBoxNetAdp/linux/Makefile_VBOX_HARDENED),$$(VBOX_WITH_HARDENING)),,FORCE) \
+ | $$(dir $$@)
+ $(QUIET)$(RM) -f -- $@
+ ifndef VBOX_WITH_HARDENING
+ $(QUIET)$(SED) -e "s;VBOX_WITH_HARDENING;;g" --output $@ $<
+ else
+ $(QUIET)$(CP) -f $< $@
+ endif
+ %$(QUIET2)$(APPEND) -t '$(PATH_TARGET)/VBoxNetAdp-src-1.dep' 'VBoxNetAdp/linux/Makefile_VBOX_HARDENED=$(VBOX_WITH_HARDENING)'
+
+ #
+ # Build test for the linux host kernel modules.
+ #
+ $(evalcall2 VBOX_LINUX_KMOD_TEST_BUILD_RULE_FN,VBoxNetAdp-src,vboxdrv-src,)
+endif # linux
+
+ifeq ($(KBUILD_TARGET),freebsd)
+ #
+ # Install source files for compilation on FreeBSD.
+ # files_vboxnetadp defines VBOX_VBOXNETADP_SOURCES.
+ #
+ INSTALLS += VBoxNetAdp-src
+ include $(PATH_SUB_CURRENT)/freebsd/files_vboxnetadp
+ VBoxNetAdp-src_INST = bin/src/vboxnetadp/
+ VBoxNetAdp-src_SOURCES = \
+ $(subst $(DQUOTE),,$(VBOX_VBOXNETADP_SOURCES)) \
+ $(VBoxNetAdp-src_0_OUTDIR)/Makefile
+ VBoxNetAdp-src_CLEAN = \
+ $(VBoxNetAdp-src_0_OUTDIR)/Makefile
+
+ $$(VBoxNetAdp-src_0_OUTDIR)/Makefile: \
+ $(PATH_SUB_CURRENT)/freebsd/Makefile \
+ $$(if $$(eq $$(VBoxNetAdp/freebsd/Makefile_VBOX_HARDENED),$$(VBOX_WITH_HARDENING)),,FORCE) \
+ | $$(dir $$@)
+ $(QUIET)$(RM) -f -- $@
+ ifndef VBOX_WITH_HARDENING
+ $(QUIET)$(SED) -e "s;VBOX_WITH_HARDENING;;g" --output $@ $<
+ else
+ $(QUIET)$(CP) -f $< $@
+ endif
+
+endif # freebsd
+
+include $(FILE_KBUILD_SUB_FOOTER)
+
diff --git a/src/VBox/HostDrivers/VBoxNetAdp/VBoxNetAdp.c b/src/VBox/HostDrivers/VBoxNetAdp/VBoxNetAdp.c
new file mode 100644
index 00000000..ea3f7766
--- /dev/null
+++ b/src/VBox/HostDrivers/VBoxNetAdp/VBoxNetAdp.c
@@ -0,0 +1,238 @@
+/* $Id: VBoxNetAdp.c $ */
+/** @file
+ * VBoxNetAdp - Virtual Network Adapter Driver (Host), Common Code.
+ */
+
+/*
+ * Copyright (C) 2008-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+/** @page pg_netadp VBoxNetAdp - Network Adapter
+ *
+ * This is a kernel module that creates a virtual interface that can be attached
+ * to an internal network.
+ *
+ * In the big picture we're one of the three trunk interface on the internal
+ * network, the one named "TAP Interface": @image html Networking_Overview.gif
+ *
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#define LOG_GROUP LOG_GROUP_NET_ADP_DRV
+#include "VBoxNetAdpInternal.h"
+
+#include <VBox/log.h>
+#include <VBox/err.h>
+#include <iprt/string.h>
+
+
+VBOXNETADP g_aAdapters[VBOXNETADP_MAX_INSTANCES];
+static uint8_t g_aUnits[VBOXNETADP_MAX_UNITS/8];
+
+
+DECLINLINE(int) vboxNetAdpGetUnitByName(const char *pcszName)
+{
+ uint32_t iUnit = RTStrToUInt32(pcszName + sizeof(VBOXNETADP_NAME) - 1);
+ bool fOld;
+
+ if (iUnit >= VBOXNETADP_MAX_UNITS)
+ return -1;
+
+ fOld = ASMAtomicBitTestAndSet(g_aUnits, iUnit);
+ return fOld ? -1 : (int)iUnit;
+}
+
+DECLINLINE(int) vboxNetAdpGetNextAvailableUnit(void)
+{
+ bool fOld;
+ int iUnit;
+ /* There is absolutely no chance that all units are taken */
+ do {
+ iUnit = ASMBitFirstClear(g_aUnits, VBOXNETADP_MAX_UNITS);
+ if (iUnit < 0)
+ break;
+ fOld = ASMAtomicBitTestAndSet(g_aUnits, iUnit);
+ } while (fOld);
+
+ return iUnit;
+}
+
+DECLINLINE(void) vboxNetAdpReleaseUnit(int iUnit)
+{
+ bool fSet = ASMAtomicBitTestAndClear(g_aUnits, iUnit);
+ NOREF(fSet);
+ Assert(fSet);
+}
+
+/**
+ * Generate a suitable MAC address.
+ *
+ * @param pThis The instance.
+ * @param pMac Where to return the MAC address.
+ */
+DECLHIDDEN(void) vboxNetAdpComposeMACAddress(PVBOXNETADP pThis, PRTMAC pMac)
+{
+ /* Use a locally administered version of the OUI we use for the guest NICs. */
+ pMac->au8[0] = 0x08 | 2;
+ pMac->au8[1] = 0x00;
+ pMac->au8[2] = 0x27;
+
+ pMac->au8[3] = 0; /* pThis->iUnit >> 16; */
+ pMac->au8[4] = 0; /* pThis->iUnit >> 8; */
+ pMac->au8[5] = pThis->iUnit;
+}
+
+int vboxNetAdpCreate(PVBOXNETADP *ppNew, const char *pcszName)
+{
+ int rc;
+ unsigned i;
+ for (i = 0; i < RT_ELEMENTS(g_aAdapters); i++)
+ {
+ PVBOXNETADP pThis = &g_aAdapters[i];
+
+ if (ASMAtomicCmpXchgU32((uint32_t volatile *)&pThis->enmState, kVBoxNetAdpState_Transitional, kVBoxNetAdpState_Invalid))
+ {
+ RTMAC Mac;
+ /* Found an empty slot -- use it. */
+ Log(("vboxNetAdpCreate: found empty slot: %d\n", i));
+ if (pcszName)
+ {
+ Log(("vboxNetAdpCreate: using name: %s\n", pcszName));
+ pThis->iUnit = vboxNetAdpGetUnitByName(pcszName);
+ strncpy(pThis->szName, pcszName, sizeof(pThis->szName) - 1);
+ pThis->szName[sizeof(pThis->szName) - 1] = '\0';
+ }
+ else
+ {
+ pThis->iUnit = vboxNetAdpGetNextAvailableUnit();
+ pThis->szName[0] = '\0';
+ }
+ if (pThis->iUnit < 0)
+ rc = VERR_INVALID_PARAMETER;
+ else
+ {
+ vboxNetAdpComposeMACAddress(pThis, &Mac);
+ rc = vboxNetAdpOsCreate(pThis, &Mac);
+ Log(("vboxNetAdpCreate: pThis=%p pThis->iUnit=%d, pThis->szName=%s\n",
+ pThis, pThis->iUnit, pThis->szName));
+ }
+ if (RT_SUCCESS(rc))
+ {
+ *ppNew = pThis;
+ ASMAtomicWriteU32((uint32_t volatile *)&pThis->enmState, kVBoxNetAdpState_Active);
+ Log2(("VBoxNetAdpCreate: Created %s\n", g_aAdapters[i].szName));
+ }
+ else
+ {
+ ASMAtomicWriteU32((uint32_t volatile *)&pThis->enmState, kVBoxNetAdpState_Invalid);
+ Log(("vboxNetAdpCreate: vboxNetAdpOsCreate failed with '%Rrc'.\n", rc));
+ }
+ for (i = 0; i < RT_ELEMENTS(g_aAdapters); i++)
+ Log2(("VBoxNetAdpCreate: Scanning entry: state=%d unit=%d name=%s\n",
+ g_aAdapters[i].enmState, g_aAdapters[i].iUnit, g_aAdapters[i].szName));
+ return rc;
+ }
+ }
+ Log(("vboxNetAdpCreate: no empty slots!\n"));
+
+ /* All slots in adapter array are busy. */
+ return VERR_OUT_OF_RESOURCES;
+}
+
+int vboxNetAdpDestroy(PVBOXNETADP pThis)
+{
+ int rc = VINF_SUCCESS;
+
+ if (!ASMAtomicCmpXchgU32((uint32_t volatile *)&pThis->enmState, kVBoxNetAdpState_Transitional, kVBoxNetAdpState_Active))
+ return VERR_INTNET_FLT_IF_BUSY;
+
+ Assert(pThis->iUnit >= 0 && pThis->iUnit < VBOXNETADP_MAX_UNITS);
+ vboxNetAdpOsDestroy(pThis);
+ vboxNetAdpReleaseUnit(pThis->iUnit);
+ pThis->iUnit = -1;
+ pThis->szName[0] = '\0';
+
+ ASMAtomicWriteU32((uint32_t volatile *)&pThis->enmState, kVBoxNetAdpState_Invalid);
+
+ return rc;
+}
+
+int vboxNetAdpInit(void)
+{
+ unsigned i;
+ /*
+ * Init common members and call OS-specific init.
+ */
+ memset(g_aUnits, 0, sizeof(g_aUnits));
+ memset(g_aAdapters, 0, sizeof(g_aAdapters));
+ LogFlow(("vboxnetadp: max host-only interfaces supported: %d (%d bytes)\n",
+ VBOXNETADP_MAX_INSTANCES, sizeof(g_aAdapters)));
+ for (i = 0; i < RT_ELEMENTS(g_aAdapters); i++)
+ {
+ g_aAdapters[i].enmState = kVBoxNetAdpState_Invalid;
+ g_aAdapters[i].iUnit = -1;
+ vboxNetAdpOsInit(&g_aAdapters[i]);
+ }
+
+ return VINF_SUCCESS;
+}
+
+/**
+ * Finds an adapter by its name.
+ *
+ * @returns Pointer to the instance by the given name. NULL if not found.
+ * @param pszName The name of the instance.
+ */
+PVBOXNETADP vboxNetAdpFindByName(const char *pszName)
+{
+ unsigned i;
+
+ for (i = 0; i < RT_ELEMENTS(g_aAdapters); i++)
+ {
+ PVBOXNETADP pThis = &g_aAdapters[i];
+ Log2(("VBoxNetAdp: Scanning entry: state=%d name=%s\n", pThis->enmState, pThis->szName));
+ if ( strcmp(pThis->szName, pszName) == 0
+ && ASMAtomicReadU32((uint32_t volatile *)&pThis->enmState) == kVBoxNetAdpState_Active)
+ return pThis;
+ }
+ return NULL;
+}
+
+void vboxNetAdpShutdown(void)
+{
+ unsigned i;
+
+ /* Remove virtual adapters */
+ for (i = 0; i < RT_ELEMENTS(g_aAdapters); i++)
+ vboxNetAdpDestroy(&g_aAdapters[i]);
+}
diff --git a/src/VBox/HostDrivers/VBoxNetAdp/VBoxNetAdpInternal.h b/src/VBox/HostDrivers/VBoxNetAdp/VBoxNetAdpInternal.h
new file mode 100644
index 00000000..d50fa039
--- /dev/null
+++ b/src/VBox/HostDrivers/VBoxNetAdp/VBoxNetAdpInternal.h
@@ -0,0 +1,202 @@
+/* $Id: VBoxNetAdpInternal.h $ */
+/** @file
+ * VBoxNetAdp - Network Filter Driver (Host), Internal Header.
+ */
+
+/*
+ * Copyright (C) 2008-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+#ifndef VBOX_INCLUDED_SRC_VBoxNetAdp_VBoxNetAdpInternal_h
+#define VBOX_INCLUDED_SRC_VBoxNetAdp_VBoxNetAdpInternal_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+#include <VBox/sup.h>
+#include <VBox/intnet.h>
+#include <iprt/semaphore.h>
+#include <iprt/assert.h>
+
+
+RT_C_DECLS_BEGIN
+
+/** Pointer to the globals. */
+typedef struct VBOXNETADPGLOBALS *PVBOXNETADPGLOBALS;
+
+#define VBOXNETADP_MAX_INSTANCES 128
+#define VBOXNETADP_MAX_UNITS 128
+#define VBOXNETADP_NAME "vboxnet"
+#define VBOXNETADP_MAX_NAME_LEN 32
+#define VBOXNETADP_MTU 1500
+#if defined(RT_OS_DARWIN)
+# define VBOXNETADP_MAX_FAMILIES 4
+# define VBOXNETADP_DETACH_TIMEOUT 500
+#endif
+
+#define VBOXNETADP_CTL_DEV_NAME "vboxnetctl"
+#define VBOXNETADP_CTL_ADD _IOWR('v', 1, VBOXNETADPREQ)
+#define VBOXNETADP_CTL_REMOVE _IOW('v', 2, VBOXNETADPREQ)
+
+typedef struct VBoxNetAdpReq
+{
+ char szName[VBOXNETADP_MAX_NAME_LEN];
+} VBOXNETADPREQ;
+typedef VBOXNETADPREQ *PVBOXNETADPREQ;
+
+/**
+ * Void entries mark vacant slots in adapter array. Valid entries are busy slots.
+ * As soon as slot is being modified its state changes to transitional.
+ * An entry in transitional state must only be accessed by the thread that
+ * put it to this state.
+ */
+/**
+ * To avoid races on adapter fields we stick to the following rules:
+ * - rewrite: Int net port calls are serialized
+ * - No modifications are allowed on busy adapters (deactivate first)
+ * Refuse to destroy adapter until it gets to available state
+ * - No transfers (thus getting busy) on inactive adapters
+ * - Init sequence: void->available->connected->active
+ 1) Create
+ 2) Connect
+ 3) Activate
+ * - Destruction sequence: active->connected->available->void
+ 1) Deactivate
+ 2) Disconnect
+ 3) Destroy
+*/
+
+enum VBoxNetAdpState
+{
+ kVBoxNetAdpState_Invalid,
+ kVBoxNetAdpState_Transitional,
+ kVBoxNetAdpState_Active,
+ kVBoxNetAdpState_32BitHack = 0x7FFFFFFF
+};
+typedef enum VBoxNetAdpState VBOXNETADPSTATE;
+
+struct VBoxNetAdapter
+{
+ /** Denotes availability of this slot in adapter array. */
+ VBOXNETADPSTATE enmState;
+ /** Corresponds to the digit at the end of device name. */
+ int iUnit;
+
+ union
+ {
+#ifdef VBOXNETADP_OS_SPECFIC
+ struct
+ {
+# if defined(RT_OS_DARWIN)
+ /** @name Darwin instance data.
+ * @{ */
+ /** Event to signal detachment of interface. */
+ RTSEMEVENT hEvtDetached;
+ /** Pointer to Darwin interface structure. */
+ ifnet_t pIface;
+ /** MAC address. */
+ RTMAC Mac;
+ /** @} */
+# elif defined(RT_OS_LINUX)
+ /** @name Darwin instance data.
+ * @{ */
+ /** Pointer to Linux network device structure. */
+ struct net_device *pNetDev;
+ /** @} */
+# elif defined(RT_OS_FREEBSD)
+ /** @name FreeBSD instance data.
+ * @{ */
+ struct ifnet *ifp;
+ /** @} */
+# else
+# error PORTME
+# endif
+ } s;
+#endif
+ /** Union alignment to a pointer. */
+ void *pvAlign;
+ /** Padding. */
+ uint8_t abPadding[64];
+ } u;
+ /** The interface name. */
+ char szName[VBOXNETADP_MAX_NAME_LEN];
+};
+typedef struct VBoxNetAdapter VBOXNETADP;
+typedef VBOXNETADP *PVBOXNETADP;
+/* Paranoia checks for alignment and padding. */
+AssertCompileMemberAlignment(VBOXNETADP, u, ARCH_BITS/8);
+AssertCompileMemberAlignment(VBOXNETADP, szName, ARCH_BITS/8);
+AssertCompileMembersSameSize(VBOXNETADP, u, VBOXNETADP, u.abPadding);
+
+DECLHIDDEN(int) vboxNetAdpInit(void);
+DECLHIDDEN(void) vboxNetAdpShutdown(void);
+DECLHIDDEN(int) vboxNetAdpCreate(PVBOXNETADP *ppNew, const char *pcszName);
+DECLHIDDEN(int) vboxNetAdpDestroy(PVBOXNETADP pThis);
+DECLHIDDEN(PVBOXNETADP) vboxNetAdpFindByName(const char *pszName);
+DECLHIDDEN(void) vboxNetAdpComposeMACAddress(PVBOXNETADP pThis, PRTMAC pMac);
+
+
+/**
+ * This is called to perform OS-specific structure initializations.
+ *
+ * @return IPRT status code.
+ * @param pThis The new instance.
+ *
+ * @remarks Owns no locks.
+ */
+DECLHIDDEN(int) vboxNetAdpOsInit(PVBOXNETADP pThis);
+
+/**
+ * Counter part to vboxNetAdpOsCreate().
+ *
+ * @param pThis The new instance.
+ *
+ * @remarks May own the semaphores for the global list, the network lock and the out-bound trunk port.
+ */
+DECLHIDDEN(void) vboxNetAdpOsDestroy(PVBOXNETADP pThis);
+
+/**
+ * This is called to attach to the actual host interface
+ * after linking the instance into the list.
+ *
+ * @return IPRT status code.
+ * @param pThis The new instance.
+ * @param pMac The MAC address to use for this instance.
+ *
+ * @remarks Owns no locks.
+ */
+DECLHIDDEN(int) vboxNetAdpOsCreate(PVBOXNETADP pThis, PCRTMAC pMac);
+
+
+
+RT_C_DECLS_END
+
+#endif /* !VBOX_INCLUDED_SRC_VBoxNetAdp_VBoxNetAdpInternal_h */
+
diff --git a/src/VBox/HostDrivers/VBoxNetAdp/darwin/Info.plist b/src/VBox/HostDrivers/VBoxNetAdp/darwin/Info.plist
new file mode 100644
index 00000000..c0b20e7b
--- /dev/null
+++ b/src/VBox/HostDrivers/VBoxNetAdp/darwin/Info.plist
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+ <key>CFBundleDevelopmentRegion</key> <string>English</string>
+ <key>CFBundleExecutable</key> <string>VBoxNetAdp</string>
+ <key>CFBundleIdentifier</key> <string>org.virtualbox.kext.VBoxNetAdp</string>
+ <key>CFBundleInfoDictionaryVersion</key> <string>6.0</string>
+ <key>CFBundleName</key> <string>VBoxNetAdp</string>
+ <key>CFBundlePackageType</key> <string>KEXT</string>
+ <key>CFBundleSignature</key> <string>????</string>
+ <key>CFBundleGetInfoString</key> <string>@VBOX_PRODUCT@ @VBOX_VERSION_STRING@, © 2008-@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>IOKitPersonalities</key>
+ <dict>
+ </dict>
+ <key>OSBundleLibraries</key>
+ <dict>
+ <key>com.apple.kpi.bsd</key> <string>8.8.1</string>
+ <key>com.apple.kpi.mach</key> <string>8.8.1</string>
+ <key>com.apple.kpi.libkern</key> <string>8.8.1</string>
+ <key>org.virtualbox.kext.VBoxDrv</key> <string>@VBOX_VERSION_MAJOR@.@VBOX_VERSION_MINOR@.@VBOX_VERSION_BUILD@</string>
+ </dict>
+</dict>
+</plist>
diff --git a/src/VBox/HostDrivers/VBoxNetAdp/darwin/Makefile.kup b/src/VBox/HostDrivers/VBoxNetAdp/darwin/Makefile.kup
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/src/VBox/HostDrivers/VBoxNetAdp/darwin/Makefile.kup
diff --git a/src/VBox/HostDrivers/VBoxNetAdp/darwin/VBoxNetAdp-darwin.cpp b/src/VBox/HostDrivers/VBoxNetAdp/darwin/VBoxNetAdp-darwin.cpp
new file mode 100644
index 00000000..ecf4d1f5
--- /dev/null
+++ b/src/VBox/HostDrivers/VBoxNetAdp/darwin/VBoxNetAdp-darwin.cpp
@@ -0,0 +1,529 @@
+/* $Id: VBoxNetAdp-darwin.cpp $ */
+/** @file
+ * VBoxNetAdp - Virtual Network Adapter Driver (Host), Darwin Specific Code.
+ */
+
+/*
+ * Copyright (C) 2008-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#define LOG_GROUP LOG_GROUP_NET_ADP_DRV
+#include "../../../Runtime/r0drv/darwin/the-darwin-kernel.h"
+
+#include <VBox/log.h>
+#include <VBox/err.h>
+#include <VBox/version.h>
+#include <iprt/assert.h>
+#include <iprt/initterm.h>
+#include <iprt/semaphore.h>
+#include <iprt/spinlock.h>
+#include <iprt/string.h>
+#include <iprt/uuid.h>
+#include <iprt/alloca.h>
+
+#include "../../darwin/VBoxNetSend.h"
+
+#include <sys/systm.h>
+RT_C_DECLS_BEGIN /* Buggy 10.4 headers, fixed in 10.5. */
+#include <sys/kpi_mbuf.h>
+RT_C_DECLS_END
+
+#include <net/ethernet.h>
+#include <net/if_ether.h>
+#include <net/if_types.h>
+#include <sys/socket.h>
+#include <net/if.h>
+#include <net/if_dl.h>
+#include <sys/errno.h>
+#include <sys/param.h>
+#include <sys/conf.h>
+#include <miscfs/devfs/devfs.h>
+RT_C_DECLS_BEGIN
+#include <net/bpf.h>
+RT_C_DECLS_END
+
+#define VBOXNETADP_OS_SPECFIC 1
+#include "../VBoxNetAdpInternal.h"
+
+
+/*********************************************************************************************************************************
+* Defined Constants And Macros *
+*********************************************************************************************************************************/
+/** The maximum number of SG segments.
+ * Used to prevent stack overflow and similar bad stuff. */
+#define VBOXNETADP_DARWIN_MAX_SEGS 32
+#define VBOXNETADP_DARWIN_MAX_FAMILIES 4
+#define VBOXNETADP_DARWIN_NAME "vboxnet"
+#define VBOXNETADP_DARWIN_MTU 1500
+#define VBOXNETADP_DARWIN_DETACH_TIMEOUT 500
+
+#define VBOXNETADP_FROM_IFACE(iface) ((PVBOXNETADP) ifnet_softc(iface))
+
+
+/*********************************************************************************************************************************
+* Internal Functions *
+*********************************************************************************************************************************/
+RT_C_DECLS_BEGIN
+static kern_return_t VBoxNetAdpDarwinStart(struct kmod_info *pKModInfo, void *pvData);
+static kern_return_t VBoxNetAdpDarwinStop(struct kmod_info *pKModInfo, void *pvData);
+RT_C_DECLS_END
+
+static int VBoxNetAdpDarwinOpen(dev_t Dev, int fFlags, int fDevType, struct proc *pProcess);
+static int VBoxNetAdpDarwinClose(dev_t Dev, int fFlags, int fDevType, struct proc *pProcess);
+static int VBoxNetAdpDarwinIOCtl(dev_t Dev, u_long iCmd, caddr_t pData, int fFlags, struct proc *pProcess);
+
+
+/*********************************************************************************************************************************
+* 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(VBoxNetAdp, VBOX_VERSION_STRING, _start, _stop)
+DECL_HIDDEN_DATA(kmod_start_func_t *) _realmain = VBoxNetAdpDarwinStart;
+DECL_HIDDEN_DATA(kmod_stop_func_t *) _antimain = VBoxNetAdpDarwinStop;
+DECL_HIDDEN_DATA(int) _kext_apple_cc = __APPLE_CC__;
+RT_C_DECLS_END
+
+/**
+ * The (common) global data.
+ */
+static int g_nCtlDev = -1; /* Major dev number */
+static void *g_hCtlDev = 0; /* FS dev handle */
+
+/**
+ * The character device switch table for the driver.
+ */
+static struct cdevsw g_ChDev =
+{
+ /*.d_open = */VBoxNetAdpDarwinOpen,
+ /*.d_close = */VBoxNetAdpDarwinClose,
+ /*.d_read = */eno_rdwrt,
+ /*.d_write = */eno_rdwrt,
+ /*.d_ioctl = */VBoxNetAdpDarwinIOCtl,
+ /*.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
+};
+
+
+
+static void vboxNetAdpDarwinComposeUUID(PVBOXNETADP pThis, PRTUUID pUuid)
+{
+ /* Generate UUID from name and MAC address. */
+ RTUuidClear(pUuid);
+ memcpy(pUuid->au8, "vboxnet", 7);
+ pUuid->Gen.u8ClockSeqHiAndReserved = (pUuid->Gen.u8ClockSeqHiAndReserved & 0x3f) | 0x80;
+ pUuid->Gen.u16TimeHiAndVersion = (pUuid->Gen.u16TimeHiAndVersion & 0x0fff) | 0x4000;
+ pUuid->Gen.u8ClockSeqLow = pThis->iUnit;
+ vboxNetAdpComposeMACAddress(pThis, (PRTMAC)pUuid->Gen.au8Node);
+}
+
+static errno_t vboxNetAdpDarwinOutput(ifnet_t pIface, mbuf_t pMBuf)
+{
+ /*
+ * We are a dummy interface with all the real work done in
+ * VBoxNetFlt bridged networking filter. If anything makes it
+ * this far, it must be a broadcast or a packet for an unknown
+ * guest that intnet didn't know where to dispatch. In that case
+ * we must still do the BPF tap and stats.
+ */
+ bpf_tap_out(pIface, DLT_EN10MB, pMBuf, NULL, 0);
+ ifnet_stat_increment_out(pIface, 1, mbuf_len(pMBuf), 0);
+
+ mbuf_freem_list(pMBuf);
+ return 0;
+}
+
+static void vboxNetAdpDarwinDetach(ifnet_t pIface)
+{
+ PVBOXNETADP pThis = VBOXNETADP_FROM_IFACE(pIface);
+ Assert(pThis);
+ Log2(("vboxNetAdpDarwinDetach: Signaling detach to vboxNetAdpUnregisterDevice.\n"));
+ /* Let vboxNetAdpDarwinUnregisterDevice know that the interface has been detached. */
+ RTSemEventSignal(pThis->u.s.hEvtDetached);
+}
+
+static errno_t vboxNetAdpDarwinDemux(ifnet_t pIface, mbuf_t pMBuf,
+ char *pFrameHeader,
+ protocol_family_t *pProtocolFamily)
+{
+ /*
+ * Anything we get here comes from VBoxNetFlt bridged networking
+ * filter where it has already been accounted for and fed to bpf.
+ */
+ return ether_demux(pIface, pMBuf, pFrameHeader, pProtocolFamily);
+}
+
+
+static errno_t vboxNetAdpDarwinIfIOCtl(ifnet_t pIface, unsigned long uCmd, void *pvData)
+{
+ errno_t error = 0;
+
+ if (pvData == NULL)
+ {
+ /*
+ * Common pattern in the kernel code is to make changes in the
+ * net layer and then notify the device driver by calling its
+ * ioctl function with NULL parameter, e.g.:
+ *
+ * ifnet_set_flags(interface, ...);
+ * ifnet_ioctl(interface, 0, SIOCSIFFLAGS, NULL);
+ *
+ * These are no-ops for us, so tell the caller we succeeded
+ * because some callers do check that return value.
+ */
+ switch (uCmd)
+ {
+ case SIOCSIFFLAGS:
+ Log2(("VBoxNetAdp: %s%d: SIOCSIFFLAGS (null): flags = 0x%04hx\n",
+ ifnet_name(pIface), ifnet_unit(pIface),
+ (uint16_t)ifnet_flags(pIface)));
+ return 0;
+
+ case SIOCADDMULTI:
+ case SIOCDELMULTI:
+ Log2(("VBoxNetAdp: %s%d: SIOC%sMULTI (null)\n",
+ ifnet_name(pIface), ifnet_unit(pIface),
+ uCmd == SIOCADDMULTI ? "ADD" : "DEL"));
+ return 0;
+ }
+ }
+
+ Log2(("VBoxNetAdp: %s%d: %c%c '%c' %u len %u\n",
+ ifnet_name(pIface), ifnet_unit(pIface),
+ uCmd & IOC_OUT ? '<' : '-',
+ uCmd & IOC_IN ? '>' : '-',
+ IOCGROUP(uCmd),
+ uCmd & 0xff,
+ IOCPARM_LEN(uCmd)));
+
+ error = ether_ioctl(pIface, uCmd, pvData);
+ return error;
+}
+
+
+int vboxNetAdpOsCreate(PVBOXNETADP pThis, PCRTMAC pMACAddress)
+{
+ int rc;
+ struct ifnet_init_params Params;
+ RTUUID uuid;
+ struct sockaddr_dl mac;
+
+ pThis->u.s.hEvtDetached = NIL_RTSEMEVENT;
+ rc = RTSemEventCreate(&pThis->u.s.hEvtDetached);
+ if (RT_FAILURE(rc))
+ {
+ printf("vboxNetAdpOsCreate: failed to create semaphore (rc=%d).\n", rc);
+ return rc;
+ }
+
+ mac.sdl_len = sizeof(mac);
+ mac.sdl_family = AF_LINK;
+ mac.sdl_alen = ETHER_ADDR_LEN;
+ mac.sdl_nlen = 0;
+ mac.sdl_slen = 0;
+ memcpy(LLADDR(&mac), pMACAddress->au8, mac.sdl_alen);
+
+ RTStrPrintf(pThis->szName, VBOXNETADP_MAX_NAME_LEN, "%s%d", VBOXNETADP_NAME, pThis->iUnit);
+ vboxNetAdpDarwinComposeUUID(pThis, &uuid);
+ Params.uniqueid = uuid.au8;
+ Params.uniqueid_len = sizeof(uuid);
+ Params.name = VBOXNETADP_NAME;
+ Params.unit = pThis->iUnit;
+ Params.family = IFNET_FAMILY_ETHERNET;
+ Params.type = IFT_ETHER;
+ Params.output = vboxNetAdpDarwinOutput;
+ Params.demux = vboxNetAdpDarwinDemux;
+ Params.add_proto = ether_add_proto;
+ Params.del_proto = ether_del_proto;
+ Params.check_multi = ether_check_multi;
+ Params.framer = ether_frameout;
+ Params.softc = pThis;
+ Params.ioctl = vboxNetAdpDarwinIfIOCtl;
+ Params.set_bpf_tap = NULL;
+ Params.detach = vboxNetAdpDarwinDetach;
+ Params.event = NULL;
+ Params.broadcast_addr = "\xFF\xFF\xFF\xFF\xFF\xFF";
+ Params.broadcast_len = ETHER_ADDR_LEN;
+
+ errno_t err = ifnet_allocate(&Params, &pThis->u.s.pIface);
+ if (!err)
+ {
+ err = ifnet_attach(pThis->u.s.pIface, &mac);
+ if (!err)
+ {
+ bpfattach(pThis->u.s.pIface, DLT_EN10MB, ETHER_HDR_LEN);
+
+ err = ifnet_set_flags(pThis->u.s.pIface, IFF_RUNNING | IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST, 0xFFFF);
+ if (!err)
+ {
+ ifnet_set_mtu(pThis->u.s.pIface, VBOXNETADP_MTU);
+ VBoxNetSendDummy(pThis->u.s.pIface);
+ return VINF_SUCCESS;
+ }
+ else
+ Log(("vboxNetAdpDarwinRegisterDevice: Failed to set flags (err=%d).\n", err));
+ ifnet_detach(pThis->u.s.pIface);
+ }
+ else
+ Log(("vboxNetAdpDarwinRegisterDevice: Failed to attach to interface (err=%d).\n", err));
+ ifnet_release(pThis->u.s.pIface);
+ }
+ else
+ Log(("vboxNetAdpDarwinRegisterDevice: Failed to allocate interface (err=%d).\n", err));
+
+ RTSemEventDestroy(pThis->u.s.hEvtDetached);
+ pThis->u.s.hEvtDetached = NIL_RTSEMEVENT;
+
+ return RTErrConvertFromErrno(err);
+}
+
+void vboxNetAdpOsDestroy(PVBOXNETADP pThis)
+{
+ /* Bring down the interface */
+ int rc = VINF_SUCCESS;
+ errno_t err;
+
+ AssertPtr(pThis->u.s.pIface);
+ Assert(pThis->u.s.hEvtDetached != NIL_RTSEMEVENT);
+
+ err = ifnet_set_flags(pThis->u.s.pIface, 0, IFF_UP | IFF_RUNNING);
+ if (err)
+ Log(("vboxNetAdpDarwinUnregisterDevice: Failed to bring down interface "
+ "(err=%d).\n", err));
+ err = ifnet_detach(pThis->u.s.pIface);
+ if (err)
+ Log(("vboxNetAdpDarwinUnregisterDevice: Failed to detach interface "
+ "(err=%d).\n", err));
+ Log2(("vboxNetAdpDarwinUnregisterDevice: Waiting for 'detached' event...\n"));
+ /* Wait until we get a signal from detach callback. */
+ rc = RTSemEventWait(pThis->u.s.hEvtDetached, VBOXNETADP_DETACH_TIMEOUT);
+ if (rc == VERR_TIMEOUT)
+ LogRel(("VBoxAdpDrv: Failed to detach interface %s%d\n.",
+ VBOXNETADP_NAME, pThis->iUnit));
+ err = ifnet_release(pThis->u.s.pIface);
+ if (err)
+ Log(("vboxNetAdpUnregisterDevice: Failed to release interface (err=%d).\n", err));
+
+ RTSemEventDestroy(pThis->u.s.hEvtDetached);
+ pThis->u.s.hEvtDetached = NIL_RTSEMEVENT;
+}
+
+/**
+ * Device open. Called on open /dev/vboxnetctl
+ *
+ * @param Dev The device number.
+ * @param fFlags ???.
+ * @param fDevType ???.
+ * @param pProcess The process issuing this request.
+ */
+static int VBoxNetAdpDarwinOpen(dev_t Dev, int fFlags, int fDevType, struct proc *pProcess)
+{
+ RT_NOREF(Dev, fFlags, fDevType, pProcess);
+#ifdef LOG_ENABLED
+ char szName[128];
+ szName[0] = '\0';
+ proc_name(proc_pid(pProcess), szName, sizeof(szName));
+ Log(("VBoxNetAdpDarwinOpen: pid=%d '%s'\n", proc_pid(pProcess), szName));
+#endif
+ return 0;
+}
+
+/**
+ * Close device.
+ */
+static int VBoxNetAdpDarwinClose(dev_t Dev, int fFlags, int fDevType, struct proc *pProcess)
+{
+ RT_NOREF(Dev, fFlags, fDevType, pProcess);
+ Log(("VBoxNetAdpDarwinClose: pid=%d\n", proc_pid(pProcess)));
+ return 0;
+}
+
+/**
+ * Device I/O Control entry point.
+ *
+ * @returns Darwin for slow IOCtls and VBox status code for the fast ones.
+ * @param Dev The device number (major+minor).
+ * @param iCmd The IOCtl command.
+ * @param pData Pointer to the data (if any it's a SUPDRVIOCTLDATA (kernel copy)).
+ * @param fFlags Flag saying we're a character device (like we didn't know already).
+ * @param pProcess The process issuing this request.
+ */
+static int VBoxNetAdpDarwinIOCtl(dev_t Dev, u_long iCmd, caddr_t pData, int fFlags, struct proc *pProcess)
+{
+ RT_NOREF(Dev, fFlags, pProcess);
+ uint32_t cbReq = IOCPARM_LEN(iCmd);
+ PVBOXNETADPREQ pReq = (PVBOXNETADPREQ)pData;
+ int rc;
+
+ Log(("VBoxNetAdpDarwinIOCtl: param len %#x; iCmd=%#lx\n", cbReq, iCmd));
+ switch (IOCBASECMD(iCmd))
+ {
+ case IOCBASECMD(VBOXNETADP_CTL_ADD):
+ {
+ if ( (IOC_DIRMASK & iCmd) != IOC_INOUT
+ || cbReq < sizeof(VBOXNETADPREQ))
+ return EINVAL;
+
+ PVBOXNETADP pNew;
+ Log(("VBoxNetAdpDarwinIOCtl: szName=%s\n", pReq->szName));
+ rc = vboxNetAdpCreate(&pNew,
+ pReq->szName[0] && RTStrEnd(pReq->szName, RT_MIN(cbReq, sizeof(pReq->szName))) ?
+ pReq->szName : NULL);
+ if (RT_FAILURE(rc))
+ return rc == VERR_OUT_OF_RESOURCES ? ENOMEM : EINVAL;
+
+ Assert(strlen(pReq->szName) < sizeof(pReq->szName));
+ strncpy(pReq->szName, pNew->szName, sizeof(pReq->szName) - 1);
+ pReq->szName[sizeof(pReq->szName) - 1] = '\0';
+ Log(("VBoxNetAdpDarwinIOCtl: Added '%s'\n", pReq->szName));
+ break;
+ }
+
+ case IOCBASECMD(VBOXNETADP_CTL_REMOVE):
+ {
+ if (!RTStrEnd(pReq->szName, RT_MIN(cbReq, sizeof(pReq->szName))))
+ return EINVAL;
+
+ PVBOXNETADP pAdp = vboxNetAdpFindByName(pReq->szName);
+ if (!pAdp)
+ return EINVAL;
+
+ rc = vboxNetAdpDestroy(pAdp);
+ if (RT_FAILURE(rc))
+ return EINVAL;
+ Log(("VBoxNetAdpDarwinIOCtl: Removed %s\n", pReq->szName));
+ break;
+ }
+
+ default:
+ printf("VBoxNetAdpDarwinIOCtl: unknown command %lx.\n", IOCBASECMD(iCmd));
+ return EINVAL;
+ }
+
+ return 0;
+}
+
+int vboxNetAdpOsInit(PVBOXNETADP pThis)
+{
+ /*
+ * Init the darwin specific members.
+ */
+ pThis->u.s.pIface = NULL;
+ pThis->u.s.hEvtDetached = NIL_RTSEMEVENT;
+
+ return VINF_SUCCESS;
+}
+
+/**
+ * Start the kernel module.
+ */
+static kern_return_t VBoxNetAdpDarwinStart(struct kmod_info *pKModInfo, void *pvData)
+{
+ RT_NOREF(pKModInfo, pvData);
+ int rc;
+
+ /*
+ * Initialize IPRT and find our module tag id.
+ * (IPRT is shared with VBoxDrv, it creates the loggers.)
+ */
+ rc = RTR0Init(0);
+ if (RT_SUCCESS(rc))
+ {
+ Log(("VBoxNetAdpDarwinStart\n"));
+ rc = vboxNetAdpInit();
+ if (RT_SUCCESS(rc))
+ {
+ g_nCtlDev = cdevsw_add(-1, &g_ChDev);
+ if (g_nCtlDev < 0)
+ {
+ LogRel(("VBoxAdp: failed to register control device."));
+ rc = VERR_CANT_CREATE;
+ }
+ else
+ {
+ g_hCtlDev = devfs_make_node(makedev(g_nCtlDev, 0), DEVFS_CHAR,
+ UID_ROOT, GID_WHEEL, 0600, VBOXNETADP_CTL_DEV_NAME);
+ if (!g_hCtlDev)
+ {
+ LogRel(("VBoxAdp: failed to create FS node for control device."));
+ rc = VERR_CANT_CREATE;
+ }
+ }
+ }
+
+ if (RT_SUCCESS(rc))
+ {
+ LogRel(("VBoxAdpDrv: version " VBOX_VERSION_STRING " r%d\n", VBOX_SVN_REV));
+ return KMOD_RETURN_SUCCESS;
+ }
+
+ LogRel(("VBoxAdpDrv: failed to initialize device extension (rc=%d)\n", rc));
+ RTR0Term();
+ }
+ else
+ printf("VBoxAdpDrv: failed to initialize IPRT (rc=%d)\n", rc);
+
+ return KMOD_RETURN_FAILURE;
+}
+
+
+/**
+ * Stop the kernel module.
+ */
+static kern_return_t VBoxNetAdpDarwinStop(struct kmod_info *pKModInfo, void *pvData)
+{
+ RT_NOREF(pKModInfo, pvData);
+ Log(("VBoxNetAdpDarwinStop\n"));
+
+ vboxNetAdpShutdown();
+ /* Remove control device */
+ devfs_remove(g_hCtlDev);
+ cdevsw_remove(g_nCtlDev, &g_ChDev);
+
+ RTR0Term();
+
+ return KMOD_RETURN_SUCCESS;
+}
diff --git a/src/VBox/HostDrivers/VBoxNetAdp/darwin/loadnetadp.sh b/src/VBox/HostDrivers/VBoxNetAdp/darwin/loadnetadp.sh
new file mode 100755
index 00000000..b1e28d94
--- /dev/null
+++ b/src/VBox/HostDrivers/VBoxNetAdp/darwin/loadnetadp.sh
@@ -0,0 +1,133 @@
+#!/bin/bash
+## @file
+# For development.
+#
+
+#
+# Copyright (C) 2006-2023 Oracle and/or its affiliates.
+#
+# This file is part of VirtualBox base platform packages, as
+# available from https://www.virtualbox.org.
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation, in version 3 of the
+# License.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, see <https://www.gnu.org/licenses>.
+#
+# The contents of this file may alternatively be used under the terms
+# of the Common Development and Distribution License Version 1.0
+# (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+# in the VirtualBox distribution, in which case the provisions of the
+# CDDL are applicable instead of those of the GPL.
+#
+# You may elect to license modified versions of this file under the
+# terms and conditions of either the GPL or the CDDL or both.
+#
+# SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+#
+
+SCRIPT_NAME="loadnetadp"
+XNU_VERSION=`LC_ALL=C uname -r | LC_ALL=C cut -d . -f 1`
+
+DRVNAME="VBoxNetAdp.kext"
+BUNDLE="org.virtualbox.kext.VBoxNetAdp"
+
+DEP_DRVNAME="VBoxDrv.kext"
+DEP_BUNDLE="org.virtualbox.kext.VBoxDrv"
+
+
+DIR=`dirname "$0"`
+DIR=`cd "$DIR" && pwd`
+DEP_DIR="$DIR/$DEP_DRVNAME"
+DIR="$DIR/$DRVNAME"
+if [ ! -d "$DIR" ]; then
+ echo "Cannot find $DIR or it's not a directory..."
+ exit 1;
+fi
+if [ ! -d "$DEP_DIR" ]; then
+ echo "Cannot find $DEP_DIR or it's not a directory... (dependency)"
+ exit 1;
+fi
+if [ -n "$*" ]; then
+ OPTS="$*"
+else
+ OPTS="-t"
+fi
+
+trap "sudo chown -R `whoami` $DIR $DEP_DIR; exit 1" INT
+
+# Try unload any existing instance first.
+LOADED=`kextstat -b $BUNDLE -l`
+if test -n "$LOADED"; then
+ echo "${SCRIPT_NAME}.sh: Unloading $BUNDLE..."
+ sudo kextunload -v 6 -b $BUNDLE
+ LOADED=`kextstat -b $BUNDLE -l`
+ if test -n "$LOADED"; then
+ echo "${SCRIPT_NAME}.sh: failed to unload $BUNDLE, see above..."
+ exit 1;
+ fi
+ echo "${SCRIPT_NAME}.sh: Successfully unloaded $BUNDLE"
+fi
+
+set -e
+
+# Copy the .kext to the symbols directory and tweak the kextload options.
+if test -n "$VBOX_DARWIN_SYMS"; then
+ echo "${SCRIPT_NAME}.sh: copying the extension the symbol area..."
+ rm -Rf "$VBOX_DARWIN_SYMS/$DRVNAME"
+ mkdir -p "$VBOX_DARWIN_SYMS"
+ cp -R "$DIR" "$VBOX_DARWIN_SYMS/"
+ OPTS="$OPTS -s $VBOX_DARWIN_SYMS/ "
+ sync
+fi
+
+# On smbfs, this might succeed just fine but make no actual changes,
+# so we might have to temporarily copy the driver to a local directory.
+if sudo chown -R root:wheel "$DIR" "$DEP_DIR"; then
+ OWNER=`/usr/bin/stat -f "%u" "$DIR"`
+else
+ OWNER=1000
+fi
+if test "$OWNER" -ne 0; then
+ TMP_DIR=/tmp/${SCRIPT_NAME}.tmp
+ echo "${SCRIPT_NAME}.sh: chown didn't work on $DIR, using temp location $TMP_DIR/$DRVNAME"
+
+ # clean up first (no sudo rm)
+ if test -e "$TMP_DIR"; then
+ sudo chown -R `whoami` "$TMP_DIR"
+ rm -Rf "$TMP_DIR"
+ fi
+
+ # make a copy and switch over DIR
+ mkdir -p "$TMP_DIR/"
+ sudo cp -Rp "$DIR" "$TMP_DIR/"
+ DIR="$TMP_DIR/$DRVNAME"
+
+ # load.sh puts it here.
+ DEP_DIR="/tmp/loaddrv.tmp/$DEP_DRVNAME"
+
+ # retry
+ sudo chown -R root:wheel "$DIR" "$DEP_DIR"
+fi
+
+sudo chmod -R o-rwx "$DIR"
+sync
+if [ "$XNU_VERSION" -ge "10" ]; then
+ echo "${SCRIPT_NAME}.sh: loading $DIR... (kextutil $OPTS -d \"$DEP_DIR\" \"$DIR\")"
+ sudo kextutil $OPTS -d "$DEP_DIR" "$DIR"
+else
+ echo "${SCRIPT_NAME}.sh: loading $DIR... (kextload $OPTS -d \"$DEP_DIR\" \"$DIR\")"
+sudo kextload $OPTS -d "$DEP_DIR" "$DIR"
+fi
+sync
+sudo chown -R `whoami` "$DIR" "$DEP_DIR"
+kextstat | grep org.virtualbox.kext
+
diff --git a/src/VBox/HostDrivers/VBoxNetAdp/freebsd/Makefile b/src/VBox/HostDrivers/VBoxNetAdp/freebsd/Makefile
new file mode 100644
index 00000000..013a223d
--- /dev/null
+++ b/src/VBox/HostDrivers/VBoxNetAdp/freebsd/Makefile
@@ -0,0 +1,54 @@
+# $Id: Makefile $
+## @file
+# Makefile for the VirtualBox FreeBSD Host Driver.
+#
+
+#
+# Copyright (C) 2006-2023 Oracle and/or its affiliates.
+#
+# This file is part of VirtualBox base platform packages, as
+# available from https://www.virtualbox.org.
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation, in version 3 of the
+# License.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, see <https://www.gnu.org/licenses>.
+#
+# The contents of this file may alternatively be used under the terms
+# of the Common Development and Distribution License Version 1.0
+# (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+# in the VirtualBox distribution, in which case the provisions of the
+# CDDL are applicable instead of those of the GPL.
+#
+# You may elect to license modified versions of this file under the
+# terms and conditions of either the GPL or the CDDL or both.
+#
+# SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+#
+
+KMOD = vboxnetadp
+
+CFLAGS += -DRT_OS_FREEBSD -DIN_RING0 -DIN_RT_R0 -DIN_SUP_R0 -DVBOX -DRT_WITH_VBOX -Iinclude -I. -Ir0drv -w -DVBOX_WITH_HARDENING -DVIMAGE
+
+.if (${MACHINE_ARCH} == "i386")
+ CFLAGS += -DRT_ARCH_X86
+.elif (${MACHINE_ARCH} == "amd64")
+ CFLAGS += -DRT_ARCH_AMD64
+.endif
+
+SRCS = \
+ VBoxNetAdp-freebsd.c \
+ VBoxNetAdp.c
+
+SRCS += device_if.h bus_if.h
+
+.include <bsd.kmod.mk>
+
diff --git a/src/VBox/HostDrivers/VBoxNetAdp/freebsd/VBoxNetAdp-freebsd.c b/src/VBox/HostDrivers/VBoxNetAdp/freebsd/VBoxNetAdp-freebsd.c
new file mode 100644
index 00000000..db5c0b64
--- /dev/null
+++ b/src/VBox/HostDrivers/VBoxNetAdp/freebsd/VBoxNetAdp-freebsd.c
@@ -0,0 +1,339 @@
+/* $Id: VBoxNetAdp-freebsd.c $ */
+/** @file
+ * VBoxNetAdp - Virtual Network Adapter Driver (Host), FreeBSD Specific Code.
+ */
+
+/*-
+ * Copyright (c) 2009 Fredrik Lindberg <fli@shapeshifter.se>
+ *
+ * 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>
+#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/socket.h>
+#include <sys/sockio.h>
+
+#include <net/if.h>
+#include <net/if_var.h>
+#include <net/route.h>
+#include <net/if_dl.h>
+#include <net/if_media.h>
+#include <net/if_types.h>
+#include <net/ethernet.h>
+#include <net/bpf.h>
+
+#define LOG_GROUP LOG_GROUP_NET_ADP_DRV
+#include <VBox/version.h>
+#include <iprt/errcore.h>
+#include <VBox/log.h>
+#include <iprt/initterm.h>
+#include <iprt/string.h>
+#include <iprt/spinlock.h>
+#include <iprt/process.h>
+#include <iprt/assert.h>
+#include <iprt/uuid.h>
+#include <iprt/alloc.h>
+#include <iprt/errcore.h>
+
+#define VBOXNETADP_OS_SPECFIC 1
+#include "../VBoxNetAdpInternal.h"
+
+#if defined(__FreeBSD_version) && __FreeBSD_version >= 800500
+# include <sys/jail.h>
+# include <net/vnet.h>
+
+# define VBOXCURVNET_SET(arg) CURVNET_SET_QUIET(arg)
+# define VBOXCURVNET_SET_FROM_UCRED() VBOXCURVNET_SET(CRED_TO_VNET(curthread->td_ucred))
+# define VBOXCURVNET_RESTORE() CURVNET_RESTORE()
+
+#else /* !defined(__FreeBSD_version) || __FreeBSD_version < 800500 */
+
+# define VBOXCURVNET_SET(arg)
+# define VBOXCURVNET_SET_FROM_UCRED()
+# define VBOXCURVNET_RESTORE()
+
+#endif /* !defined(__FreeBSD_version) || __FreeBSD_version < 800500 */
+
+static int VBoxNetAdpFreeBSDCtrlioctl(struct cdev *, u_long, caddr_t, int flags,
+ struct thread *);
+static struct cdevsw vboxnetadp_cdevsw =
+{
+ .d_version = D_VERSION,
+ .d_ioctl = VBoxNetAdpFreeBSDCtrlioctl,
+ .d_read = (d_read_t *)nullop,
+ .d_write = (d_write_t *)nullop,
+ .d_name = VBOXNETADP_CTL_DEV_NAME,
+};
+
+static struct cdev *VBoxNetAdpFreeBSDcdev;
+
+static int VBoxNetAdpFreeBSDModuleEvent(struct module *, int, void *);
+static moduledata_t g_VBoxNetAdpFreeBSDModule = {
+ "vboxnetadp",
+ VBoxNetAdpFreeBSDModuleEvent,
+ NULL
+};
+
+/** Declare the module as a pseudo device. */
+DECLARE_MODULE(vboxnetadp, g_VBoxNetAdpFreeBSDModule, SI_SUB_PSEUDO, SI_ORDER_ANY);
+MODULE_VERSION(vboxnetadp, 1);
+MODULE_DEPEND(vboxnetadp, vboxdrv, 1, 1, 1);
+MODULE_DEPEND(vboxnetadp, vboxnetflt, 1, 1, 1);
+
+/**
+ * Module event handler
+ */
+static int
+VBoxNetAdpFreeBSDModuleEvent(struct module *pMod, int enmEventType, void *pvArg)
+{
+ int rc = 0;
+
+ Log(("VBoxNetAdpFreeBSDModuleEvent\n"));
+
+ switch (enmEventType)
+ {
+ case MOD_LOAD:
+ rc = RTR0Init(0);
+ if (RT_FAILURE(rc))
+ {
+ Log(("RTR0Init failed %d\n", rc));
+ return RTErrConvertToErrno(rc);
+ }
+ rc = vboxNetAdpInit();
+ if (RT_FAILURE(rc))
+ {
+ RTR0Term();
+ Log(("vboxNetAdpInit failed %d\n", rc));
+ return RTErrConvertToErrno(rc);
+ }
+ /* Create dev node */
+ VBoxNetAdpFreeBSDcdev = make_dev(&vboxnetadp_cdevsw, 0,
+ UID_ROOT, GID_WHEEL, 0600, VBOXNETADP_CTL_DEV_NAME);
+
+ break;
+
+ case MOD_UNLOAD:
+ vboxNetAdpShutdown();
+ destroy_dev(VBoxNetAdpFreeBSDcdev);
+ RTR0Term();
+ break;
+ case MOD_SHUTDOWN:
+ case MOD_QUIESCE:
+ default:
+ return EOPNOTSUPP;
+ }
+
+ if (RT_SUCCESS(rc))
+ return 0;
+ return RTErrConvertToErrno(rc);
+}
+
+/**
+ * Device I/O Control entry point.
+ */
+static int
+VBoxNetAdpFreeBSDCtrlioctl(struct cdev *dev, u_long iCmd, caddr_t data, int flags, struct thread *td)
+{
+ PVBOXNETADP pAdp;
+ PVBOXNETADPREQ pReq = (PVBOXNETADPREQ)data;
+ struct ifnet *ifp;
+ int rc;
+
+ switch (iCmd)
+ {
+ case VBOXNETADP_CTL_ADD:
+ if ( !(iCmd & IOC_INOUT) /* paranoia*/
+ || IOCPARM_LEN(iCmd) < sizeof(*pReq))
+ return EINVAL;
+
+ rc = vboxNetAdpCreate(&pAdp,
+ pReq->szName[0] && RTStrEnd(pReq->szName, RT_MIN(IOCPARM_LEN(iCmd), sizeof(pReq->szName))) ?
+ pReq->szName : NULL);
+ if (RT_FAILURE(rc))
+ return EINVAL;
+
+ strncpy(pReq->szName, pAdp->szName, sizeof(pReq->szName) - 1);
+ pReq->szName[sizeof(pReq->szName) - 1] = '\0';
+ break;
+
+ case VBOXNETADP_CTL_REMOVE:
+ if (!RTStrEnd(pReq->szName, RT_MIN(sizeof(pReq->szName), IOCPARM_LEN(iCmd))))
+ return EINVAL;
+
+ pAdp = vboxNetAdpFindByName(pReq->szName);
+ if (!pAdp)
+ return EINVAL;
+
+ rc = vboxNetAdpDestroy(pAdp);
+ if (RT_FAILURE(rc))
+ return EINVAL;
+
+ break;
+
+ default:
+ return EINVAL;
+ }
+ return 0;
+}
+
+/**
+ * Initialize device, just set the running flag.
+ */
+static void VBoxNetAdpFreeBSDNetinit(void *priv)
+{
+ PVBOXNETADP pThis = priv;
+ struct ifnet *ifp = pThis->u.s.ifp;
+
+ ifp->if_drv_flags |= IFF_DRV_RUNNING;
+}
+
+/**
+ * Transmit packets.
+ * netflt has already done everything for us so we just hand the
+ * packets to BPF and increment the packet stats.
+ */
+static void VBoxNetAdpFreeBSDNetstart(struct ifnet *ifp)
+{
+ PVBOXNETADP pThis = ifp->if_softc;
+ struct mbuf *m;
+
+ if ((ifp->if_drv_flags & (IFF_DRV_RUNNING | IFF_DRV_OACTIVE)) != IFF_DRV_RUNNING)
+ return;
+
+ ifp->if_drv_flags |= IFF_DRV_OACTIVE;
+ while (!IFQ_DRV_IS_EMPTY(&ifp->if_snd))
+ {
+#if __FreeBSD_version >= 1100036
+ if_inc_counter(ifp, IFCOUNTER_OPACKETS, 1);
+#else
+ ifp->if_opackets++;
+#endif
+ IFQ_DRV_DEQUEUE(&ifp->if_snd, m);
+ BPF_MTAP(ifp, m);
+ m_freem(m);
+ }
+ ifp->if_drv_flags &= ~IFF_DRV_OACTIVE;
+}
+
+/**
+ * Interface ioctl handling
+ */
+static int VBoxNetAdpFreeBSDNetioctl(struct ifnet *ifp, u_long cmd, caddr_t data)
+{
+ int error = 0;
+
+ switch (cmd)
+ {
+ case SIOCSIFFLAGS:
+ if (ifp->if_flags & IFF_UP)
+ {
+ if (!(ifp->if_drv_flags & IFF_DRV_RUNNING))
+ ifp->if_init(ifp->if_softc);
+ }
+ else
+ {
+ if (ifp->if_drv_flags & IFF_DRV_RUNNING)
+ ifp->if_drv_flags &= ~IFF_DRV_RUNNING;
+ }
+ break;
+ case SIOCGIFMEDIA:
+ {
+ struct ifmediareq *ifmr;
+ int count;
+
+ ifmr = (struct ifmediareq *)data;
+ count = ifmr->ifm_count;
+ ifmr->ifm_count = 1;
+ ifmr->ifm_status = IFM_AVALID;
+ ifmr->ifm_active = IFM_ETHER;
+ ifmr->ifm_status |= IFM_ACTIVE;
+ ifmr->ifm_current = ifmr->ifm_active;
+ if (count >= 1)
+ {
+ int media = IFM_ETHER;
+ error = copyout(&media, ifmr->ifm_ulist, sizeof(int));
+ }
+ break;
+ }
+ default:
+ return ether_ioctl(ifp, cmd, data);
+ }
+ return error;
+}
+
+int vboxNetAdpOsInit(PVBOXNETADP pThis)
+{
+ pThis->u.s.ifp = NULL;
+ return VINF_SUCCESS;;
+}
+
+int vboxNetAdpOsCreate(PVBOXNETADP pThis, PCRTMAC pMac)
+{
+ struct ifnet *ifp;
+
+ VBOXCURVNET_SET_FROM_UCRED();
+ ifp = if_alloc(IFT_ETHER);
+ if (ifp == NULL)
+ return VERR_NO_MEMORY;
+
+ if_initname(ifp, VBOXNETADP_NAME, pThis->iUnit);
+ ifp->if_softc = pThis;
+ ifp->if_mtu = ETHERMTU;
+ ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST;
+ ifp->if_ioctl = VBoxNetAdpFreeBSDNetioctl;
+ ifp->if_start = VBoxNetAdpFreeBSDNetstart;
+ ifp->if_init = VBoxNetAdpFreeBSDNetinit;
+ IFQ_SET_MAXLEN(&ifp->if_snd, IFQ_MAXLEN);
+ ifp->if_snd.ifq_drv_maxlen = IFQ_MAXLEN;
+ IFQ_SET_READY(&ifp->if_snd);
+ ether_ifattach(ifp, (void *)pMac);
+ ifp->if_baudrate = 0;
+
+ strncpy(pThis->szName, ifp->if_xname, VBOXNETADP_MAX_NAME_LEN);
+ pThis->u.s.ifp = ifp;
+ VBOXCURVNET_RESTORE();
+ return 0;
+}
+
+void vboxNetAdpOsDestroy(PVBOXNETADP pThis)
+{
+ struct ifnet *ifp;
+
+ ifp = pThis->u.s.ifp;
+ VBOXCURVNET_SET(ifp->if_vnet);
+ ether_ifdetach(ifp);
+ if_free(ifp);
+ VBOXCURVNET_RESTORE();
+}
diff --git a/src/VBox/HostDrivers/VBoxNetAdp/freebsd/files_vboxnetadp b/src/VBox/HostDrivers/VBoxNetAdp/freebsd/files_vboxnetadp
new file mode 100755
index 00000000..d90ced7b
--- /dev/null
+++ b/src/VBox/HostDrivers/VBoxNetAdp/freebsd/files_vboxnetadp
@@ -0,0 +1,94 @@
+#!/bin/sh
+# $Id: files_vboxnetadp $
+## @file
+# Shared file between Makefile.kmk and export_modules.sh.
+#
+
+#
+# Copyright (C) 2007-2023 Oracle and/or its affiliates.
+#
+# This file is part of VirtualBox base platform packages, as
+# available from https://www.virtualbox.org.
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation, in version 3 of the
+# License.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, see <https://www.gnu.org/licenses>.
+#
+# The contents of this file may alternatively be used under the terms
+# of the Common Development and Distribution License Version 1.0
+# (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+# in the VirtualBox distribution, in which case the provisions of the
+# CDDL are applicable instead of those of the GPL.
+#
+# You may elect to license modified versions of this file under the
+# terms and conditions of either the GPL or the CDDL or both.
+#
+# SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+#
+
+
+VBOX_VBOXNETADP_SOURCES=" \
+ ${PATH_ROOT}/include/iprt/alloc.h=>include/iprt/alloc.h \
+ ${PATH_ROOT}/include/iprt/alloca.h=>include/iprt/alloca.h \
+ ${PATH_ROOT}/include/iprt/asm.h=>include/iprt/asm.h \
+ ${PATH_ROOT}/include/iprt/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/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/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/uni.h=>include/iprt/uni.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/x86-helpers.h=>include/iprt/x86-helpers.h \
+ ${PATH_ROOT}/include/iprt/nocrt/limits.h=>include/iprt/nocrt/limits.h \
+ ${PATH_ROOT}/include/VBox/cdefs.h=>include/VBox/cdefs.h \
+ ${PATH_ROOT}/include/VBox/err.h=>include/VBox/err.h \
+ ${PATH_ROOT}/include/VBox/log.h=>include/VBox/log.h \
+ ${PATH_ROOT}/include/VBox/intnet.h=>include/VBox/intnet.h \
+ ${PATH_ROOT}/include/VBox/vmm/stam.h=>include/VBox/vmm/stam.h \
+ ${PATH_ROOT}/include/VBox/sup.h=>include/VBox/sup.h \
+ ${PATH_ROOT}/include/VBox/types.h=>include/VBox/types.h \
+ ${PATH_ROOT}/include/VBox/version.h=>include/VBox/version.h \
+ ${PATH_ROOT}/include/VBox/SUPDrvMangling.h=>include/VBox/SUPDrvMangling.h \
+ ${PATH_ROOT}/src/VBox/HostDrivers/VBoxNetAdp/freebsd/VBoxNetAdp-freebsd.c=>VBoxNetAdp-freebsd.c \
+ ${PATH_ROOT}/src/VBox/HostDrivers/VBoxNetAdp/VBoxNetAdp.c=>VBoxNetAdp.c \
+ ${PATH_ROOT}/src/VBox/HostDrivers/VBoxNetAdp/VBoxNetAdpInternal.h=>VBoxNetAdpInternal.h \
+ ${PATH_ROOT}/src/VBox/Runtime/r0drv/freebsd/the-freebsd-kernel.h=>r0drv/freebsd/the-freebsd-kernel.h \
+ ${PATH_OUT}/version-generated.h=>version-generated.h \
+ ${PATH_OUT}/product-generated.h=>product-generated.h \
+"
+
diff --git a/src/VBox/HostDrivers/VBoxNetAdp/linux/Makefile b/src/VBox/HostDrivers/VBoxNetAdp/linux/Makefile
new file mode 100644
index 00000000..30aada92
--- /dev/null
+++ b/src/VBox/HostDrivers/VBoxNetAdp/linux/Makefile
@@ -0,0 +1,78 @@
+# $Id: Makefile $
+## @file
+# Makefile for the VirtualBox Linux Host Virtual Network Adapter Driver.
+#
+
+#
+# Copyright (C) 2006-2023 Oracle and/or its affiliates.
+#
+# This file is part of VirtualBox base platform packages, as
+# available from https://www.virtualbox.org.
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation, in version 3 of the
+# License.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, see <https://www.gnu.org/licenses>.
+#
+# The contents of this file may alternatively be used under the terms
+# of the Common Development and Distribution License Version 1.0
+# (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+# in the VirtualBox distribution, in which case the provisions of the
+# CDDL are applicable instead of those of the GPL.
+#
+# You may elect to license modified versions of this file under the
+# terms and conditions of either the GPL or the CDDL or both.
+#
+# SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+#
+
+# Linux kbuild sets this to our source directory if we are called from there
+obj ?= $(CURDIR)
+include $(obj)/Makefile-header.gmk
+VBOXNETADP_DIR := $(VBOX_MODULE_SRC_DIR)
+
+# Allow building directly from the subdirectory without assuming the toplevel
+# makefile has done the copying. Not the default use case, but can be handy.
+ifndef KBUILD_EXTRA_SYMBOLS
+KBUILD_EXTRA_SYMBOLS=$(abspath $(VBOXNETADP_DIR)/../vboxdrv/Module.symvers)
+endif
+
+VBOXMOD_NAME = vboxnetadp
+VBOXMOD_OBJS = \
+ linux/VBoxNetAdp-linux.o \
+ VBoxNetAdp.o
+ifeq ($(VBOX_KBUILD_TARGET_ARCH),x86)
+VBOXMOD_OBJS += \
+ math/gcc/divdi3.o \
+ math/gcc/moddi3.o \
+ math/gcc/qdivrem.o \
+ math/gcc/udivdi3.o \
+ math/gcc/udivmoddi4.o \
+ math/gcc/divdi3.o \
+ math/gcc/umoddi3.o
+endif
+VBOXMOD_INCL = \
+ $(VBOXNETADP_DIR) \
+ $(VBOXNETADP_DIR)include \
+ $(VBOXNETADP_DIR)r0drv/linux
+VBOXMOD_DEFS = \
+ RT_OS_LINUX \
+ IN_RING0 \
+ IN_RT_R0 \
+ IN_SUP_R0 \
+ VBOX \
+ RT_WITH_VBOX \
+ VBOX_WITH_HARDENING \
+ VBOX_WITH_64_BITS_GUESTS # <-- must be consistent with Config.kmk!
+VBOXMOD_CFLAGS = -include $(VBOXNETADP_DIR)include/VBox/SUPDrvMangling.h -fno-pie -Wno-declaration-after-statement
+
+include $(obj)/Makefile-footer.gmk
+
diff --git a/src/VBox/HostDrivers/VBoxNetAdp/linux/Makefile.kup b/src/VBox/HostDrivers/VBoxNetAdp/linux/Makefile.kup
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/src/VBox/HostDrivers/VBoxNetAdp/linux/Makefile.kup
diff --git a/src/VBox/HostDrivers/VBoxNetAdp/linux/VBoxNetAdp-linux.c b/src/VBox/HostDrivers/VBoxNetAdp/linux/VBoxNetAdp-linux.c
new file mode 100644
index 00000000..0654e1d7
--- /dev/null
+++ b/src/VBox/HostDrivers/VBoxNetAdp/linux/VBoxNetAdp-linux.c
@@ -0,0 +1,583 @@
+/* $Id: VBoxNetAdp-linux.c $ */
+/** @file
+ * VBoxNetAdp - Virtual Network Adapter Driver (Host), Linux Specific Code.
+ */
+
+/*
+ * Copyright (C) 2009-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include "the-linux-kernel.h"
+#include "version-generated.h"
+#include "revision-generated.h"
+#include "product-generated.h"
+#include <linux/ethtool.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/miscdevice.h>
+
+#define LOG_GROUP LOG_GROUP_NET_ADP_DRV
+#include <VBox/log.h>
+#include <iprt/errcore.h>
+#include <iprt/process.h>
+#include <iprt/initterm.h>
+#include <iprt/mem.h>
+#include <iprt/string.h>
+
+/*
+#include <iprt/assert.h>
+#include <iprt/semaphore.h>
+#include <iprt/spinlock.h>
+#include <iprt/string.h>
+#include <iprt/uuid.h>
+#include <iprt/alloca.h>
+*/
+
+#define VBOXNETADP_OS_SPECFIC 1
+#include "../VBoxNetAdpInternal.h"
+
+
+/*********************************************************************************************************************************
+* Defined Constants And Macros *
+*********************************************************************************************************************************/
+#define VBOXNETADP_LINUX_NAME "vboxnet%d"
+#define VBOXNETADP_CTL_DEV_NAME "vboxnetctl"
+
+#define VBOXNETADP_FROM_IFACE(iface) ((PVBOXNETADP) ifnet_softc(iface))
+
+/** Set netdev MAC address. */
+#if RTLNX_VER_MIN(5,17,0)
+# define VBOX_DEV_ADDR_SET(dev, addr, len) dev_addr_mod(dev, 0, addr, len)
+#else /* < 5.17.0 */
+# define VBOX_DEV_ADDR_SET(dev, addr, len) memcpy(dev->dev_addr, addr, len)
+#endif
+
+
+/*********************************************************************************************************************************
+* Internal Functions *
+*********************************************************************************************************************************/
+static int __init VBoxNetAdpLinuxInit(void);
+static void __exit VBoxNetAdpLinuxUnload(void);
+
+static int VBoxNetAdpLinuxOpen(struct inode *pInode, struct file *pFilp);
+static int VBoxNetAdpLinuxClose(struct inode *pInode, struct file *pFilp);
+#if RTLNX_VER_MAX(2,6,36)
+static int VBoxNetAdpLinuxIOCtl(struct inode *pInode, struct file *pFilp,
+ unsigned int uCmd, unsigned long ulArg);
+#else /* >= 2,6,36 */
+static long VBoxNetAdpLinuxIOCtlUnlocked(struct file *pFilp,
+ unsigned int uCmd, unsigned long ulArg);
+#endif /* >= 2,6,36 */
+
+static void vboxNetAdpEthGetDrvinfo(struct net_device *dev, struct ethtool_drvinfo *info);
+#if RTLNX_VER_MIN(4,20,0)
+static int vboxNetAdpEthGetLinkSettings(struct net_device *pNetDev, struct ethtool_link_ksettings *pLinkSettings);
+#else /* < 4,20,0 */
+static int vboxNetAdpEthGetSettings(struct net_device *dev, struct ethtool_cmd *cmd);
+#endif /* < 4,20,0 */
+
+
+/*********************************************************************************************************************************
+* Global Variables *
+*********************************************************************************************************************************/
+module_init(VBoxNetAdpLinuxInit);
+module_exit(VBoxNetAdpLinuxUnload);
+
+MODULE_AUTHOR(VBOX_VENDOR);
+MODULE_DESCRIPTION(VBOX_PRODUCT " Network Adapter Driver");
+MODULE_LICENSE("GPL");
+#ifdef MODULE_VERSION
+MODULE_VERSION(VBOX_VERSION_STRING " r" RT_XSTR(VBOX_SVN_REV) " (" RT_XSTR(INTNETTRUNKIFPORT_VERSION) ")");
+#endif
+
+/**
+ * The (common) global data.
+ */
+static struct file_operations gFileOpsVBoxNetAdp =
+{
+ owner: THIS_MODULE,
+ open: VBoxNetAdpLinuxOpen,
+ release: VBoxNetAdpLinuxClose,
+#if RTLNX_VER_MAX(2,6,36)
+ ioctl: VBoxNetAdpLinuxIOCtl,
+#else /* RTLNX_VER_MIN(2,6,36) */
+ unlocked_ioctl: VBoxNetAdpLinuxIOCtlUnlocked,
+#endif /* RTLNX_VER_MIN(2,6,36) */
+};
+
+/** The miscdevice structure. */
+static struct miscdevice g_CtlDev =
+{
+ minor: MISC_DYNAMIC_MINOR,
+ name: VBOXNETADP_CTL_DEV_NAME,
+ fops: &gFileOpsVBoxNetAdp,
+# if RTLNX_VER_MAX(2,6,18)
+ devfs_name: VBOXNETADP_CTL_DEV_NAME
+# endif
+};
+
+# if RTLNX_VER_MIN(2,6,19)
+static const struct ethtool_ops gEthToolOpsVBoxNetAdp =
+# else
+static struct ethtool_ops gEthToolOpsVBoxNetAdp =
+# endif
+{
+ .get_drvinfo = vboxNetAdpEthGetDrvinfo,
+# if RTLNX_VER_MIN(4,20,0)
+ .get_link_ksettings = vboxNetAdpEthGetLinkSettings,
+# else
+ .get_settings = vboxNetAdpEthGetSettings,
+# endif
+ .get_link = ethtool_op_get_link,
+};
+
+
+struct VBoxNetAdpPriv
+{
+ struct net_device_stats Stats;
+};
+
+typedef struct VBoxNetAdpPriv VBOXNETADPPRIV;
+typedef VBOXNETADPPRIV *PVBOXNETADPPRIV;
+
+static int vboxNetAdpLinuxOpen(struct net_device *pNetDev)
+{
+ netif_start_queue(pNetDev);
+ return 0;
+}
+
+static int vboxNetAdpLinuxStop(struct net_device *pNetDev)
+{
+ netif_stop_queue(pNetDev);
+ return 0;
+}
+
+static int vboxNetAdpLinuxXmit(struct sk_buff *pSkb, struct net_device *pNetDev)
+{
+ PVBOXNETADPPRIV pPriv = netdev_priv(pNetDev);
+
+ /* Update the stats. */
+ pPriv->Stats.tx_packets++;
+ pPriv->Stats.tx_bytes += pSkb->len;
+#if RTLNX_VER_MAX(2,6,31)
+ /* Update transmission time stamp. */
+ pNetDev->trans_start = jiffies;
+#endif
+ /* Nothing else to do, just free the sk_buff. */
+ dev_kfree_skb(pSkb);
+ return 0;
+}
+
+static struct net_device_stats *vboxNetAdpLinuxGetStats(struct net_device *pNetDev)
+{
+ PVBOXNETADPPRIV pPriv = netdev_priv(pNetDev);
+ return &pPriv->Stats;
+}
+
+
+/* ethtool_ops::get_drvinfo */
+static void vboxNetAdpEthGetDrvinfo(struct net_device *pNetDev, struct ethtool_drvinfo *info)
+{
+ PVBOXNETADPPRIV pPriv = netdev_priv(pNetDev);
+ NOREF(pPriv);
+
+ RTStrPrintf(info->driver, sizeof(info->driver),
+ "%s", VBOXNETADP_NAME);
+
+ /*
+ * Would be nice to include VBOX_SVN_REV, but it's not available
+ * here. Use file's svn revision via svn keyword?
+ */
+ RTStrPrintf(info->version, sizeof(info->version),
+ "%s", VBOX_VERSION_STRING);
+
+ RTStrPrintf(info->fw_version, sizeof(info->fw_version),
+ "0x%08X", INTNETTRUNKIFPORT_VERSION);
+
+ RTStrPrintf(info->bus_info, sizeof(info->driver),
+ "N/A");
+}
+
+
+# if RTLNX_VER_MIN(4,20,0)
+/* ethtool_ops::get_link_ksettings */
+static int vboxNetAdpEthGetLinkSettings(struct net_device *pNetDev, struct ethtool_link_ksettings *pLinkSettings)
+{
+ /* We just need to set field we care for, the rest is done by ethtool_get_link_ksettings() helper in ethtool. */
+ ethtool_link_ksettings_zero_link_mode(pLinkSettings, supported);
+ ethtool_link_ksettings_zero_link_mode(pLinkSettings, advertising);
+ ethtool_link_ksettings_zero_link_mode(pLinkSettings, lp_advertising);
+ pLinkSettings->base.speed = SPEED_10;
+ pLinkSettings->base.duplex = DUPLEX_FULL;
+ pLinkSettings->base.port = PORT_TP;
+ pLinkSettings->base.phy_address = 0;
+ pLinkSettings->base.transceiver = XCVR_INTERNAL;
+ pLinkSettings->base.autoneg = AUTONEG_DISABLE;
+ return 0;
+}
+#else /* RTLNX_VER_MAX(4,20,0) */
+/* ethtool_ops::get_settings */
+static int vboxNetAdpEthGetSettings(struct net_device *pNetDev, struct ethtool_cmd *cmd)
+{
+ cmd->supported = 0;
+ cmd->advertising = 0;
+#if RTLNX_VER_MIN(2,6,27)
+ ethtool_cmd_speed_set(cmd, SPEED_10);
+#else
+ cmd->speed = SPEED_10;
+#endif
+ cmd->duplex = DUPLEX_FULL;
+ cmd->port = PORT_TP;
+ cmd->phy_address = 0;
+ cmd->transceiver = XCVR_INTERNAL;
+ cmd->autoneg = AUTONEG_DISABLE;
+ cmd->maxtxpkt = 0;
+ cmd->maxrxpkt = 0;
+ return 0;
+}
+#endif /* RTLNX_VER_MAX(4,20,0) */
+
+
+#if RTLNX_VER_MIN(2,6,29)
+static const struct net_device_ops vboxNetAdpNetdevOps = {
+ .ndo_open = vboxNetAdpLinuxOpen,
+ .ndo_stop = vboxNetAdpLinuxStop,
+ .ndo_start_xmit = vboxNetAdpLinuxXmit,
+ .ndo_get_stats = vboxNetAdpLinuxGetStats
+};
+#endif
+
+static void vboxNetAdpNetDevInit(struct net_device *pNetDev)
+{
+ PVBOXNETADPPRIV pPriv;
+
+ ether_setup(pNetDev);
+#if RTLNX_VER_MIN(2,6,29)
+ pNetDev->netdev_ops = &vboxNetAdpNetdevOps;
+#else /* RTLNX_VER_MAX(2,6,29) */
+ pNetDev->open = vboxNetAdpLinuxOpen;
+ pNetDev->stop = vboxNetAdpLinuxStop;
+ pNetDev->hard_start_xmit = vboxNetAdpLinuxXmit;
+ pNetDev->get_stats = vboxNetAdpLinuxGetStats;
+#endif /* RTLNX_VER_MAX(2,6,29) */
+#if RTLNX_VER_MIN(4,10,0)
+ pNetDev->max_mtu = 65536;
+ pNetDev->features = NETIF_F_TSO
+ | NETIF_F_TSO6
+ | NETIF_F_TSO_ECN
+ | NETIF_F_SG
+ | NETIF_F_HW_CSUM;
+#endif /* RTLNX_VER_MIN(4,10,0) */
+
+ pNetDev->ethtool_ops = &gEthToolOpsVBoxNetAdp;
+
+ pPriv = netdev_priv(pNetDev);
+ memset(pPriv, 0, sizeof(*pPriv));
+}
+
+
+int vboxNetAdpOsCreate(PVBOXNETADP pThis, PCRTMAC pMACAddress)
+{
+ int rc = VINF_SUCCESS;
+ struct net_device *pNetDev;
+
+ /* No need for private data. */
+ pNetDev = alloc_netdev(sizeof(VBOXNETADPPRIV),
+ pThis->szName[0] ? pThis->szName : VBOXNETADP_LINUX_NAME,
+#if RTLNX_VER_MIN(3,17,0)
+ NET_NAME_UNKNOWN,
+#endif
+ vboxNetAdpNetDevInit);
+ if (pNetDev)
+ {
+ int err;
+
+ if (pNetDev->dev_addr)
+ {
+ VBOX_DEV_ADDR_SET(pNetDev, pMACAddress, ETH_ALEN);
+ Log2(("vboxNetAdpOsCreate: pNetDev->dev_addr = %.6Rhxd\n", pNetDev->dev_addr));
+
+ /*
+ * We treat presence of VBoxNetFlt filter as our "carrier",
+ * see vboxNetFltSetLinkState().
+ *
+ * operstates.txt: "On device allocation, networking core
+ * sets the flags equivalent to netif_carrier_ok() and
+ * !netif_dormant()" - so turn carrier off here.
+ */
+ netif_carrier_off(pNetDev);
+
+ err = register_netdev(pNetDev);
+ if (!err)
+ {
+ strncpy(pThis->szName, pNetDev->name, sizeof(pThis->szName));
+ pThis->szName[sizeof(pThis->szName) - 1] = '\0';
+ pThis->u.s.pNetDev = pNetDev;
+ Log2(("vboxNetAdpOsCreate: pThis=%p pThis->szName = %p\n", pThis, pThis->szName));
+ return VINF_SUCCESS;
+ }
+ }
+ else
+ {
+ LogRel(("VBoxNetAdp: failed to set MAC address (dev->dev_addr == NULL)\n"));
+ err = EFAULT;
+ }
+ free_netdev(pNetDev);
+ rc = RTErrConvertFromErrno(err);
+ }
+ return rc;
+}
+
+void vboxNetAdpOsDestroy(PVBOXNETADP pThis)
+{
+ struct net_device *pNetDev = pThis->u.s.pNetDev;
+ AssertPtr(pThis->u.s.pNetDev);
+
+ pThis->u.s.pNetDev = NULL;
+ unregister_netdev(pNetDev);
+ free_netdev(pNetDev);
+}
+
+/**
+ * Device open. Called on open /dev/vboxnetctl
+ *
+ * @param pInode Pointer to inode info structure.
+ * @param pFilp Associated file pointer.
+ */
+static int VBoxNetAdpLinuxOpen(struct inode *pInode, struct file *pFilp)
+{
+ Log(("VBoxNetAdpLinuxOpen: pid=%d/%d %s\n", RTProcSelf(), current->pid, current->comm));
+
+#ifdef VBOX_WITH_HARDENING
+ /*
+ * Only root is allowed to access the device, enforce it!
+ */
+ if (!capable(CAP_SYS_ADMIN))
+ {
+ Log(("VBoxNetAdpLinuxOpen: admin privileges required!\n"));
+ return -EPERM;
+ }
+#endif
+
+ return 0;
+}
+
+
+/**
+ * Close device.
+ *
+ * @param pInode Pointer to inode info structure.
+ * @param pFilp Associated file pointer.
+ */
+static int VBoxNetAdpLinuxClose(struct inode *pInode, struct file *pFilp)
+{
+ Log(("VBoxNetAdpLinuxClose: pid=%d/%d %s\n",
+ RTProcSelf(), current->pid, current->comm));
+ 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 RTLNX_VER_MAX(2,6,36)
+static int VBoxNetAdpLinuxIOCtl(struct inode *pInode, struct file *pFilp,
+ unsigned int uCmd, unsigned long ulArg)
+#else /* RTLNX_VER_MIN(2,6,36) */
+static long VBoxNetAdpLinuxIOCtlUnlocked(struct file *pFilp,
+ unsigned int uCmd, unsigned long ulArg)
+#endif /* RTLNX_VER_MIN(2,6,36) */
+{
+ VBOXNETADPREQ Req;
+ PVBOXNETADP pAdp;
+ int rc;
+ char *pszName = NULL;
+
+ Log(("VBoxNetAdpLinuxIOCtl: param len %#x; uCmd=%#x; add=%#x\n", _IOC_SIZE(uCmd), uCmd, VBOXNETADP_CTL_ADD));
+ if (RT_UNLIKELY(_IOC_SIZE(uCmd) != sizeof(Req))) /* paranoia */
+ {
+ Log(("VBoxNetAdpLinuxIOCtl: bad ioctl sizeof(Req)=%#x _IOC_SIZE=%#x; uCmd=%#x.\n", sizeof(Req), _IOC_SIZE(uCmd), uCmd));
+ return -EINVAL;
+ }
+
+ switch (uCmd)
+ {
+ case VBOXNETADP_CTL_ADD:
+ Log(("VBoxNetAdpLinuxIOCtl: _IOC_DIR(uCmd)=%#x; IOC_OUT=%#x\n", _IOC_DIR(uCmd), IOC_OUT));
+ if (RT_UNLIKELY(copy_from_user(&Req, (void *)ulArg, sizeof(Req))))
+ {
+ Log(("VBoxNetAdpLinuxIOCtl: copy_from_user(,%#lx,) failed; uCmd=%#x.\n", ulArg, uCmd));
+ return -EFAULT;
+ }
+ Log(("VBoxNetAdpLinuxIOCtl: Add %s\n", Req.szName));
+
+ if (Req.szName[0])
+ {
+ pAdp = vboxNetAdpFindByName(Req.szName);
+ if (pAdp)
+ {
+ Log(("VBoxNetAdpLinuxIOCtl: '%s' already exists\n", Req.szName));
+ return -EINVAL;
+ }
+ pszName = Req.szName;
+ }
+ rc = vboxNetAdpCreate(&pAdp, pszName);
+ if (RT_FAILURE(rc))
+ {
+ Log(("VBoxNetAdpLinuxIOCtl: vboxNetAdpCreate -> %Rrc\n", rc));
+ return -(rc == VERR_OUT_OF_RESOURCES ? ENOMEM : EINVAL);
+ }
+
+ Assert(strlen(pAdp->szName) < sizeof(Req.szName));
+ strncpy(Req.szName, pAdp->szName, sizeof(Req.szName) - 1);
+ Req.szName[sizeof(Req.szName) - 1] = '\0';
+
+ if (RT_UNLIKELY(copy_to_user((void *)ulArg, &Req, sizeof(Req))))
+ {
+ /* this is really bad! */
+ /** @todo remove the adapter again? */
+ printk(KERN_ERR "VBoxNetAdpLinuxIOCtl: copy_to_user(%#lx,,%#zx); uCmd=%#x!\n", ulArg, sizeof(Req), uCmd);
+ return -EFAULT;
+ }
+ Log(("VBoxNetAdpLinuxIOCtl: Successfully added '%s'\n", Req.szName));
+ break;
+
+ case VBOXNETADP_CTL_REMOVE:
+ if (RT_UNLIKELY(copy_from_user(&Req, (void *)ulArg, sizeof(Req))))
+ {
+ Log(("VBoxNetAdpLinuxIOCtl: copy_from_user(,%#lx,) failed; uCmd=%#x.\n", ulArg, uCmd));
+ return -EFAULT;
+ }
+ Log(("VBoxNetAdpLinuxIOCtl: Remove %s\n", Req.szName));
+
+ pAdp = vboxNetAdpFindByName(Req.szName);
+ if (!pAdp)
+ {
+ Log(("VBoxNetAdpLinuxIOCtl: '%s' not found\n", Req.szName));
+ return -EINVAL;
+ }
+
+ rc = vboxNetAdpDestroy(pAdp);
+ if (RT_FAILURE(rc))
+ {
+ Log(("VBoxNetAdpLinuxIOCtl: vboxNetAdpDestroy('%s') -> %Rrc\n", Req.szName, rc));
+ return -EINVAL;
+ }
+ Log(("VBoxNetAdpLinuxIOCtl: Successfully removed '%s'\n", Req.szName));
+ break;
+
+ default:
+ printk(KERN_ERR "VBoxNetAdpLinuxIOCtl: unknown command %x.\n", uCmd);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+int vboxNetAdpOsInit(PVBOXNETADP pThis)
+{
+ /*
+ * Init linux-specific members.
+ */
+ pThis->u.s.pNetDev = NULL;
+
+ return VINF_SUCCESS;
+}
+
+
+
+/**
+ * Initialize module.
+ *
+ * @returns appropriate status code.
+ */
+static int __init VBoxNetAdpLinuxInit(void)
+{
+ int rc;
+ /*
+ * Initialize IPRT.
+ */
+ rc = RTR0Init(0);
+ if (RT_SUCCESS(rc))
+ {
+ Log(("VBoxNetAdpLinuxInit\n"));
+
+ rc = vboxNetAdpInit();
+ if (RT_SUCCESS(rc))
+ {
+ rc = misc_register(&g_CtlDev);
+ if (rc)
+ {
+ printk(KERN_ERR "VBoxNetAdp: Can't register " VBOXNETADP_CTL_DEV_NAME " device! rc=%d\n", rc);
+ return rc;
+ }
+ LogRel(("VBoxNetAdp: Successfully started.\n"));
+ return 0;
+ }
+ else
+ LogRel(("VBoxNetAdp: failed to register vboxnet0 device (rc=%d)\n", rc));
+ }
+ else
+ LogRel(("VBoxNetAdp: failed to initialize IPRT (rc=%d)\n", rc));
+
+ return -RTErrConvertToErrno(rc);
+}
+
+
+/**
+ * Unload the module.
+ *
+ * @todo We have to prevent this if we're busy!
+ */
+static void __exit VBoxNetAdpLinuxUnload(void)
+{
+ Log(("VBoxNetAdpLinuxUnload\n"));
+
+ /*
+ * Undo the work done during start (in reverse order).
+ */
+
+ vboxNetAdpShutdown();
+ /* Remove control device */
+ misc_deregister(&g_CtlDev);
+
+ RTR0Term();
+
+ Log(("VBoxNetAdpLinuxUnload - done\n"));
+}
+
diff --git a/src/VBox/HostDrivers/VBoxNetAdp/linux/files_vboxnetadp b/src/VBox/HostDrivers/VBoxNetAdp/linux/files_vboxnetadp
new file mode 100755
index 00000000..3f028677
--- /dev/null
+++ b/src/VBox/HostDrivers/VBoxNetAdp/linux/files_vboxnetadp
@@ -0,0 +1,112 @@
+#!/bin/sh
+# $Id: files_vboxnetadp $
+## @files
+# Shared file between Makefile.kmk and export_modules.sh.
+#
+
+#
+# Copyright (C) 2009-2023 Oracle and/or its affiliates.
+#
+# This file is part of VirtualBox base platform packages, as
+# available from https://www.virtualbox.org.
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation, in version 3 of the
+# License.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, see <https://www.gnu.org/licenses>.
+#
+# The contents of this file may alternatively be used under the terms
+# of the Common Development and Distribution License Version 1.0
+# (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+# in the VirtualBox distribution, in which case the provisions of the
+# CDDL are applicable instead of those of the GPL.
+#
+# You may elect to license modified versions of this file under the
+# terms and conditions of either the GPL or the CDDL or both.
+#
+# SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+#
+
+VBOX_VBOXNETADP_SOURCES=" \
+ ${PATH_ROOT}/include/iprt/alloc.h=>include/iprt/alloc.h \
+ ${PATH_ROOT}/include/iprt/alloca.h=>include/iprt/alloca.h \
+ ${PATH_ROOT}/include/iprt/asm.h=>include/iprt/asm.h \
+ ${PATH_ROOT}/include/iprt/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/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/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/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/uuid.h=>include/iprt/uuid.h \
+ ${PATH_ROOT}/include/iprt/x86.h=>include/iprt/x86.h \
+ ${PATH_ROOT}/include/iprt/x86-helpers.h=>include/iprt/x86-helpers.h \
+ ${PATH_ROOT}/include/iprt/linux/version.h=>include/iprt/linux/version.h \
+ ${PATH_ROOT}/include/iprt/nocrt/limits.h=>include/iprt/nocrt/limits.h \
+ ${PATH_ROOT}/include/VBox/cdefs.h=>include/VBox/cdefs.h \
+ ${PATH_ROOT}/include/VBox/err.h=>include/VBox/err.h \
+ ${PATH_ROOT}/include/VBox/log.h=>include/VBox/log.h \
+ ${PATH_ROOT}/include/VBox/intnet.h=>include/VBox/intnet.h \
+ ${PATH_ROOT}/include/VBox/vmm/stam.h=>include/VBox/vmm/stam.h \
+ ${PATH_ROOT}/include/VBox/sup.h=>include/VBox/sup.h \
+ ${PATH_ROOT}/include/VBox/types.h=>include/VBox/types.h \
+ ${PATH_ROOT}/include/VBox/SUPDrvMangling.h=>include/VBox/SUPDrvMangling.h \
+ ${PATH_ROOT}/src/VBox/HostDrivers/VBoxNetAdp/linux/VBoxNetAdp-linux.c=>linux/VBoxNetAdp-linux.c \
+ ${PATH_ROOT}/src/VBox/HostDrivers/VBoxNetAdp/VBoxNetAdp.c=>VBoxNetAdp.c \
+ ${PATH_ROOT}/src/VBox/HostDrivers/VBoxNetAdp/VBoxNetAdpInternal.h=>VBoxNetAdpInternal.h \
+ ${PATH_ROOT}/src/VBox/Installer/linux/Makefile-footer.gmk=>Makefile-footer.gmk \
+ ${PATH_ROOT}/src/VBox/Installer/linux/Makefile-header.gmk=>Makefile-header.gmk \
+ ${PATH_ROOT}/src/VBox/Runtime/common/math/gcc/divdi3.c=>math/gcc/divdi3.c \
+ ${PATH_ROOT}/src/VBox/Runtime/common/math/gcc/moddi3.c=>math/gcc/moddi3.c \
+ ${PATH_ROOT}/src/VBox/Runtime/common/math/gcc/qdivrem.c=>math/gcc/qdivrem.c \
+ ${PATH_ROOT}/src/VBox/Runtime/common/math/gcc/quad.h=>math/gcc/quad.h \
+ ${PATH_ROOT}/src/VBox/Runtime/common/math/gcc/udivdi3.c=>math/gcc/udivdi3.c \
+ ${PATH_ROOT}/src/VBox/Runtime/common/math/gcc/udivmoddi4.c=>math/gcc/udivmoddi4.c \
+ ${PATH_ROOT}/src/VBox/Runtime/common/math/gcc/umoddi3.c=>math/gcc/umoddi3.c \
+ ${PATH_ROOT}/src/VBox/Runtime/common/string/strformat.cpp=>common/string/strformat.c \
+ ${PATH_ROOT}/src/VBox/Runtime/common/string/strformatrt.cpp=>common/string/strformatrt.c \
+ ${PATH_ROOT}/src/VBox/Runtime/common/string/strformattype.cpp=>common/string/strformattype.c \
+ ${PATH_ROOT}/src/VBox/Runtime/common/string/strprintf.cpp=>common/string/strprintf.c \
+ ${PATH_ROOT}/src/VBox/Runtime/common/string/strtonum.cpp=>common/string/strtonum.c \
+ ${PATH_ROOT}/src/VBox/Runtime/include/internal/iprt.h=>include/internal/iprt.h \
+ ${PATH_ROOT}/src/VBox/Runtime/r0drv/linux/the-linux-kernel.h=>r0drv/linux/the-linux-kernel.h \
+ ${PATH_OUT}/version-generated.h=>version-generated.h \
+ ${PATH_OUT}/revision-generated.h=>revision-generated.h \
+ ${PATH_OUT}/product-generated.h=>product-generated.h \
+"
+
diff --git a/src/VBox/HostDrivers/VBoxNetAdp/solaris/VBoxNetAdp-solaris.c b/src/VBox/HostDrivers/VBoxNetAdp/solaris/VBoxNetAdp-solaris.c
new file mode 100644
index 00000000..44227dbd
--- /dev/null
+++ b/src/VBox/HostDrivers/VBoxNetAdp/solaris/VBoxNetAdp-solaris.c
@@ -0,0 +1,581 @@
+/* $Id: VBoxNetAdp-solaris.c $ */
+/** @file
+ * VBoxNetAdapter - Network Adapter Driver (Host), Solaris Specific Code.
+ */
+
+/*
+ * Copyright (C) 2009-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#define LOG_GROUP LOG_GROUP_NET_ADP_DRV
+#include <VBox/log.h>
+#include <iprt/errcore.h>
+#include <VBox/version.h>
+#include <iprt/assert.h>
+#include <iprt/semaphore.h>
+#include <iprt/initterm.h>
+#include <iprt/assert.h>
+#include <iprt/mem.h>
+#include <iprt/rand.h>
+
+#include <sys/types.h>
+#include <sys/dlpi.h>
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/stat.h>
+#include <sys/stream.h>
+#include <sys/stropts.h>
+#include <sys/strsun.h>
+#include <sys/modctl.h>
+#include <sys/ddi.h>
+#include <sys/sunddi.h>
+#include <sys/sunldi.h>
+#include <sys/gld.h>
+
+#include "../VBoxNetAdpInternal.h"
+
+
+/*********************************************************************************************************************************
+* Defined Constants And Macros *
+*********************************************************************************************************************************/
+#define DEVICE_NAME "vboxnet"
+/** The module descriptions as seen in 'modinfo'. */
+#define DEVICE_DESC_DRV "VirtualBox NetAdp"
+/** The maximum MTU size permittable, value taken from "Oracle Quad 10 Gb or Dual 40
+ * Gb Ethernet Adapter User's Guide". */
+#define DEVICE_MAX_MTU_SIZE 9706
+
+
+/*********************************************************************************************************************************
+* Internal Functions *
+*********************************************************************************************************************************/
+static int VBoxNetAdpSolarisAttach(dev_info_t *pDip, ddi_attach_cmd_t enmCmd);
+static int VBoxNetAdpSolarisDetach(dev_info_t *pDip, ddi_detach_cmd_t enmCmd);
+static int VBoxNetAdpSolarisQuiesceNotNeeded(dev_info_t *pDip);
+
+/**
+ * Streams: module info.
+ */
+static struct module_info g_VBoxNetAdpSolarisModInfo =
+{
+ 0x0dd, /* module id */
+ DEVICE_NAME,
+ 0, /* min. packet size */
+ INFPSZ, /* max. packet size */
+ 0, /* hi-water mark */
+ 0 /* lo-water mark */
+};
+
+/**
+ * Streams: read queue hooks.
+ */
+static struct qinit g_VBoxNetAdpSolarisReadQ =
+{
+ NULL, /* read */
+ gld_rsrv,
+ gld_open,
+ gld_close,
+ NULL, /* admin (reserved) */
+ &g_VBoxNetAdpSolarisModInfo,
+ NULL /* module stats */
+};
+
+/**
+ * Streams: write queue hooks.
+ */
+static struct qinit g_VBoxNetAdpSolarisWriteQ =
+{
+ gld_wput,
+ gld_wsrv,
+ NULL, /* open */
+ NULL, /* close */
+ NULL, /* admin (reserved) */
+ &g_VBoxNetAdpSolarisModInfo,
+ NULL /* module stats */
+};
+
+/**
+ * Streams: IO stream tab.
+ */
+static struct streamtab g_VBoxNetAdpSolarisStreamTab =
+{
+ &g_VBoxNetAdpSolarisReadQ,
+ &g_VBoxNetAdpSolarisWriteQ,
+ NULL, /* muxread init */
+ NULL /* muxwrite init */
+};
+
+/**
+ * cb_ops: driver char/block entry points
+ */
+static struct cb_ops g_VBoxNetAdpSolarisCbOps =
+{
+ nulldev, /* cb open */
+ nulldev, /* cb close */
+ nodev, /* b strategy */
+ nodev, /* b dump */
+ nodev, /* b print */
+ nodev, /* cb read */
+ nodev, /* cb write */
+ nodev, /* cb ioctl */
+ nodev, /* c devmap */
+ nodev, /* c mmap */
+ nodev, /* c segmap */
+ nochpoll, /* c poll */
+ ddi_prop_op, /* property ops */
+ &g_VBoxNetAdpSolarisStreamTab,
+ D_MP, /* compat. flag */
+ CB_REV /* revision */
+};
+
+/**
+ * dev_ops: driver entry/exit and other ops.
+ */
+static struct dev_ops g_VBoxNetAdpSolarisDevOps =
+{
+ DEVO_REV, /* driver build revision */
+ 0, /* ref count */
+ gld_getinfo,
+ nulldev, /* identify */
+ nulldev, /* probe */
+ VBoxNetAdpSolarisAttach,
+ VBoxNetAdpSolarisDetach,
+ nodev, /* reset */
+ &g_VBoxNetAdpSolarisCbOps,
+ (struct bus_ops *)0,
+ nodev, /* power */
+ VBoxNetAdpSolarisQuiesceNotNeeded
+};
+
+/**
+ * modldrv: export driver specifics to kernel
+ */
+static struct modldrv g_VBoxNetAdpSolarisDriver =
+{
+ &mod_driverops, /* extern from kernel */
+ DEVICE_DESC_DRV " " VBOX_VERSION_STRING "r" RT_XSTR(VBOX_SVN_REV),
+ &g_VBoxNetAdpSolarisDevOps
+};
+
+/**
+ * modlinkage: export install/remove/info to the kernel
+ */
+static struct modlinkage g_VBoxNetAdpSolarisModLinkage =
+{
+ MODREV_1, /* loadable module system revision */
+ {
+ &g_VBoxNetAdpSolarisDriver, /* adapter streams driver framework */
+ NULL /* terminate array of linkage structures */
+ }
+};
+
+
+/*********************************************************************************************************************************
+* Global Variables *
+*********************************************************************************************************************************/
+/** The default ethernet broadcast address */
+static uchar_t achBroadcastAddr[] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF };
+
+/**
+ * vboxnetadp_state_t: per-instance data
+ */
+typedef struct vboxnetadp_state_t
+{
+ dev_info_t *pDip; /* device info. */
+ RTMAC FactoryMac; /* default 'factory' MAC address */
+ RTMAC CurrentMac; /* current MAC address */
+} vboxnetadp_state_t;
+
+
+/*********************************************************************************************************************************
+* Internal Functions *
+*********************************************************************************************************************************/
+static int vboxNetAdpSolarisGenerateMac(PRTMAC pMac);
+static int vboxNetAdpSolarisSetMacAddress(gld_mac_info_t *pMacInfo, unsigned char *pszMacAddr);
+static int vboxNetAdpSolarisSend(gld_mac_info_t *pMacInfo, mblk_t *pMsg);
+static int vboxNetAdpSolarisStub(gld_mac_info_t *pMacInfo);
+static int vboxNetAdpSolarisSetPromisc(gld_mac_info_t *pMacInfo, int fPromisc);
+static int vboxNetAdpSolarisSetMulticast(gld_mac_info_t *pMacInfo, unsigned char *pMulticastAddr, int fMulticast);
+static int vboxNetAdpSolarisGetStats(gld_mac_info_t *pMacInfo, struct gld_stats *pStats);
+
+
+/**
+ * Kernel entry points
+ */
+int _init(void)
+{
+ LogFunc((DEVICE_NAME ":_init\n"));
+
+ /*
+ * Prevent module autounloading.
+ */
+ modctl_t *pModCtl = mod_getctl(&g_VBoxNetAdpSolarisModLinkage);
+ if (pModCtl)
+ pModCtl->mod_loadflags |= MOD_NOAUTOUNLOAD;
+ else
+ LogRel((DEVICE_NAME ":failed to disable autounloading!\n"));
+
+ /*
+ * Initialize IPRT.
+ */
+ int rc = RTR0Init(0);
+ if (RT_SUCCESS(rc))
+ {
+ rc = mod_install(&g_VBoxNetAdpSolarisModLinkage);
+ if (!rc)
+ return rc;
+
+ LogRel((DEVICE_NAME ":mod_install failed. rc=%d\n", rc));
+ RTR0Term();
+ }
+ else
+ LogRel((DEVICE_NAME ":failed to initialize IPRT (rc=%d)\n", rc));
+
+ return RTErrConvertToErrno(rc);
+}
+
+
+int _fini(void)
+{
+ LogFunc((DEVICE_NAME ":_fini\n"));
+
+ /*
+ * Undo the work done during start (in reverse order).
+ */
+ int rc = mod_remove(&g_VBoxNetAdpSolarisModLinkage);
+ if (!rc)
+ RTR0Term();
+
+ return rc;
+}
+
+
+int _info(struct modinfo *pModInfo)
+{
+ LogFunc((DEVICE_NAME ":_info\n"));
+
+ int rc = mod_info(&g_VBoxNetAdpSolarisModLinkage, pModInfo);
+
+ Log((DEVICE_NAME ":_info returns %d\n", rc));
+ return rc;
+}
+
+
+/**
+ * Attach entry point, to attach a device to the system or resume it.
+ *
+ * @param pDip The module structure instance.
+ * @param enmCmd Operation type (attach/resume).
+ *
+ * @returns corresponding solaris error code.
+ */
+static int VBoxNetAdpSolarisAttach(dev_info_t *pDip, ddi_attach_cmd_t enmCmd)
+{
+ LogFunc((DEVICE_NAME ":VBoxNetAdpSolarisAttach pDip=%p enmCmd=%d\n", pDip, enmCmd));
+
+ int rc = -1;
+ switch (enmCmd)
+ {
+ case DDI_ATTACH:
+ {
+ gld_mac_info_t *pMacInfo = gld_mac_alloc(pDip);
+ if (pMacInfo)
+ {
+ vboxnetadp_state_t *pState = RTMemAllocZ(sizeof(vboxnetadp_state_t));
+ if (pState)
+ {
+ pState->pDip = pDip;
+
+ /*
+ * Setup GLD MAC layer registration info.
+ */
+ pMacInfo->gldm_reset = vboxNetAdpSolarisStub;
+ pMacInfo->gldm_start = vboxNetAdpSolarisStub;
+ pMacInfo->gldm_stop = vboxNetAdpSolarisStub;
+ pMacInfo->gldm_set_mac_addr = vboxNetAdpSolarisSetMacAddress;
+ pMacInfo->gldm_set_multicast = vboxNetAdpSolarisSetMulticast;
+ pMacInfo->gldm_set_promiscuous = vboxNetAdpSolarisSetPromisc;
+ pMacInfo->gldm_send = vboxNetAdpSolarisSend;
+ pMacInfo->gldm_intr = NULL;
+ pMacInfo->gldm_get_stats = vboxNetAdpSolarisGetStats;
+ pMacInfo->gldm_ioctl = NULL;
+ pMacInfo->gldm_ident = DEVICE_NAME;
+ pMacInfo->gldm_type = DL_ETHER;
+ pMacInfo->gldm_minpkt = 0;
+ pMacInfo->gldm_maxpkt = DEVICE_MAX_MTU_SIZE;
+ pMacInfo->gldm_capabilities = GLD_CAP_LINKSTATE;
+ AssertCompile(sizeof(RTMAC) == ETHERADDRL);
+
+ pMacInfo->gldm_addrlen = ETHERADDRL;
+ pMacInfo->gldm_saplen = -2;
+ pMacInfo->gldm_broadcast_addr = achBroadcastAddr;
+ pMacInfo->gldm_ppa = ddi_get_instance(pState->pDip);
+ pMacInfo->gldm_devinfo = pState->pDip;
+ pMacInfo->gldm_private = (caddr_t)pState;
+
+ /*
+ * We use a semi-random MAC addresses similar to a guest NIC's MAC address
+ * as the default factory address of the interface.
+ */
+ rc = vboxNetAdpSolarisGenerateMac(&pState->FactoryMac);
+ if (RT_SUCCESS(rc))
+ {
+ bcopy(&pState->FactoryMac, &pState->CurrentMac, sizeof(RTMAC));
+ pMacInfo->gldm_vendor_addr = (unsigned char *)&pState->FactoryMac;
+
+ /*
+ * Now try registering our GLD with the MAC layer.
+ * Registration can fail on some S10 versions when the MTU size is more than 1500.
+ * When we implement jumbo frames we should probably retry with MTU 1500 for S10.
+ */
+ rc = gld_register(pDip, (char *)ddi_driver_name(pDip), pMacInfo);
+ if (rc == DDI_SUCCESS)
+ {
+ ddi_report_dev(pDip);
+ gld_linkstate(pMacInfo, GLD_LINKSTATE_UP);
+ return DDI_SUCCESS;
+ }
+ else
+ LogRel((DEVICE_NAME ":VBoxNetAdpSolarisAttach failed to register GLD. rc=%d\n", rc));
+ }
+ else
+ LogRel((DEVICE_NAME ":VBoxNetAdpSolarisAttach failed to generate mac address.rc=%d\n"));
+
+ RTMemFree(pState);
+ }
+ else
+ LogRel((DEVICE_NAME ":VBoxNetAdpSolarisAttach failed to alloc state.\n"));
+
+ gld_mac_free(pMacInfo);
+ }
+ else
+ LogRel((DEVICE_NAME ":VBoxNetAdpSolarisAttach failed to alloc mac structure.\n"));
+ return DDI_FAILURE;
+ }
+
+ case DDI_RESUME:
+ {
+ /* Nothing to do here... */
+ return DDI_SUCCESS;
+ }
+
+ /* case DDI_PM_RESUME: */
+ 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 Operation type (detach/suspend).
+ *
+ * @returns corresponding solaris error code.
+ */
+static int VBoxNetAdpSolarisDetach(dev_info_t *pDip, ddi_detach_cmd_t enmCmd)
+{
+ LogFunc((DEVICE_NAME ":VBoxNetAdpSolarisDetach pDip=%p enmCmd=%d\n", pDip, enmCmd));
+
+ switch (enmCmd)
+ {
+ case DDI_DETACH:
+ {
+ /*
+ * Unregister and clean up.
+ */
+ gld_mac_info_t *pMacInfo = ddi_get_driver_private(pDip);
+ if (pMacInfo)
+ {
+ vboxnetadp_state_t *pState = (vboxnetadp_state_t *)pMacInfo->gldm_private;
+ if (pState)
+ {
+ gld_linkstate(pMacInfo, GLD_LINKSTATE_DOWN);
+ int rc = gld_unregister(pMacInfo);
+ if (rc == DDI_SUCCESS)
+ {
+ gld_mac_free(pMacInfo);
+ RTMemFree(pState);
+ return DDI_SUCCESS;
+ }
+ else
+ LogRel((DEVICE_NAME ":VBoxNetAdpSolarisDetach failed to unregister GLD from MAC layer.rc=%d\n", rc));
+ }
+ else
+ LogRel((DEVICE_NAME ":VBoxNetAdpSolarisDetach failed to get internal state.\n"));
+ }
+ else
+ LogRel((DEVICE_NAME ":VBoxNetAdpSolarisDetach failed to get driver private GLD data.\n"));
+
+ return DDI_FAILURE;
+ }
+
+ case DDI_RESUME:
+ {
+ /* Nothing to do here... */
+ return DDI_SUCCESS;
+ }
+
+ /* case DDI_SUSPEND: */
+ /* case DDI_HOTPLUG_DETACH: */
+ default:
+ return DDI_FAILURE;
+ }
+}
+
+
+/**
+ * Quiesce not-needed entry point, as Solaris 10 doesn't have any
+ * ddi_quiesce_not_needed() function.
+ *
+ * @param pDip The module structure instance.
+ *
+ * @return corresponding solaris error code.
+ */
+static int VBoxNetAdpSolarisQuiesceNotNeeded(dev_info_t *pDip)
+{
+ return DDI_SUCCESS;
+}
+
+
+static int vboxNetAdpSolarisGenerateMac(PRTMAC pMac)
+{
+ pMac->au8[0] = 0x08;
+ pMac->au8[1] = 0x00;
+ pMac->au8[2] = 0x27;
+ RTRandBytes(&pMac->au8[3], 3);
+ Log((DEVICE_NAME ":VBoxNetAdpSolarisGenerateMac Generated %.*Rhxs\n", sizeof(RTMAC), &pMac));
+ return VINF_SUCCESS;
+}
+
+
+static int vboxNetAdpSolarisSetMacAddress(gld_mac_info_t *pMacInfo, unsigned char *pszMacAddr)
+{
+ vboxnetadp_state_t *pState = (vboxnetadp_state_t *)pMacInfo->gldm_private;
+ if (pState)
+ {
+ bcopy(pszMacAddr, &pState->CurrentMac, sizeof(RTMAC));
+ Log((DEVICE_NAME ":vboxNetAdpSolarisSetMacAddress updated MAC %.*Rhxs\n", sizeof(RTMAC), &pState->CurrentMac));
+ return GLD_SUCCESS;
+ }
+ else
+ LogRel((DEVICE_NAME ":vboxNetAdpSolarisSetMacAddress failed to get internal state.\n"));
+ return GLD_FAILURE;
+}
+
+
+static int vboxNetAdpSolarisSend(gld_mac_info_t *pMacInfo, mblk_t *pMsg)
+{
+ while (pMsg)
+ {
+ mblk_t *pMsgNext = pMsg->b_cont;
+ pMsg->b_cont = NULL;
+ freemsg(pMsg);
+ pMsg = pMsgNext;
+ }
+ return GLD_SUCCESS;
+}
+
+
+static int vboxNetAdpSolarisStub(gld_mac_info_t *pMacInfo)
+{
+ return GLD_SUCCESS;
+}
+
+
+static int vboxNetAdpSolarisSetMulticast(gld_mac_info_t *pMacInfo, unsigned char *pMulticastAddr, int fMulticast)
+{
+ return GLD_SUCCESS;
+}
+
+
+static int vboxNetAdpSolarisSetPromisc(gld_mac_info_t *pMacInfo, int fPromisc)
+{
+ /* Host requesting promiscuous intnet connection... */
+ return GLD_SUCCESS;
+}
+
+
+static int vboxNetAdpSolarisGetStats(gld_mac_info_t *pMacInfo, struct gld_stats *pStats)
+{
+ /*
+ * For now fake up stats. Stats like duplex and speed are better set as they
+ * are used in utilities like dladm. Link state capabilities are critical
+ * as they are used by ipadm while trying to restore persistent interface configs.
+ */
+ vboxnetadp_state_t *pState = (vboxnetadp_state_t *)pMacInfo->gldm_private;
+ if (pState)
+ {
+ pStats->glds_speed = 1000000000ULL; /* Bits/sec. */
+ pStats->glds_media = GLDM_UNKNOWN; /* Media/Connector Type */
+ pStats->glds_intr = 0; /* Interrupt count */
+ pStats->glds_norcvbuf = 0; /* Recv. discards */
+ pStats->glds_errxmt = 0; /* Xmit errors */
+ pStats->glds_errrcv = 0; /* Recv. errors */
+ pStats->glds_missed = 0; /* Pkt Drops on Recv. */
+ pStats->glds_underflow = 0; /* Buffer underflows */
+ pStats->glds_overflow = 0; /* Buffer overflows */
+
+ /* Ether */
+ pStats->glds_frame = 0; /* Align errors */
+ pStats->glds_crc = 0; /* CRC errors */
+ pStats->glds_duplex = GLD_DUPLEX_FULL; /* Link duplex state */
+ pStats->glds_nocarrier = 0; /* Carrier sense errors */
+ pStats->glds_collisions = 0; /* Xmit Collisions */
+ pStats->glds_excoll = 0; /* Frame discard due to excess collisions */
+ pStats->glds_xmtlatecoll = 0; /* Late collisions */
+ pStats->glds_defer = 0; /* Deferred Xmits */
+ pStats->glds_dot3_first_coll = 0; /* Single collision frames */
+ pStats->glds_dot3_multi_coll = 0; /* Multiple collision frames */
+ pStats->glds_dot3_sqe_error = 0; /* SQE errors */
+ pStats->glds_dot3_mac_xmt_error = 0; /* MAC Xmit errors */
+ pStats->glds_dot3_mac_rcv_error = 0; /* Mac Recv. errors */
+ pStats->glds_dot3_frame_too_long = 0; /* Frame too long errors */
+ pStats->glds_short = 0; /* Runt frames */
+
+ pStats->glds_noxmtbuf = 0; /* Xmit Buf errors */
+ pStats->glds_xmtretry = 0; /* Xmit retries */
+ pStats->glds_multixmt = 0; /* Multicast Xmits */
+ pStats->glds_multircv = 0; /* Multicast Recvs. */
+ pStats->glds_brdcstxmt = 0; /* Broadcast Xmits*/
+ pStats->glds_brdcstrcv = 0; /* Broadcast Recvs. */
+
+ return GLD_SUCCESS;
+ }
+ else
+ LogRel((DEVICE_NAME ":vboxNetAdpSolarisGetStats failed to get internal state.\n"));
+ return GLD_FAILURE;
+}
+
diff --git a/src/VBox/HostDrivers/VBoxNetAdp/solaris/hostname.vboxnet0 b/src/VBox/HostDrivers/VBoxNetAdp/solaris/hostname.vboxnet0
new file mode 100644
index 00000000..30545bb9
--- /dev/null
+++ b/src/VBox/HostDrivers/VBoxNetAdp/solaris/hostname.vboxnet0
@@ -0,0 +1 @@
+192.168.56.1
diff --git a/src/VBox/HostDrivers/VBoxNetAdp/solaris/vboxnet.conf b/src/VBox/HostDrivers/VBoxNetAdp/solaris/vboxnet.conf
new file mode 100644
index 00000000..69aa69dd
--- /dev/null
+++ b/src/VBox/HostDrivers/VBoxNetAdp/solaris/vboxnet.conf
@@ -0,0 +1,43 @@
+# $Id: vboxnet.conf $
+## @file
+# Solaris Host VBoxNet Configuration
+#
+
+#
+# Copyright (C) 2009-2023 Oracle and/or its affiliates.
+#
+# This file is part of VirtualBox base platform packages, as
+# available from https://www.virtualbox.org.
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation, in version 3 of the
+# License.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, see <https://www.gnu.org/licenses>.
+#
+# The contents of this file may alternatively be used under the terms
+# of the Common Development and Distribution License Version 1.0
+# (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+# in the VirtualBox distribution, in which case the provisions of the
+# CDDL are applicable instead of those of the GPL.
+#
+# You may elect to license modified versions of this file under the
+# terms and conditions of either the GPL or the CDDL or both.
+#
+# SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+#
+
+# This needs to go into /platform/i86pc/kernel/drv,
+# while the 64-bit driver object goes into the amd64
+# subdirectory (32-bit drivers goes into the same
+# directory).
+#
+name="vboxnet" parent="pseudo" instance=0;
+
diff --git a/src/VBox/HostDrivers/VBoxNetAdp/win/Makefile.kup b/src/VBox/HostDrivers/VBoxNetAdp/win/Makefile.kup
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/src/VBox/HostDrivers/VBoxNetAdp/win/Makefile.kup
diff --git a/src/VBox/HostDrivers/VBoxNetAdp/win/VBoxNetAdp-win.cpp b/src/VBox/HostDrivers/VBoxNetAdp/win/VBoxNetAdp-win.cpp
new file mode 100644
index 00000000..d89ba4d0
--- /dev/null
+++ b/src/VBox/HostDrivers/VBoxNetAdp/win/VBoxNetAdp-win.cpp
@@ -0,0 +1,1888 @@
+/* $Id: VBoxNetAdp-win.cpp $ */
+/** @file
+ * VBoxNetAdp-win.cpp - NDIS6 Host-only Networking Driver, Windows-specific code.
+ */
+/*
+ * Copyright (C) 2014-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+#define LOG_GROUP LOG_GROUP_NET_ADP_DRV
+
+#include <VBox/log.h>
+#include <VBox/version.h>
+#include <VBox/err.h>
+#include <VBox/sup.h>
+#include <VBox/intnet.h>
+#include <VBox/intnetinline.h>
+#include <iprt/assert.h>
+#include <iprt/initterm.h>
+#include <iprt/list.h>
+#include <iprt/net.h>
+#include <iprt/semaphore.h>
+#include <iprt/string.h>
+#include <iprt/uuid.h>
+
+#include <iprt/nt/ntddk.h>
+#include <iprt/nt/ndis.h>
+
+#include "VBoxNetAdp-win.h"
+#include "VBox/VBoxNetCmn-win.h"
+
+#define VBOXNETADP_MEM_TAG 'OHBV'
+
+/*
+ * By default the link speed reported to be 1Gbps. We may wish to lower
+ * it to 100Mbps to work around issues with multi-cast traffic on the host.
+ * See @bugref{6379}.
+ */
+#define VBOXNETADPWIN_LINK_SPEED 1000000000ULL
+
+#define LogError LogRel
+
+/* Forward declarations */
+MINIPORT_INITIALIZE vboxNetAdpWinInitializeEx;
+MINIPORT_HALT vboxNetAdpWinHaltEx;
+MINIPORT_UNLOAD vboxNetAdpWinUnload;
+MINIPORT_PAUSE vboxNetAdpWinPause;
+MINIPORT_RESTART vboxNetAdpWinRestart;
+MINIPORT_OID_REQUEST vboxNetAdpWinOidRequest;
+MINIPORT_SEND_NET_BUFFER_LISTS vboxNetAdpWinSendNetBufferLists;
+MINIPORT_RETURN_NET_BUFFER_LISTS vboxNetAdpWinReturnNetBufferLists;
+MINIPORT_CANCEL_SEND vboxNetAdpWinCancelSend;
+MINIPORT_CHECK_FOR_HANG vboxNetAdpWinCheckForHangEx;
+MINIPORT_RESET vboxNetAdpWinResetEx;
+MINIPORT_DEVICE_PNP_EVENT_NOTIFY vboxNetAdpWinDevicePnPEventNotify;
+MINIPORT_SHUTDOWN vboxNetAdpWinShutdownEx;
+MINIPORT_CANCEL_OID_REQUEST vboxNetAdpWinCancelOidRequest;
+
+
+/* Packet types by destination address; used in statistics. */
+typedef enum {
+ kVBoxNetAdpWinPacketType_Unicast,
+ kVBoxNetAdpWinPacketType_Multicast,
+ kVBoxNetAdpWinPacketType_Broadcast,
+ kVBoxNetAdpWinPacketType_ArraySize /* Must be the last one */
+} VBOXNETADPWIN_PACKET_TYPE;
+
+
+/* Miniport states as defined by NDIS. */
+typedef enum {
+ kVBoxNetAdpWinState_Initializing,
+ kVBoxNetAdpWinState_Paused,
+ kVBoxNetAdpWinState_Restarting,
+ kVBoxNetAdpWinState_Running,
+ kVBoxNetAdpWinState_Pausing,
+ kVBoxNetAdpWinState_32BitHack = 0x7fffffff
+} VBOXNETADPWIN_ADAPTER_STATE;
+
+
+/*
+ * Valid state transitions are:
+ * 1) Disconnected -> Connecting : start the worker thread, attempting to init IDC;
+ * 2) Connecting -> Disconnected : failed to start IDC init worker thread;
+ * 3) Connecting -> Connected : IDC init successful, terminate the worker;
+ * 4) Connecting -> Stopping : IDC init incomplete, but the driver is being unloaded, terminate the worker;
+ * 5) Connected -> Stopping : IDC init was successful, no worker, the driver is being unloaded;
+ *
+ * Driver terminates in either in Disconnected or in Stopping state.
+ */
+typedef enum {
+ kVBoxNetAdpWinIdcState_Disconnected = 0, /* Initial state */
+ kVBoxNetAdpWinIdcState_Connecting, /* Attemping to init IDC, worker thread running */
+ kVBoxNetAdpWinIdcState_Connected, /* Successfully connected to IDC, worker thread terminated */
+ kVBoxNetAdpWinIdcState_Stopping /* Terminating the worker thread and disconnecting IDC */
+} VBOXNETADPWIN_IDC_STATE;
+
+typedef struct _VBOXNETADPGLOBALS
+{
+ /** Miniport driver handle. */
+ NDIS_HANDLE hMiniportDriver;
+ /** Power management capabilities, shared by all instances, do not change after init. */
+ NDIS_PNP_CAPABILITIES PMCaps;
+ /** The INTNET trunk network interface factory. */
+ INTNETTRUNKFACTORY TrunkFactory;
+ /** The SUPDRV component factory registration. */
+ SUPDRVFACTORY SupDrvFactory;
+ /** The SUPDRV IDC handle (opaque struct). */
+ SUPDRVIDCHANDLE SupDrvIDC;
+ /** IDC init thread handle. */
+ HANDLE hInitIdcThread;
+ /** Lock protecting the following members. */
+ NDIS_SPIN_LOCK Lock;
+ /** Lock-protected: the head of module list. */
+ RTLISTANCHOR ListOfAdapters;
+ /** Lock-protected: The number of current factory references. */
+ int32_t volatile cFactoryRefs;
+ /** Lock-protected: IDC initialization state. */
+ volatile uint32_t enmIdcState;
+ /** Lock-protected: event signaled when trunk factory is not in use. */
+ NDIS_EVENT EventUnloadAllowed;
+} VBOXNETADPGLOBALS, *PVBOXNETADPGLOBALS;
+
+/* win-specific global data */
+VBOXNETADPGLOBALS g_VBoxNetAdpGlobals;
+
+
+typedef struct _VBOXNETADP_ADAPTER {
+ /** Auxiliary member to link adapters into a list. */
+ RTLISTNODE node;
+ /** Adapter handle for NDIS. */
+ NDIS_HANDLE hAdapter;
+ /** Memory pool network buffers are allocated from. */
+ NDIS_HANDLE hPool;
+ /** Our RJ-45 port.
+ * This is what the internal network plugs into. */
+ INTNETTRUNKIFPORT MyPort;
+ /** The RJ-45 port on the INTNET "switch".
+ * This is what we're connected to. */
+ PINTNETTRUNKSWPORT pSwitchPort;
+ /** Pointer to global data */
+ PVBOXNETADPGLOBALS pGlobals;
+ /** Adapter state in NDIS, used for assertions only */
+ VBOXNETADPWIN_ADAPTER_STATE volatile enmAdapterState; /// @todo do we need it really?
+ /** The trunk state. */
+ INTNETTRUNKIFSTATE volatile enmTrunkState;
+ /** Number of pending operations, when it reaches zero we signal EventIdle. */
+ int32_t volatile cBusy;
+ /** The event that is signaled when we go idle and that pfnWaitForIdle blocks on. */
+ NDIS_EVENT EventIdle;
+ /** MAC address of adapter. */
+ RTMAC MacAddr;
+ /** Statistics: bytes received from internal network. */
+ uint64_t au64StatsInOctets[kVBoxNetAdpWinPacketType_ArraySize];
+ /** Statistics: packets received from internal network. */
+ uint64_t au64StatsInPackets[kVBoxNetAdpWinPacketType_ArraySize];
+ /** Statistics: bytes sent to internal network. */
+ uint64_t au64StatsOutOctets[kVBoxNetAdpWinPacketType_ArraySize];
+ /** Statistics: packets sent to internal network. */
+ uint64_t au64StatsOutPackets[kVBoxNetAdpWinPacketType_ArraySize];
+ /** Adapter friendly name. */
+ char szName[1];
+} VBOXNETADP_ADAPTER;
+typedef VBOXNETADP_ADAPTER *PVBOXNETADP_ADAPTER;
+
+
+/* Port */
+
+#define IFPORT_2_VBOXNETADP_ADAPTER(pIfPort) \
+ ( (PVBOXNETADP_ADAPTER)((uint8_t *)(pIfPort) - RT_UOFFSETOF(VBOXNETADP_ADAPTER, MyPort)) )
+
+DECLINLINE(VBOXNETADPWIN_ADAPTER_STATE) vboxNetAdpWinGetState(PVBOXNETADP_ADAPTER pThis)
+{
+ return (VBOXNETADPWIN_ADAPTER_STATE)ASMAtomicUoReadU32((uint32_t volatile *)&pThis->enmAdapterState);
+}
+
+DECLINLINE(VBOXNETADPWIN_ADAPTER_STATE) vboxNetAdpWinSetState(PVBOXNETADP_ADAPTER pThis, VBOXNETADPWIN_ADAPTER_STATE enmNewState)
+{
+ return (VBOXNETADPWIN_ADAPTER_STATE)ASMAtomicXchgU32((uint32_t volatile *)&pThis->enmAdapterState, enmNewState);
+}
+
+DECLINLINE(bool) vboxNetAdpWinSetState(PVBOXNETADP_ADAPTER pThis, VBOXNETADPWIN_ADAPTER_STATE enmNewState,
+ VBOXNETADPWIN_ADAPTER_STATE enmOldState)
+{
+ return ASMAtomicCmpXchgU32((uint32_t volatile *)&pThis->enmAdapterState, enmNewState, enmOldState);
+}
+
+#ifdef DEBUG
+
+DECLHIDDEN(void) vboxNetAdpWinDumpPackets(const char *pszMsg, PNET_BUFFER_LIST pBufLists)
+{
+ for (PNET_BUFFER_LIST pList = pBufLists; pList; pList = NET_BUFFER_LIST_NEXT_NBL(pList))
+ {
+ for (PNET_BUFFER pBuf = NET_BUFFER_LIST_FIRST_NB(pList); pBuf; pBuf = NET_BUFFER_NEXT_NB(pBuf))
+ {
+ Log6(("%s packet: cb=%d offset=%d", pszMsg, NET_BUFFER_DATA_LENGTH(pBuf), NET_BUFFER_DATA_OFFSET(pBuf)));
+ for (PMDL pMdl = NET_BUFFER_FIRST_MDL(pBuf);
+ pMdl != NULL;
+ pMdl = NDIS_MDL_LINKAGE(pMdl))
+ {
+ Log6((" MDL: cb=%d", MmGetMdlByteCount(pMdl)));
+ }
+ Log6(("\n"));
+ }
+ }
+}
+
+DECLINLINE(const char *) vboxNetAdpWinEthTypeStr(uint16_t uType)
+{
+ switch (uType)
+ {
+ case RTNET_ETHERTYPE_IPV4: return "IP";
+ case RTNET_ETHERTYPE_IPV6: return "IPv6";
+ case RTNET_ETHERTYPE_ARP: return "ARP";
+ }
+ return "unknown";
+}
+
+#define VBOXNETADP_PKTDMPSIZE 0x50
+
+/**
+ * Dump a packet to debug log.
+ *
+ * @param cpPacket The packet.
+ * @param cb The size of the packet.
+ * @param cszText A string denoting direction of packet transfer.
+ */
+DECLINLINE(void) vboxNetAdpWinDumpPacket(PCINTNETSG pSG, const char *cszText)
+{
+ uint8_t bPacket[VBOXNETADP_PKTDMPSIZE];
+
+ uint32_t cb = pSG->cbTotal < VBOXNETADP_PKTDMPSIZE ? pSG->cbTotal : VBOXNETADP_PKTDMPSIZE;
+ IntNetSgReadEx(pSG, 0, cb, bPacket);
+
+ AssertReturnVoid(cb >= 14);
+
+ uint8_t *pHdr = bPacket;
+ uint8_t *pEnd = bPacket + cb;
+ AssertReturnVoid(pEnd - pHdr >= 14);
+ uint16_t uEthType = RT_N2H_U16(*(uint16_t*)(pHdr+12));
+ Log2(("NetADP: %s (%d bytes), %RTmac => %RTmac, EthType=%s(0x%x)\n",
+ cszText, pSG->cbTotal, pHdr+6, pHdr, vboxNetAdpWinEthTypeStr(uEthType), uEthType));
+ pHdr += sizeof(RTNETETHERHDR);
+ if (uEthType == RTNET_ETHERTYPE_VLAN)
+ {
+ AssertReturnVoid(pEnd - pHdr >= 4);
+ uEthType = RT_N2H_U16(*(uint16_t*)(pHdr+2));
+ Log2((" + VLAN: id=%d EthType=%s(0x%x)\n", RT_N2H_U16(*(uint16_t*)(pHdr)) & 0xFFF,
+ vboxNetAdpWinEthTypeStr(uEthType), uEthType));
+ pHdr += 2 * sizeof(uint16_t);
+ }
+ uint8_t uProto = 0xFF;
+ switch (uEthType)
+ {
+ case RTNET_ETHERTYPE_IPV6:
+ AssertReturnVoid(pEnd - pHdr >= 40);
+ uProto = pHdr[6];
+ Log2((" + IPv6: %RTnaipv6 => %RTnaipv6\n", pHdr+8, pHdr+24));
+ pHdr += 40;
+ break;
+ case RTNET_ETHERTYPE_IPV4:
+ AssertReturnVoid(pEnd - pHdr >= 20);
+ uProto = pHdr[9];
+ Log2((" + IP: %RTnaipv4 => %RTnaipv4\n", *(uint32_t*)(pHdr+12), *(uint32_t*)(pHdr+16)));
+ pHdr += (pHdr[0] & 0xF) * 4;
+ break;
+ case RTNET_ETHERTYPE_ARP:
+ AssertReturnVoid(pEnd - pHdr >= 28);
+ AssertReturnVoid(RT_N2H_U16(*(uint16_t*)(pHdr+2)) == RTNET_ETHERTYPE_IPV4);
+ switch (RT_N2H_U16(*(uint16_t*)(pHdr+6)))
+ {
+ case 1: /* ARP request */
+ Log2((" + ARP-REQ: who-has %RTnaipv4 tell %RTnaipv4\n",
+ *(uint32_t*)(pHdr+24), *(uint32_t*)(pHdr+14)));
+ break;
+ case 2: /* ARP reply */
+ Log2((" + ARP-RPL: %RTnaipv4 is-at %RTmac\n",
+ *(uint32_t*)(pHdr+14), pHdr+8));
+ break;
+ default:
+ Log2((" + ARP: unknown op %d\n", RT_N2H_U16(*(uint16_t*)(pHdr+6))));
+ break;
+ }
+ break;
+ /* There is no default case as uProto is initialized with 0xFF */
+ }
+ while (uProto != 0xFF)
+ {
+ switch (uProto)
+ {
+ case 0: /* IPv6 Hop-by-Hop option*/
+ case 60: /* IPv6 Destination option*/
+ case 43: /* IPv6 Routing option */
+ case 44: /* IPv6 Fragment option */
+ Log2((" + IPv6 option (%d): <not implemented>\n", uProto));
+ uProto = pHdr[0];
+ pHdr += pHdr[1] * 8 + 8; /* Skip to the next extension/protocol */
+ break;
+ case 51: /* IPv6 IPsec AH */
+ Log2((" + IPv6 IPsec AH: <not implemented>\n"));
+ uProto = pHdr[0];
+ pHdr += (pHdr[1] + 2) * 4; /* Skip to the next extension/protocol */
+ break;
+ case 50: /* IPv6 IPsec ESP */
+ /* Cannot decode IPsec, fall through */
+ Log2((" + IPv6 IPsec ESP: <not implemented>\n"));
+ uProto = 0xFF;
+ break;
+ case 59: /* No Next Header */
+ Log2((" + IPv6 No Next Header\n"));
+ uProto = 0xFF;
+ break;
+ case 58: /* IPv6-ICMP */
+ switch (pHdr[0])
+ {
+ case 1: Log2((" + IPv6-ICMP: destination unreachable, code %d\n", pHdr[1])); break;
+ case 128: Log2((" + IPv6-ICMP: echo request\n")); break;
+ case 129: Log2((" + IPv6-ICMP: echo reply\n")); break;
+ default: Log2((" + IPv6-ICMP: unknown type %d, code %d\n", pHdr[0], pHdr[1])); break;
+ }
+ uProto = 0xFF;
+ break;
+ case 1: /* ICMP */
+ switch (pHdr[0])
+ {
+ case 0: Log2((" + ICMP: echo reply\n")); break;
+ case 8: Log2((" + ICMP: echo request\n")); break;
+ case 3: Log2((" + ICMP: destination unreachable, code %d\n", pHdr[1])); break;
+ default: Log2((" + ICMP: unknown type %d, code %d\n", pHdr[0], pHdr[1])); break;
+ }
+ uProto = 0xFF;
+ break;
+ case 6: /* TCP */
+ Log2((" + TCP: src=%d dst=%d seq=%x ack=%x\n",
+ RT_N2H_U16(*(uint16_t*)(pHdr)), RT_N2H_U16(*(uint16_t*)(pHdr+2)),
+ RT_N2H_U32(*(uint32_t*)(pHdr+4)), RT_N2H_U32(*(uint32_t*)(pHdr+8))));
+ uProto = 0xFF;
+ break;
+ case 17: /* UDP */
+ Log2((" + UDP: src=%d dst=%d\n",
+ RT_N2H_U16(*(uint16_t*)(pHdr)), RT_N2H_U16(*(uint16_t*)(pHdr+2))));
+ uProto = 0xFF;
+ break;
+ default:
+ Log2((" + Unknown: proto=0x%x\n", uProto));
+ uProto = 0xFF;
+ break;
+ }
+ }
+ Log3(("%.*Rhxd\n", cb, bPacket));
+}
+
+#else /* !DEBUG */
+//# define vboxNetAdpWinDumpFilterTypes(uFlags) do { } while (0)
+//# define vboxNetAdpWinDumpOffloadSettings(p) do { } while (0)
+//# define vboxNetAdpWinDumpSetOffloadSettings(p) do { } while (0)
+# define vboxNetAdpWinDumpPackets(m,l) do { } while (0)
+# define vboxNetAdpWinDumpPacket(p,t) do { } while (0)
+#endif /* !DEBUG */
+
+
+DECLHIDDEN(VBOXNETADPWIN_PACKET_TYPE) vboxNetAdpWinPacketType(PINTNETSG pSG)
+{
+ static const uint8_t g_abBcastAddr[] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF };
+ AssertReturn(pSG->cbTotal >= sizeof(g_abBcastAddr), kVBoxNetAdpWinPacketType_Unicast);
+ AssertReturn(pSG->cSegsUsed > 0, kVBoxNetAdpWinPacketType_Unicast);
+ AssertReturn(pSG->aSegs[0].cb >= sizeof(g_abBcastAddr), kVBoxNetAdpWinPacketType_Unicast);
+ if (!memcmp(pSG->aSegs[0].pv, g_abBcastAddr, sizeof(g_abBcastAddr)))
+ return kVBoxNetAdpWinPacketType_Broadcast;
+ if ((*(uint8_t*)pSG->aSegs[0].pv) & 1)
+ return kVBoxNetAdpWinPacketType_Multicast;
+ return kVBoxNetAdpWinPacketType_Unicast;
+}
+
+DECLINLINE(void) vboxNetAdpWinUpdateStats(uint64_t *pPacketStats, uint64_t *pOctetStats, PINTNETSG pSG)
+{
+ VBOXNETADPWIN_PACKET_TYPE enmPktType = vboxNetAdpWinPacketType(pSG);
+ ASMAtomicIncU64(&pPacketStats[enmPktType]);
+ ASMAtomicAddU64(&pOctetStats[enmPktType], pSG->cbTotal);
+}
+
+DECLINLINE(void) vboxNetAdpWinFreeMdlChain(PMDL pMdl)
+{
+ PMDL pMdlNext;
+ while (pMdl)
+ {
+ pMdlNext = pMdl->Next;
+ PUCHAR pDataBuf;
+ ULONG cb = 0;
+ NdisQueryMdl(pMdl, &pDataBuf, &cb, NormalPagePriority);
+ NdisFreeMdl(pMdl);
+ Log4(("vboxNetAdpWinFreeMdlChain: freed MDL 0x%p\n", pMdl));
+ NdisFreeMemory(pDataBuf, 0, 0);
+ Log4(("vboxNetAdpWinFreeMdlChain: freed data buffer 0x%p\n", pDataBuf));
+ pMdl = pMdlNext;
+ }
+}
+
+DECLHIDDEN(PNET_BUFFER_LIST) vboxNetAdpWinSGtoNB(PVBOXNETADP_ADAPTER pThis, PINTNETSG pSG)
+{
+ AssertReturn(pSG->cSegsUsed >= 1, NULL);
+ LogFlow(("==>vboxNetAdpWinSGtoNB: segments=%d hPool=%p cb=%u\n", pSG->cSegsUsed,
+ pThis->hPool, pSG->cbTotal));
+ AssertReturn(pThis->hPool, NULL);
+
+
+ PNET_BUFFER_LIST pBufList = NULL;
+ ULONG cbMdl = pSG->cbTotal;
+ ULONG uDataOffset = cbMdl - pSG->cbTotal;
+ PUCHAR pDataBuf = (PUCHAR)NdisAllocateMemoryWithTagPriority(pThis->hAdapter, cbMdl,
+ VBOXNETADP_MEM_TAG, NormalPoolPriority);
+ if (pDataBuf)
+ {
+ Log4(("vboxNetAdpWinSGtoNB: allocated data buffer (cb=%u) 0x%p\n", cbMdl, pDataBuf));
+ PMDL pMdl = NdisAllocateMdl(pThis->hAdapter, pDataBuf, cbMdl);
+ if (!pMdl)
+ {
+ NdisFreeMemory(pDataBuf, 0, 0);
+ Log4(("vboxNetAdpWinSGtoNB: freed data buffer 0x%p\n", pDataBuf));
+ LogError(("vboxNetAdpWinSGtoNB: failed to allocate an MDL (cb=%u)\n", cbMdl));
+ LogFlow(("<==vboxNetAdpWinSGtoNB: return NULL\n"));
+ return NULL;
+ }
+ PUCHAR pDst = pDataBuf + uDataOffset;
+ for (int i = 0; i < pSG->cSegsUsed; i++)
+ {
+ NdisMoveMemory(pDst, pSG->aSegs[i].pv, pSG->aSegs[i].cb);
+ pDst += pSG->aSegs[i].cb;
+ }
+ pBufList = NdisAllocateNetBufferAndNetBufferList(pThis->hPool,
+ 0 /* ContextSize */,
+ 0 /* ContextBackFill */,
+ pMdl,
+ uDataOffset,
+ pSG->cbTotal);
+ if (pBufList)
+ {
+ Log4(("vboxNetAdpWinSGtoNB: allocated NBL+NB 0x%p\n", pBufList));
+ pBufList->SourceHandle = pThis->hAdapter;
+ /** @todo Do we need to initialize anything else? */
+ }
+ else
+ {
+ LogError(("vboxNetAdpWinSGtoNB: failed to allocate an NBL+NB\n"));
+ vboxNetAdpWinFreeMdlChain(pMdl);
+ }
+ }
+ else
+ {
+ LogError(("vboxNetAdpWinSGtoNB: failed to allocate data buffer (size=%u)\n", cbMdl));
+ }
+
+ LogFlow(("<==vboxNetAdpWinSGtoNB: return %p\n", pBufList));
+ return pBufList;
+}
+
+DECLINLINE(void) vboxNetAdpWinDestroySG(PINTNETSG pSG)
+{
+ NdisFreeMemory(pSG, 0, 0);
+ Log4(("vboxNetAdpWinDestroySG: freed SG 0x%p\n", pSG));
+}
+
+/**
+ * Worker for vboxNetAdpWinNBtoSG() that gets the max segment count needed.
+ * @note vboxNetAdpWinNBtoSG may use fewer depending on cbPacket and offset!
+ * @note vboxNetLwfWinCalcSegments() is a copy of this code.
+ */
+DECLINLINE(ULONG) vboxNetAdpWinCalcSegments(PNET_BUFFER pNetBuf)
+{
+ ULONG cSegs = 0;
+ for (PMDL pMdl = NET_BUFFER_CURRENT_MDL(pNetBuf); pMdl; pMdl = NDIS_MDL_LINKAGE(pMdl))
+ {
+ /* Skip empty MDLs (see @bugref{9233}) */
+ if (MmGetMdlByteCount(pMdl))
+ cSegs++;
+ }
+ return cSegs;
+}
+
+/**
+ * @note vboxNetLwfWinNBtoSG() is a copy of this code.
+ */
+DECLHIDDEN(PINTNETSG) vboxNetAdpWinNBtoSG(PVBOXNETADP_ADAPTER pThis, PNET_BUFFER pNetBuf)
+{
+ ULONG cbPacket = NET_BUFFER_DATA_LENGTH(pNetBuf);
+ ULONG cSegs = vboxNetAdpWinCalcSegments(pNetBuf);
+ /* Allocate and initialize SG */
+ PINTNETSG pSG = (PINTNETSG)NdisAllocateMemoryWithTagPriority(pThis->hAdapter,
+ RT_UOFFSETOF_DYN(INTNETSG, aSegs[cSegs]),
+ VBOXNETADP_MEM_TAG,
+ NormalPoolPriority);
+ AssertReturn(pSG, pSG);
+ Log4(("vboxNetAdpWinNBtoSG: allocated SG 0x%p\n", pSG));
+ IntNetSgInitTempSegs(pSG, cbPacket /*cbTotal*/, cSegs, cSegs /*cSegsUsed*/);
+
+ ULONG uOffset = NET_BUFFER_CURRENT_MDL_OFFSET(pNetBuf);
+ cSegs = 0;
+ for (PMDL pMdl = NET_BUFFER_CURRENT_MDL(pNetBuf);
+ pMdl != NULL && cbPacket > 0;
+ pMdl = NDIS_MDL_LINKAGE(pMdl))
+ {
+ ULONG cbSrc = MmGetMdlByteCount(pMdl);
+ if (cbSrc == 0)
+ continue; /* Skip empty MDLs (see @bugref{9233}) */
+
+ PUCHAR pSrc = (PUCHAR)MmGetSystemAddressForMdlSafe(pMdl, LowPagePriority);
+ if (!pSrc)
+ {
+ vboxNetAdpWinDestroySG(pSG);
+ return NULL;
+ }
+
+ /* Handle the offset in the current (which is the first for us) MDL */
+ if (uOffset)
+ {
+ if (uOffset < cbSrc)
+ {
+ pSrc += uOffset;
+ cbSrc -= uOffset;
+ uOffset = 0;
+ }
+ else
+ {
+ /* This is an invalid MDL chain */
+ vboxNetAdpWinDestroySG(pSG);
+ return NULL;
+ }
+ }
+
+ /* Do not read the last MDL beyond packet's end */
+ if (cbSrc > cbPacket)
+ cbSrc = cbPacket;
+
+ Assert(cSegs < pSG->cSegsAlloc);
+ pSG->aSegs[cSegs].pv = pSrc;
+ pSG->aSegs[cSegs].cb = cbSrc;
+ pSG->aSegs[cSegs].Phys = NIL_RTHCPHYS;
+ cSegs++;
+ cbPacket -= cbSrc;
+ }
+
+ Assert(cbPacket == 0);
+ Assert(cSegs <= pSG->cSegsUsed);
+
+ /* Update actual segment count in case we used fewer than anticipated. */
+ pSG->cSegsUsed = (uint16_t)cSegs;
+
+ return pSG;
+}
+
+DECLINLINE(bool) vboxNetAdpWinIsActive(PVBOXNETADP_ADAPTER pThis)
+{
+ if (vboxNetAdpWinGetState(pThis) != kVBoxNetAdpWinState_Running)
+ return false;
+ if (pThis->enmTrunkState != INTNETTRUNKIFSTATE_ACTIVE)
+ return false;
+ AssertPtrReturn(pThis->pSwitchPort, false);
+ return true;
+}
+
+DECLHIDDEN(bool) vboxNetAdpWinForwardToIntNet(PVBOXNETADP_ADAPTER pThis, PNET_BUFFER_LIST pList, uint32_t fSrc)
+{
+ if (!vboxNetAdpWinIsActive(pThis))
+ {
+ LogFlow(("vboxNetAdpWinForwardToIntNet: not active\n"));
+ return false;
+ }
+ AssertReturn(pThis->pSwitchPort, false);
+ AssertReturn(pThis->pSwitchPort->pfnRecv, false);
+ LogFlow(("==>vboxNetAdpWinForwardToIntNet\n"));
+
+ if (ASMAtomicIncS32(&pThis->cBusy) == 1)
+ NdisResetEvent(&pThis->EventIdle);
+ for (PNET_BUFFER pBuf = NET_BUFFER_LIST_FIRST_NB(pList); pBuf; pBuf = NET_BUFFER_NEXT_NB(pBuf))
+ {
+ PINTNETSG pSG = vboxNetAdpWinNBtoSG(pThis, pBuf);
+ if (pSG)
+ {
+ vboxNetAdpWinUpdateStats(pThis->au64StatsOutPackets, pThis->au64StatsOutOctets, pSG);
+ vboxNetAdpWinDumpPacket(pSG, (fSrc & INTNETTRUNKDIR_WIRE)?"intnet <-- wire":"intnet <-- host");
+ pThis->pSwitchPort->pfnRecv(pThis->pSwitchPort, NULL, pSG, fSrc);
+ vboxNetAdpWinDestroySG(pSG);
+ }
+ }
+ if (ASMAtomicDecS32(&pThis->cBusy) == 0)
+ NdisSetEvent(&pThis->EventIdle);
+
+ return true;
+}
+
+
+/**
+ * @copydoc INTNETTRUNKIFPORT::pfnRetain
+ */
+static DECLCALLBACK(void) vboxNetAdpWinPortRetain(PINTNETTRUNKIFPORT pIfPort)
+{
+ PVBOXNETADP_ADAPTER pThis = IFPORT_2_VBOXNETADP_ADAPTER(pIfPort);
+ RT_NOREF1(pThis);
+ LogFlow(("vboxNetAdpWinPortRetain: pThis=%p, pIfPort=%p\n", pThis, pIfPort));
+}
+
+/**
+ * @copydoc INTNETTRUNKIFPORT::pfnRelease
+ */
+static DECLCALLBACK(void) vboxNetAdpWinPortRelease(PINTNETTRUNKIFPORT pIfPort)
+{
+ PVBOXNETADP_ADAPTER pThis = IFPORT_2_VBOXNETADP_ADAPTER(pIfPort);
+ RT_NOREF1(pThis);
+ LogFlow(("vboxNetAdpWinPortRelease: pThis=%p, pIfPort=%p\n", pThis, pIfPort));
+}
+
+/**
+ * @copydoc INTNETTRUNKIFPORT::pfnDisconnectAndRelease
+ */
+static DECLCALLBACK(void) vboxNetAdpWinPortDisconnectAndRelease(PINTNETTRUNKIFPORT pIfPort)
+{
+ PVBOXNETADP_ADAPTER pThis = IFPORT_2_VBOXNETADP_ADAPTER(pIfPort);
+
+ LogFlow(("vboxNetAdpWinPortDisconnectAndRelease: pThis=%p, pIfPort=%p\n", pThis, pIfPort));
+ /*
+ * Serious paranoia.
+ */
+ AssertPtr(pThis);
+ Assert(pThis->MyPort.u32Version == INTNETTRUNKIFPORT_VERSION);
+ Assert(pThis->MyPort.u32VersionEnd == INTNETTRUNKIFPORT_VERSION);
+ AssertPtr(pThis->pGlobals);
+ Assert(pThis->szName[0]);
+
+ AssertPtr(pThis->pSwitchPort);
+ Assert(pThis->enmTrunkState == INTNETTRUNKIFSTATE_DISCONNECTING);
+
+ pThis->pSwitchPort = NULL;
+}
+
+/**
+ * @copydoc INTNETTRUNKIFPORT::pfnSetState
+ */
+static DECLCALLBACK(INTNETTRUNKIFSTATE) vboxNetAdpWinPortSetState(PINTNETTRUNKIFPORT pIfPort, INTNETTRUNKIFSTATE enmState)
+{
+ PVBOXNETADP_ADAPTER pThis = IFPORT_2_VBOXNETADP_ADAPTER(pIfPort);
+ INTNETTRUNKIFSTATE enmOldTrunkState;
+
+ LogFlow(("vboxNetAdpWinPortSetState: pThis=%p, pIfPort=%p, enmState=%d\n", pThis, pIfPort, enmState));
+ /*
+ * Input validation.
+ */
+ AssertPtr(pThis);
+ AssertPtr(pThis->pGlobals);
+ Assert(pThis->MyPort.u32Version == INTNETTRUNKIFPORT_VERSION);
+ AssertPtrReturn(pThis->pSwitchPort, INTNETTRUNKIFSTATE_INVALID);
+ AssertReturn(enmState > INTNETTRUNKIFSTATE_INVALID && enmState < INTNETTRUNKIFSTATE_END,
+ INTNETTRUNKIFSTATE_INVALID);
+
+ enmOldTrunkState = pThis->enmTrunkState;
+ if (enmOldTrunkState != enmState)
+ ASMAtomicWriteU32((uint32_t volatile *)&pThis->enmTrunkState, enmState);
+
+ return enmOldTrunkState;
+}
+
+/**
+ * @copydoc INTNETTRUNKIFPORT::pfnWaitForIdle
+ */
+static DECLCALLBACK(int) vboxNetAdpWinPortWaitForIdle(PINTNETTRUNKIFPORT pIfPort, uint32_t cMillies)
+{
+ PVBOXNETADP_ADAPTER pThis = IFPORT_2_VBOXNETADP_ADAPTER(pIfPort);
+ int rc;
+
+ LogFlow(("vboxNetAdpWinPortWaitForIdle: pThis=%p, pIfPort=%p, cMillies=%u\n", pThis, pIfPort, cMillies));
+ /*
+ * Input validation.
+ */
+ AssertPtr(pThis);
+ Assert(pThis->MyPort.u32Version == INTNETTRUNKIFPORT_VERSION);
+ AssertPtrReturn(pThis->pSwitchPort, VERR_INVALID_STATE);
+ AssertReturn(pThis->enmTrunkState == INTNETTRUNKIFSTATE_DISCONNECTING, VERR_INVALID_STATE);
+
+ rc = NdisWaitEvent(&pThis->EventIdle, cMillies) ? VINF_SUCCESS : VERR_TIMEOUT;
+
+ return rc;
+}
+
+/**
+ * @copydoc INTNETTRUNKIFPORT::pfnXmit
+ */
+static DECLCALLBACK(int) vboxNetAdpWinPortXmit(PINTNETTRUNKIFPORT pIfPort, void *pvIfData, PINTNETSG pSG, uint32_t fDst)
+{
+ RT_NOREF1(fDst);
+ PVBOXNETADP_ADAPTER pThis = IFPORT_2_VBOXNETADP_ADAPTER(pIfPort);
+ int rc = VINF_SUCCESS;
+
+ LogFlow(("vboxNetAdpWinPortXmit: pThis=%p, pIfPort=%p, pvIfData=%p, pSG=%p, fDst=0x%x\n", pThis, pIfPort, pvIfData, pSG, fDst));
+ RT_NOREF1(pvIfData);
+ /*
+ * Input validation.
+ */
+ AssertPtr(pThis);
+ AssertPtr(pSG);
+ Assert(pThis->MyPort.u32Version == INTNETTRUNKIFPORT_VERSION);
+ AssertPtrReturn(pThis->pSwitchPort, VERR_INVALID_STATE);
+
+ vboxNetAdpWinDumpPacket(pSG, "intnet --> host");
+
+ /*
+ * First of all, indicate we are busy. It is possible the trunk or the adapter
+ * will get paused or even disconnected, so we need to check the state after
+ * we have marked ourselves busy.
+ * Later, when NDIS returns all buffers, we will mark ourselves idle.
+ */
+ if (ASMAtomicIncS32(&pThis->cBusy) == 1)
+ NdisResetEvent(&pThis->EventIdle);
+
+ if (vboxNetAdpWinIsActive(pThis))
+ {
+ PNET_BUFFER_LIST pBufList = vboxNetAdpWinSGtoNB(pThis, pSG);
+ if (pBufList)
+ {
+ NdisMIndicateReceiveNetBufferLists(pThis->hAdapter, pBufList, NDIS_DEFAULT_PORT_NUMBER, 1, 0);
+ vboxNetAdpWinUpdateStats(pThis->au64StatsInPackets, pThis->au64StatsInOctets, pSG);
+ }
+ }
+
+ return rc;
+}
+
+/**
+ * @copydoc INTNETTRUNKIFPORT::pfnNotifyMacAddress
+ */
+static DECLCALLBACK(void) vboxNetAdpWinPortNotifyMacAddress(PINTNETTRUNKIFPORT pIfPort, void *pvIfData, PCRTMAC pMac)
+{
+ PVBOXNETADP_ADAPTER pThis = IFPORT_2_VBOXNETADP_ADAPTER(pIfPort);
+
+ LogFlow(("vboxNetAdpWinPortNotifyMacAddress: pThis=%p, pIfPort=%p, pvIfData=%p, pMac=%p\n", pThis, pIfPort, pvIfData, pMac));
+ RT_NOREF3(pThis, pvIfData, pMac);
+ /*
+ * Input validation.
+ */
+ AssertPtr(pThis);
+ Assert(pThis->MyPort.u32Version == INTNETTRUNKIFPORT_VERSION);
+
+ /// @todo Do we really need to handle this?
+}
+
+
+/**
+ * @copydoc INTNETTRUNKIFPORT::pfnConnectInterface
+ */
+static DECLCALLBACK(int) vboxNetAdpWinPortConnectInterface(PINTNETTRUNKIFPORT pIfPort, void *pvIf, void **ppvIfData)
+{
+ PVBOXNETADP_ADAPTER pThis = IFPORT_2_VBOXNETADP_ADAPTER(pIfPort);
+ int rc;
+
+ LogFlow(("vboxNetAdpWinPortConnectInterface: pThis=%p, pIfPort=%p, pvIf=%p, ppvIfData=%p\n", pThis, pIfPort, pvIf, ppvIfData));
+ RT_NOREF3(pThis, pvIf, ppvIfData);
+ /*
+ * Input validation.
+ */
+ AssertPtr(pThis);
+ Assert(pThis->MyPort.u32Version == INTNETTRUNKIFPORT_VERSION);
+
+ rc = VINF_SUCCESS;
+
+ return rc;
+}
+
+
+/**
+ * @copydoc INTNETTRUNKIFPORT::pfnDisconnectInterface
+ */
+static DECLCALLBACK(void) vboxNetAdpWinPortDisconnectInterface(PINTNETTRUNKIFPORT pIfPort, void *pvIfData)
+{
+ PVBOXNETADP_ADAPTER pThis = IFPORT_2_VBOXNETADP_ADAPTER(pIfPort);
+ int rc;
+
+ LogFlow(("vboxNetAdpWinPortDisconnectInterface: pThis=%p, pIfPort=%p, pvIfData=%p\n", pThis, pIfPort, pvIfData));
+ RT_NOREF2(pThis, pvIfData);
+ /*
+ * Input validation.
+ */
+ AssertPtr(pThis);
+ Assert(pThis->MyPort.u32Version == INTNETTRUNKIFPORT_VERSION);
+
+ rc = VINF_SUCCESS;
+ AssertRC(rc);
+}
+
+
+
+/**
+ * Implements the SUPDRV component factor interface query method.
+ *
+ * @returns Pointer to an interface. NULL if not supported.
+ *
+ * @param pSupDrvFactory Pointer to the component factory registration structure.
+ * @param pSession The session - unused.
+ * @param pszInterfaceUuid The factory interface id.
+ */
+static DECLCALLBACK(void *) vboxNetAdpWinQueryFactoryInterface(PCSUPDRVFACTORY pSupDrvFactory, PSUPDRVSESSION pSession,
+ const char *pszInterfaceUuid)
+{
+ PVBOXNETADPGLOBALS pGlobals = (PVBOXNETADPGLOBALS)((uint8_t *)pSupDrvFactory - RT_UOFFSETOF(VBOXNETADPGLOBALS, SupDrvFactory));
+
+ /*
+ * Convert the UUID strings and compare them.
+ */
+ RTUUID UuidReq;
+ int rc = RTUuidFromStr(&UuidReq, pszInterfaceUuid);
+ if (RT_SUCCESS(rc))
+ {
+ if (!RTUuidCompareStr(&UuidReq, INTNETTRUNKFACTORY_UUID_STR))
+ {
+ NdisAcquireSpinLock(&pGlobals->Lock);
+ if (pGlobals->enmIdcState == kVBoxNetAdpWinIdcState_Connected)
+ {
+ pGlobals->cFactoryRefs++;
+ NdisResetEvent(&pGlobals->EventUnloadAllowed);
+ }
+ NdisReleaseSpinLock(&pGlobals->Lock);
+ return &pGlobals->TrunkFactory;
+ }
+#ifdef LOG_ENABLED
+ else
+ Log(("VBoxNetFlt: unknown factory interface query (%s)\n", pszInterfaceUuid));
+#endif
+ }
+ else
+ Log(("VBoxNetFlt: rc=%Rrc, uuid=%s\n", rc, pszInterfaceUuid));
+
+ RT_NOREF1(pSession);
+ return NULL;
+}
+
+
+DECLHIDDEN(void) vboxNetAdpWinReportCapabilities(PVBOXNETADP_ADAPTER pThis)
+{
+ if (pThis->pSwitchPort)
+ {
+ pThis->pSwitchPort->pfnReportMacAddress(pThis->pSwitchPort, &pThis->MacAddr);
+ /* Promiscuous mode makes no sense for host-only adapters, does it? */
+ pThis->pSwitchPort->pfnReportGsoCapabilities(pThis->pSwitchPort, 0,
+ INTNETTRUNKDIR_WIRE | INTNETTRUNKDIR_HOST);
+ pThis->pSwitchPort->pfnReportNoPreemptDsts(pThis->pSwitchPort, 0 /* none */);
+ }
+}
+
+/**
+ * @copydoc INTNETTRUNKFACTORY::pfnCreateAndConnect
+ */
+static DECLCALLBACK(int) vboxNetAdpWinFactoryCreateAndConnect(PINTNETTRUNKFACTORY pIfFactory, const char *pszName,
+ PINTNETTRUNKSWPORT pSwitchPort, uint32_t fFlags,
+ PINTNETTRUNKIFPORT *ppIfPort)
+{
+ PVBOXNETADPGLOBALS pGlobals = (PVBOXNETADPGLOBALS)((uint8_t *)pIfFactory - RT_UOFFSETOF(VBOXNETADPGLOBALS, TrunkFactory));
+
+ LogFlow(("==>vboxNetAdpWinFactoryCreateAndConnect: pszName=%p:{%s} fFlags=%#x\n", pszName, pszName, fFlags));
+ Assert(pGlobals->cFactoryRefs > 0);
+ AssertMsgReturn(!(fFlags & ~(INTNETTRUNKFACTORY_FLAG_NO_PROMISC)),
+ ("%#x\n", fFlags), VERR_INVALID_PARAMETER);
+
+ DbgPrint("vboxNetAdpWinFactoryCreateAndConnect: looking for %s...\n", pszName);
+ PVBOXNETADP_ADAPTER pAdapter = NULL;
+ NdisAcquireSpinLock(&pGlobals->Lock);
+ RTListForEach(&g_VBoxNetAdpGlobals.ListOfAdapters, pAdapter, VBOXNETADP_ADAPTER, node)
+ {
+ Log(("vboxNetAdpWinFactoryCreateAndConnect: evaluating adapter=%s\n", pAdapter->szName));
+ DbgPrint("vboxNetAdpWinFactoryCreateAndConnect: evaluating %s...\n", pAdapter->szName);
+ if (!RTStrICmp(pszName, pAdapter->szName))
+ {
+ pAdapter->pSwitchPort = pSwitchPort;
+ *ppIfPort = &pAdapter->MyPort;
+ NdisReleaseSpinLock(&g_VBoxNetAdpGlobals.Lock); /// @todo too early? adp should have been connected by the time we do this
+ Log(("vboxNetAdpWinFactoryCreateAndConnect: found matching adapter, name=%s\n", pszName));
+ vboxNetAdpWinReportCapabilities(pAdapter);
+ /// @todo I guess there is no need in vboxNetAdpWinRegisterIpAddrNotifier(pThis);
+ LogFlow(("<==vboxNetAdpWinFactoryCreateAndConnect: return VINF_SUCCESS\n"));
+ return VINF_SUCCESS;
+ }
+ }
+ NdisReleaseSpinLock(&pGlobals->Lock);
+ /// @todo vboxNetAdpLogErrorEvent(IO_ERR_INTERNAL_ERROR, STATUS_SUCCESS, 6);
+ DbgPrint("vboxNetAdpWinFactoryCreateAndConnect: could not find %s\n", pszName);
+ LogFlow(("<==vboxNetAdpWinFactoryCreateAndConnect: return VERR_INTNET_FLT_IF_NOT_FOUND\n"));
+ return VERR_INTNET_FLT_IF_NOT_FOUND;
+}
+
+
+/**
+ * @copydoc INTNETTRUNKFACTORY::pfnRelease
+ */
+static DECLCALLBACK(void) vboxNetAdpWinFactoryRelease(PINTNETTRUNKFACTORY pIfFactory)
+{
+ PVBOXNETADPGLOBALS pGlobals = (PVBOXNETADPGLOBALS)((uint8_t *)pIfFactory - RT_OFFSETOF(VBOXNETADPGLOBALS, TrunkFactory));
+
+ NdisAcquireSpinLock(&pGlobals->Lock);
+ int32_t cRefs = ASMAtomicDecS32(&pGlobals->cFactoryRefs);
+ if (cRefs == 0)
+ NdisSetEvent(&pGlobals->EventUnloadAllowed);
+ NdisReleaseSpinLock(&pGlobals->Lock);
+ Assert(cRefs >= 0); NOREF(cRefs);
+ LogFlow(("vboxNetAdpWinFactoryRelease: cRefs=%d (new)\n", cRefs));
+}
+
+
+
+/* IDC */
+
+DECLINLINE(const char *) vboxNetAdpWinIdcStateToText(uint32_t enmState)
+{
+ switch (enmState)
+ {
+ case kVBoxNetAdpWinIdcState_Disconnected: return "Disconnected";
+ case kVBoxNetAdpWinIdcState_Connecting: return "Connecting";
+ case kVBoxNetAdpWinIdcState_Connected: return "Connected";
+ case kVBoxNetAdpWinIdcState_Stopping: return "Stopping";
+ }
+ return "Unknown";
+}
+
+static VOID vboxNetAdpWinInitIdcWorker(PVOID pvContext)
+{
+ int rc;
+ PVBOXNETADPGLOBALS pGlobals = (PVBOXNETADPGLOBALS)pvContext;
+
+ /*
+ * Note that we break the rules here and access IDC state wihout acquiring
+ * the lock. This is ok because vboxNetAdpWinUnload will wait for this
+ * thread to terminate itself and we always use atomic access to IDC state.
+ * We check the state (while holding the lock) further when we have succeeded
+ * to connect. We cannot take the lock here and release it later as we will
+ * be holding it for too long.
+ */
+ while (ASMAtomicReadU32(&pGlobals->enmIdcState) == kVBoxNetAdpWinIdcState_Connecting)
+ {
+ /*
+ * Establish a connection to SUPDRV and register our component factory.
+ */
+ rc = SUPR0IdcOpen(&pGlobals->SupDrvIDC, 0 /* iReqVersion = default */, 0 /* iMinVersion = default */, NULL, NULL, NULL);
+ if (RT_SUCCESS(rc))
+ {
+ rc = SUPR0IdcComponentRegisterFactory(&pGlobals->SupDrvIDC, &pGlobals->SupDrvFactory);
+ if (RT_SUCCESS(rc))
+ {
+ /*
+ * At this point we should take the lock to access IDC state as
+ * we technically may now race with factory methods.
+ */
+ NdisAcquireSpinLock(&pGlobals->Lock);
+ bool fSuccess = ASMAtomicCmpXchgU32(&pGlobals->enmIdcState,
+ kVBoxNetAdpWinIdcState_Connected,
+ kVBoxNetAdpWinIdcState_Connecting);
+ NdisReleaseSpinLock(&pGlobals->Lock);
+ if (!fSuccess)
+ {
+ /* The state has been changed (the only valid transition is to "Stopping"), undo init */
+ rc = SUPR0IdcComponentDeregisterFactory(&pGlobals->SupDrvIDC, &pGlobals->SupDrvFactory);
+ AssertRC(rc);
+ SUPR0IdcClose(&pGlobals->SupDrvIDC);
+ Log(("vboxNetAdpWinInitIdcWorker: state change (Connecting -> %s) while initializing IDC, closed IDC, rc=0x%x\n",
+ vboxNetAdpWinIdcStateToText(ASMAtomicReadU32(&pGlobals->enmIdcState)), rc));
+ }
+ else
+ {
+ Log(("vboxNetAdpWinInitIdcWorker: IDC state change Connecting -> Connected\n"));
+ }
+ }
+ }
+ else
+ {
+ LARGE_INTEGER WaitIn100nsUnits;
+ WaitIn100nsUnits.QuadPart = -(LONGLONG)5000000; /* 0.5 sec */
+ KeDelayExecutionThread(KernelMode, FALSE /* non-alertable */, &WaitIn100nsUnits);
+ }
+ }
+ PsTerminateSystemThread(STATUS_SUCCESS);
+}
+
+
+DECLHIDDEN(int) vboxNetAdpWinStartInitIdcThread(PVBOXNETADPGLOBALS pGlobals)
+{
+ int rc = VERR_INVALID_STATE;
+
+ /* No locking needed yet */
+ if (ASMAtomicCmpXchgU32(&pGlobals->enmIdcState, kVBoxNetAdpWinIdcState_Connecting, kVBoxNetAdpWinIdcState_Disconnected))
+ {
+ Log(("vboxNetAdpWinStartInitIdcThread: IDC state change Diconnected -> Connecting\n"));
+
+ NTSTATUS Status = PsCreateSystemThread(&g_VBoxNetAdpGlobals.hInitIdcThread,
+ THREAD_ALL_ACCESS,
+ NULL,
+ NULL,
+ NULL,
+ vboxNetAdpWinInitIdcWorker,
+ &g_VBoxNetAdpGlobals);
+ Log(("vboxNetAdpWinStartInitIdcThread: create IDC initialization thread, status=0x%x\n", Status));
+ if (Status != STATUS_SUCCESS)
+ {
+ LogError(("vboxNetAdpWinStartInitIdcThread: IDC initialization failed (system thread creation, status=0x%x)\n", Status));
+ /*
+ * We failed to init IDC and there will be no second chance.
+ */
+ Log(("vboxNetAdpWinStartInitIdcThread: IDC state change Connecting -> Diconnected\n"));
+ ASMAtomicWriteU32(&g_VBoxNetAdpGlobals.enmIdcState, kVBoxNetAdpWinIdcState_Disconnected);
+ }
+ rc = RTErrConvertFromNtStatus(Status);
+ }
+ return rc;
+}
+
+
+
+/* === !!!! */
+
+
+NDIS_OID g_SupportedOids[] =
+{
+ OID_GEN_CURRENT_LOOKAHEAD,
+ OID_GEN_CURRENT_PACKET_FILTER,
+ OID_GEN_INTERRUPT_MODERATION,
+ OID_GEN_LINK_PARAMETERS,
+ OID_GEN_MAXIMUM_TOTAL_SIZE,
+ OID_GEN_RCV_OK,
+ OID_GEN_RECEIVE_BLOCK_SIZE,
+ OID_GEN_RECEIVE_BUFFER_SPACE,
+ OID_GEN_STATISTICS,
+ OID_GEN_TRANSMIT_BLOCK_SIZE,
+ OID_GEN_TRANSMIT_BUFFER_SPACE,
+ OID_GEN_VENDOR_DESCRIPTION,
+ OID_GEN_VENDOR_DRIVER_VERSION,
+ OID_GEN_VENDOR_ID,
+ OID_GEN_XMIT_OK,
+ OID_802_3_PERMANENT_ADDRESS,
+ OID_802_3_CURRENT_ADDRESS,
+ OID_802_3_MULTICAST_LIST,
+ OID_802_3_MAXIMUM_LIST_SIZE,
+ OID_PNP_CAPABILITIES,
+ OID_PNP_QUERY_POWER,
+ OID_PNP_SET_POWER
+};
+
+DECLHIDDEN(NDIS_STATUS) vboxNetAdpWinAllocAdapter(NDIS_HANDLE hAdapter, PVBOXNETADP_ADAPTER *ppAdapter, ULONG uIfIndex)
+{
+ NDIS_STATUS Status = NDIS_STATUS_SUCCESS;
+ PVBOXNETADP_ADAPTER pAdapter = NULL;
+ PVBOXNETADPGLOBALS pGlobals = &g_VBoxNetAdpGlobals;
+
+ LogFlow(("==>vboxNetAdpWinAllocAdapter: adapter handle=%p\n", hAdapter));
+
+ /* Get the name */
+ UNICODE_STRING strUnicodeName;
+ Status = NdisMQueryAdapterInstanceName(&strUnicodeName, hAdapter);
+ if (Status != NDIS_STATUS_SUCCESS)
+ {
+ LogError(("vboxNetAdpWinAllocAdapter: NdisMQueryAdapterInstanceName failed with 0x%x\n", Status));
+ return Status;
+ }
+
+ ANSI_STRING strAnsiName;
+ /* We use the miniport name to associate this filter module with the netflt instance */
+ NTSTATUS rc = RtlUnicodeStringToAnsiString(&strAnsiName,
+ &strUnicodeName,
+ TRUE);
+ if (rc != STATUS_SUCCESS)
+ {
+ LogError(("vboxNetAdpWinAllocAdapter: RtlUnicodeStringToAnsiString(%ls) failed with 0x%x\n",
+ strUnicodeName, rc));
+ //vboxNetAdpLogErrorEvent(IO_ERR_INTERNAL_ERROR, NDIS_STATUS_FAILURE, 2);
+ NdisFreeMemory(strUnicodeName.Buffer, 0, 0);
+ return NDIS_STATUS_FAILURE;
+ }
+ NdisFreeMemory(strUnicodeName.Buffer, 0, 0);
+ DbgPrint("vboxNetAdpWinAllocAdapter: name=%Z\n", &strAnsiName);
+
+ *ppAdapter = NULL;
+
+ UINT cbAdapterWithNameExtra = sizeof(VBOXNETADP_ADAPTER) + strAnsiName.Length;
+ pAdapter = (PVBOXNETADP_ADAPTER)NdisAllocateMemoryWithTagPriority(pGlobals->hMiniportDriver,
+ cbAdapterWithNameExtra,
+ VBOXNETADPWIN_TAG,
+ NormalPoolPriority);
+ if (!pAdapter)
+ {
+ RtlFreeAnsiString(&strAnsiName);
+ Status = NDIS_STATUS_RESOURCES;
+ Log(("vboxNetAdpWinAllocAdapter: Out of memory while allocating adapter context (size=%d)\n", sizeof(VBOXNETADP_ADAPTER)));
+ }
+ else
+ {
+ NdisZeroMemory(pAdapter, cbAdapterWithNameExtra);
+ NdisMoveMemory(pAdapter->szName, strAnsiName.Buffer, strAnsiName.Length);
+ RtlFreeAnsiString(&strAnsiName);
+
+ /* Allocate buffer pool */
+ NET_BUFFER_LIST_POOL_PARAMETERS PoolParams;
+ NdisZeroMemory(&PoolParams, sizeof(PoolParams));
+ PoolParams.Header.Type = NDIS_OBJECT_TYPE_DEFAULT;
+ PoolParams.Header.Revision = NET_BUFFER_LIST_POOL_PARAMETERS_REVISION_1;
+ PoolParams.Header.Size = sizeof(PoolParams);
+ PoolParams.ProtocolId = NDIS_PROTOCOL_ID_DEFAULT;
+ PoolParams.fAllocateNetBuffer = TRUE;
+ PoolParams.ContextSize = 0;
+ PoolParams.PoolTag = VBOXNETADP_MEM_TAG;
+ pAdapter->hPool = NdisAllocateNetBufferListPool(hAdapter, &PoolParams);
+ if (!pAdapter->hPool)
+ {
+ LogError(("vboxNetAdpWinAllocAdapter: NdisAllocateNetBufferListPool failed\n"));
+ NdisFreeMemory(pAdapter, 0, 0);
+ return NDIS_STATUS_RESOURCES;
+ }
+ Log4(("vboxNetAdpWinAllocAdapter: allocated NBL+NB pool 0x%p\n", pAdapter->hPool));
+
+ pAdapter->hAdapter = hAdapter;
+ pAdapter->MyPort.u32Version = INTNETTRUNKIFPORT_VERSION;
+ pAdapter->MyPort.pfnRetain = vboxNetAdpWinPortRetain;
+ pAdapter->MyPort.pfnRelease = vboxNetAdpWinPortRelease;
+ pAdapter->MyPort.pfnDisconnectAndRelease = vboxNetAdpWinPortDisconnectAndRelease;
+ pAdapter->MyPort.pfnSetState = vboxNetAdpWinPortSetState;
+ pAdapter->MyPort.pfnWaitForIdle = vboxNetAdpWinPortWaitForIdle;
+ pAdapter->MyPort.pfnXmit = vboxNetAdpWinPortXmit;
+ pAdapter->MyPort.pfnNotifyMacAddress = vboxNetAdpWinPortNotifyMacAddress;
+ pAdapter->MyPort.pfnConnectInterface = vboxNetAdpWinPortConnectInterface;
+ pAdapter->MyPort.pfnDisconnectInterface = vboxNetAdpWinPortDisconnectInterface;
+ pAdapter->MyPort.u32VersionEnd = INTNETTRUNKIFPORT_VERSION;
+ pAdapter->pGlobals = pGlobals;
+ pAdapter->enmAdapterState = kVBoxNetAdpWinState_Initializing;
+ pAdapter->enmTrunkState = INTNETTRUNKIFSTATE_INACTIVE;
+ pAdapter->cBusy = 0;
+ NdisInitializeEvent(&pAdapter->EventIdle);
+ NdisSetEvent(&pAdapter->EventIdle); /* We are idle initially */
+
+ /* Use a locally administered version of the OUI we use for the guest NICs. */
+ pAdapter->MacAddr.au8[0] = 0x08 | 2;
+ pAdapter->MacAddr.au8[1] = 0x00;
+ pAdapter->MacAddr.au8[2] = 0x27;
+
+ pAdapter->MacAddr.au8[3] = (uIfIndex >> 16) & 0xFF;
+ pAdapter->MacAddr.au8[4] = (uIfIndex >> 8) & 0xFF;
+ pAdapter->MacAddr.au8[5] = uIfIndex & 0xFF;
+
+ NdisAcquireSpinLock(&pGlobals->Lock);
+ RTListPrepend(&pGlobals->ListOfAdapters, &pAdapter->node);
+ NdisReleaseSpinLock(&pGlobals->Lock);
+
+ *ppAdapter = pAdapter;
+ }
+ LogFlow(("<==vboxNetAdpWinAllocAdapter: status=0x%x\n", Status));
+ return Status;
+}
+
+DECLHIDDEN(void) vboxNetAdpWinFreeAdapter(PVBOXNETADP_ADAPTER pAdapter)
+{
+ /* Remove from adapter chain */
+ NdisAcquireSpinLock(&pAdapter->pGlobals->Lock);
+ RTListNodeRemove(&pAdapter->node);
+ NdisReleaseSpinLock(&pAdapter->pGlobals->Lock);
+
+ NdisFreeMemory(pAdapter, 0, 0);
+}
+
+DECLINLINE(NDIS_MEDIA_CONNECT_STATE) vboxNetAdpWinGetConnectState(PVBOXNETADP_ADAPTER pAdapter)
+{
+ RT_NOREF1(pAdapter);
+ return MediaConnectStateConnected;
+}
+
+
+DECLHIDDEN(NDIS_STATUS) vboxNetAdpWinInitializeEx(IN NDIS_HANDLE NdisMiniportHandle,
+ IN NDIS_HANDLE MiniportDriverContext,
+ IN PNDIS_MINIPORT_INIT_PARAMETERS MiniportInitParameters)
+{
+ RT_NOREF1(MiniportDriverContext);
+ PVBOXNETADP_ADAPTER pAdapter = NULL;
+ NDIS_STATUS Status = NDIS_STATUS_SUCCESS;
+
+ LogFlow(("==>vboxNetAdpWinInitializeEx: miniport=0x%x\n", NdisMiniportHandle));
+
+ do
+ {
+ NDIS_MINIPORT_ADAPTER_REGISTRATION_ATTRIBUTES RAttrs = {{0}};
+ NDIS_MINIPORT_ADAPTER_GENERAL_ATTRIBUTES GAttrs = {{0}};
+
+ Status = vboxNetAdpWinAllocAdapter(NdisMiniportHandle, &pAdapter, MiniportInitParameters->IfIndex);
+ if (Status != NDIS_STATUS_SUCCESS)
+ {
+ Log(("vboxNetAdpWinInitializeEx: Failed to allocate the adapter context with 0x%x\n", Status));
+ break;
+ }
+
+ RAttrs.Header.Type = NDIS_OBJECT_TYPE_MINIPORT_ADAPTER_REGISTRATION_ATTRIBUTES;
+ RAttrs.Header.Size = NDIS_SIZEOF_MINIPORT_ADAPTER_REGISTRATION_ATTRIBUTES_REVISION_1;
+ RAttrs.Header.Revision = NDIS_MINIPORT_ADAPTER_REGISTRATION_ATTRIBUTES_REVISION_1;
+ RAttrs.MiniportAdapterContext = pAdapter;
+ RAttrs.AttributeFlags = VBOXNETADPWIN_ATTR_FLAGS; // NDIS_MINIPORT_ATTRIBUTES_NDIS_WDM
+ RAttrs.CheckForHangTimeInSeconds = VBOXNETADPWIN_HANG_CHECK_TIME;
+ RAttrs.InterfaceType = NdisInterfaceInternal;
+
+ Status = NdisMSetMiniportAttributes(NdisMiniportHandle,
+ (PNDIS_MINIPORT_ADAPTER_ATTRIBUTES)&RAttrs);
+ if (Status != NDIS_STATUS_SUCCESS)
+ {
+ Log(("vboxNetAdpWinInitializeEx: NdisMSetMiniportAttributes(registration) failed with 0x%x\n", Status));
+ break;
+ }
+
+ /// @todo Registry?
+
+ /// @todo WDM stack?
+
+ /// @todo DPC?
+
+ GAttrs.Header.Type = NDIS_OBJECT_TYPE_MINIPORT_ADAPTER_GENERAL_ATTRIBUTES;
+ GAttrs.Header.Size = NDIS_SIZEOF_MINIPORT_ADAPTER_GENERAL_ATTRIBUTES_REVISION_1;
+ GAttrs.Header.Revision = NDIS_MINIPORT_ADAPTER_GENERAL_ATTRIBUTES_REVISION_1;
+
+ GAttrs.MediaType = NdisMedium802_3;
+ GAttrs.PhysicalMediumType = NdisPhysicalMediumUnspecified;
+ GAttrs.MtuSize = 1500; /// @todo
+ GAttrs.MaxXmitLinkSpeed = VBOXNETADPWIN_LINK_SPEED;
+ GAttrs.XmitLinkSpeed = VBOXNETADPWIN_LINK_SPEED;
+ GAttrs.MaxRcvLinkSpeed = VBOXNETADPWIN_LINK_SPEED;
+ GAttrs.RcvLinkSpeed = VBOXNETADPWIN_LINK_SPEED;
+ GAttrs.MediaConnectState = vboxNetAdpWinGetConnectState(pAdapter);
+ GAttrs.MediaDuplexState = MediaDuplexStateFull;
+ GAttrs.LookaheadSize = 1500; /// @todo
+ GAttrs.MacOptions = VBOXNETADP_MAC_OPTIONS;
+ GAttrs.SupportedPacketFilters = VBOXNETADP_SUPPORTED_FILTERS;
+ GAttrs.MaxMulticastListSize = 32; /// @todo
+
+ GAttrs.MacAddressLength = ETH_LENGTH_OF_ADDRESS;
+ Assert(GAttrs.MacAddressLength == sizeof(pAdapter->MacAddr));
+ memcpy(GAttrs.PermanentMacAddress, pAdapter->MacAddr.au8, GAttrs.MacAddressLength);
+ memcpy(GAttrs.CurrentMacAddress, pAdapter->MacAddr.au8, GAttrs.MacAddressLength);
+
+ GAttrs.RecvScaleCapabilities = NULL;
+ GAttrs.AccessType = NET_IF_ACCESS_BROADCAST;
+ GAttrs.DirectionType = NET_IF_DIRECTION_SENDRECEIVE;
+ GAttrs.ConnectionType = NET_IF_CONNECTION_DEDICATED;
+ GAttrs.IfType = IF_TYPE_ETHERNET_CSMACD;
+ GAttrs.IfConnectorPresent = false;
+ GAttrs.SupportedStatistics = VBOXNETADPWIN_SUPPORTED_STATISTICS;
+ GAttrs.SupportedPauseFunctions = NdisPauseFunctionsUnsupported;
+ GAttrs.DataBackFillSize = 0;
+ GAttrs.ContextBackFillSize = 0;
+ GAttrs.SupportedOidList = g_SupportedOids;
+ GAttrs.SupportedOidListLength = sizeof(g_SupportedOids);
+ GAttrs.AutoNegotiationFlags = NDIS_LINK_STATE_DUPLEX_AUTO_NEGOTIATED;
+ GAttrs.PowerManagementCapabilities = &g_VBoxNetAdpGlobals.PMCaps;
+
+ Status = NdisMSetMiniportAttributes(NdisMiniportHandle,
+ (PNDIS_MINIPORT_ADAPTER_ATTRIBUTES)&GAttrs);
+ if (Status != NDIS_STATUS_SUCCESS)
+ {
+ Log(("vboxNetAdpWinInitializeEx: NdisMSetMiniportAttributes(general) failed with 0x%x\n", Status));
+ break;
+ }
+
+ VBOXNETADPWIN_ADAPTER_STATE enmPrevState = vboxNetAdpWinSetState(pAdapter, kVBoxNetAdpWinState_Paused);
+ RT_NOREF1(enmPrevState);
+ Assert(enmPrevState == kVBoxNetAdpWinState_Initializing);
+ } while (false);
+
+ if (Status != NDIS_STATUS_SUCCESS)
+ {
+ if (pAdapter)
+ vboxNetAdpWinFreeAdapter(pAdapter);
+ }
+
+ LogFlow(("<==vboxNetAdpWinInitializeEx: status=0x%x\n", Status));
+ return Status;
+}
+
+DECLHIDDEN(VOID) vboxNetAdpWinHaltEx(IN NDIS_HANDLE MiniportAdapterContext,
+ IN NDIS_HALT_ACTION HaltAction)
+{
+ RT_NOREF1(HaltAction);
+ PVBOXNETADP_ADAPTER pThis = (PVBOXNETADP_ADAPTER)MiniportAdapterContext;
+ LogFlow(("==>vboxNetAdpWinHaltEx\n"));
+ AssertPtr(pThis);
+ Assert(vboxNetAdpWinGetState(pThis) == kVBoxNetAdpWinState_Paused);
+ /*
+ * Check if the trunk is active which means the adapter gets disabled
+ * while it is used by VM(s) and we need to disconnect the trunk.
+ */
+ if (pThis->pSwitchPort && pThis->enmTrunkState == INTNETTRUNKIFSTATE_ACTIVE)
+ pThis->pSwitchPort->pfnDisconnect(pThis->pSwitchPort, &pThis->MyPort, NULL);
+ /*
+ * Since we are already in the paused state and we have disconnected
+ * the trunk, we can safely destroy this adapter.
+ */
+ vboxNetAdpWinFreeAdapter(pThis);
+ LogFlow(("<==vboxNetAdpWinHaltEx\n"));
+}
+
+DECLHIDDEN(NDIS_STATUS) vboxNetAdpWinPause(IN NDIS_HANDLE MiniportAdapterContext,
+ IN PNDIS_MINIPORT_PAUSE_PARAMETERS MiniportPauseParameters)
+{
+ RT_NOREF1(MiniportPauseParameters);
+ PVBOXNETADP_ADAPTER pThis = (PVBOXNETADP_ADAPTER)MiniportAdapterContext;
+ NDIS_STATUS Status = NDIS_STATUS_SUCCESS;
+ LogFlow(("==>vboxNetAdpWinPause\n"));
+ VBOXNETADPWIN_ADAPTER_STATE enmPrevState = vboxNetAdpWinSetState(pThis, kVBoxNetAdpWinState_Pausing);
+ Assert(enmPrevState == kVBoxNetAdpWinState_Running);
+ if (!NdisWaitEvent(&pThis->EventIdle, 1000 /* ms */))
+ {
+ LogError(("vboxNetAdpWinPause: timed out while pausing the adapter\n"));
+ /// @todo implement NDIS_STATUS_PENDING case? probably not.
+ }
+ enmPrevState = vboxNetAdpWinSetState(pThis, kVBoxNetAdpWinState_Paused);
+ Assert(enmPrevState == kVBoxNetAdpWinState_Pausing);
+ LogFlow(("<==vboxNetAdpWinPause: status=0x%x\n", Status));
+ return Status;
+}
+
+DECLHIDDEN(NDIS_STATUS) vboxNetAdpWinRestart(IN NDIS_HANDLE MiniportAdapterContext,
+ IN PNDIS_MINIPORT_RESTART_PARAMETERS MiniportRestartParameters)
+{
+ RT_NOREF1(MiniportRestartParameters);
+ PVBOXNETADP_ADAPTER pThis = (PVBOXNETADP_ADAPTER)MiniportAdapterContext;
+ NDIS_STATUS Status = NDIS_STATUS_SUCCESS;
+ LogFlow(("==>vboxNetAdpWinRestart\n"));
+ VBOXNETADPWIN_ADAPTER_STATE enmPrevState = vboxNetAdpWinSetState(pThis, kVBoxNetAdpWinState_Restarting);
+ Assert(enmPrevState == kVBoxNetAdpWinState_Paused);
+ /// @todo anything?
+ enmPrevState = vboxNetAdpWinSetState(pThis, kVBoxNetAdpWinState_Running);
+ Assert(enmPrevState == kVBoxNetAdpWinState_Restarting);
+ LogFlow(("<==vboxNetAdpWinRestart: status=0x%x\n", Status));
+ return Status;
+}
+
+DECLINLINE(uint64_t) vboxNetAdpWinStatsTotals(uint64_t *pStats)
+{
+ return pStats[kVBoxNetAdpWinPacketType_Unicast]
+ + pStats[kVBoxNetAdpWinPacketType_Multicast]
+ + pStats[kVBoxNetAdpWinPacketType_Broadcast];
+}
+
+DECLINLINE(PVOID) vboxNetAdpWinStatsU64(uint64_t *pTmp, ULONG *pcbTmp, uint64_t u64Stat)
+{
+ *pcbTmp = sizeof(*pTmp);
+ *pTmp = u64Stat;
+ return pTmp;
+}
+
+DECLHIDDEN(NDIS_STATUS) vboxNetAdpWinOidRqQuery(PVBOXNETADP_ADAPTER pThis,
+ PNDIS_OID_REQUEST pRequest)
+{
+ NDIS_STATUS Status = NDIS_STATUS_SUCCESS;
+ struct _NDIS_OID_REQUEST::_REQUEST_DATA::_QUERY *pQuery = &pRequest->DATA.QUERY_INFORMATION;
+
+ LogFlow(("==>vboxNetAdpWinOidRqQuery\n"));
+
+ uint64_t u64Tmp = 0;
+ ULONG ulTmp = 0;
+ PVOID pInfo = &ulTmp;
+ ULONG cbInfo = sizeof(ulTmp);
+
+ switch (pQuery->Oid)
+ {
+ case OID_GEN_INTERRUPT_MODERATION:
+ {
+ PNDIS_INTERRUPT_MODERATION_PARAMETERS pParams =
+ (PNDIS_INTERRUPT_MODERATION_PARAMETERS)pQuery->InformationBuffer;
+ cbInfo = NDIS_SIZEOF_INTERRUPT_MODERATION_PARAMETERS_REVISION_1;
+ if (cbInfo > pQuery->InformationBufferLength)
+ break;
+ pParams->Header.Type = NDIS_OBJECT_TYPE_DEFAULT;
+ pParams->Header.Revision = NDIS_INTERRUPT_MODERATION_PARAMETERS_REVISION_1;
+ pParams->Header.Size = NDIS_SIZEOF_INTERRUPT_MODERATION_PARAMETERS_REVISION_1;
+ pParams->Flags = 0;
+ pParams->InterruptModeration = NdisInterruptModerationNotSupported;
+ pInfo = NULL; /* Do not copy */
+ break;
+ }
+ case OID_GEN_MAXIMUM_TOTAL_SIZE:
+ case OID_GEN_RECEIVE_BLOCK_SIZE:
+ case OID_GEN_TRANSMIT_BLOCK_SIZE:
+ ulTmp = VBOXNETADP_MAX_FRAME_SIZE;
+ break;
+ case OID_GEN_RECEIVE_BUFFER_SPACE:
+ case OID_GEN_TRANSMIT_BUFFER_SPACE:
+ /// @todo Make configurable
+ ulTmp = VBOXNETADP_MAX_FRAME_SIZE * 40;
+ break;
+ case OID_GEN_RCV_OK:
+ pInfo = vboxNetAdpWinStatsU64(&u64Tmp, &cbInfo, vboxNetAdpWinStatsTotals(pThis->au64StatsInPackets));
+ break;
+ case OID_GEN_XMIT_OK:
+ pInfo = vboxNetAdpWinStatsU64(&u64Tmp, &cbInfo, vboxNetAdpWinStatsTotals(pThis->au64StatsOutPackets));
+ break;
+ case OID_GEN_STATISTICS:
+ {
+ PNDIS_STATISTICS_INFO pStats =
+ (PNDIS_STATISTICS_INFO)pQuery->InformationBuffer;
+ cbInfo = NDIS_SIZEOF_STATISTICS_INFO_REVISION_1;
+ if (cbInfo > pQuery->InformationBufferLength)
+ break;
+ pInfo = NULL; /* Do not copy */
+ memset(pStats, 0, cbInfo);
+ pStats->Header.Type = NDIS_OBJECT_TYPE_DEFAULT;
+ pStats->Header.Revision = NDIS_STATISTICS_INFO_REVISION_1;
+ pStats->Header.Size = NDIS_SIZEOF_STATISTICS_INFO_REVISION_1;
+ pStats->SupportedStatistics =
+ NDIS_STATISTICS_FLAGS_VALID_DIRECTED_FRAMES_RCV
+ | NDIS_STATISTICS_FLAGS_VALID_MULTICAST_FRAMES_RCV
+ | NDIS_STATISTICS_FLAGS_VALID_BROADCAST_FRAMES_RCV
+ | NDIS_STATISTICS_FLAGS_VALID_BYTES_RCV
+ | NDIS_STATISTICS_FLAGS_VALID_RCV_DISCARDS
+ | NDIS_STATISTICS_FLAGS_VALID_RCV_ERROR
+ | NDIS_STATISTICS_FLAGS_VALID_DIRECTED_FRAMES_XMIT
+ | NDIS_STATISTICS_FLAGS_VALID_MULTICAST_FRAMES_XMIT
+ | NDIS_STATISTICS_FLAGS_VALID_BROADCAST_FRAMES_XMIT
+ | NDIS_STATISTICS_FLAGS_VALID_BYTES_XMIT
+ | NDIS_STATISTICS_FLAGS_VALID_XMIT_ERROR
+ | NDIS_STATISTICS_FLAGS_VALID_XMIT_DISCARDS
+ | NDIS_STATISTICS_FLAGS_VALID_DIRECTED_BYTES_RCV
+ | NDIS_STATISTICS_FLAGS_VALID_MULTICAST_BYTES_RCV
+ | NDIS_STATISTICS_FLAGS_VALID_BROADCAST_BYTES_RCV
+ | NDIS_STATISTICS_FLAGS_VALID_DIRECTED_BYTES_XMIT
+ | NDIS_STATISTICS_FLAGS_VALID_MULTICAST_BYTES_XMIT
+ | NDIS_STATISTICS_FLAGS_VALID_BROADCAST_BYTES_XMIT;
+
+ pStats->ifHCInOctets = vboxNetAdpWinStatsTotals(pThis->au64StatsInOctets);
+ pStats->ifHCInUcastPkts = ASMAtomicReadU64(&pThis->au64StatsInPackets[kVBoxNetAdpWinPacketType_Unicast]);
+ pStats->ifHCInMulticastPkts = ASMAtomicReadU64(&pThis->au64StatsInPackets[kVBoxNetAdpWinPacketType_Multicast]);
+ pStats->ifHCInBroadcastPkts = ASMAtomicReadU64(&pThis->au64StatsInPackets[kVBoxNetAdpWinPacketType_Broadcast]);
+ pStats->ifHCOutOctets = vboxNetAdpWinStatsTotals(pThis->au64StatsOutOctets);;
+ pStats->ifHCOutUcastPkts = ASMAtomicReadU64(&pThis->au64StatsOutPackets[kVBoxNetAdpWinPacketType_Unicast]);
+ pStats->ifHCOutMulticastPkts = ASMAtomicReadU64(&pThis->au64StatsOutPackets[kVBoxNetAdpWinPacketType_Multicast]);
+ pStats->ifHCOutBroadcastPkts = ASMAtomicReadU64(&pThis->au64StatsOutPackets[kVBoxNetAdpWinPacketType_Broadcast]);
+ pStats->ifHCInUcastOctets = ASMAtomicReadU64(&pThis->au64StatsInOctets[kVBoxNetAdpWinPacketType_Unicast]);
+ pStats->ifHCInMulticastOctets = ASMAtomicReadU64(&pThis->au64StatsInOctets[kVBoxNetAdpWinPacketType_Multicast]);
+ pStats->ifHCInBroadcastOctets = ASMAtomicReadU64(&pThis->au64StatsInOctets[kVBoxNetAdpWinPacketType_Broadcast]);
+ pStats->ifHCOutUcastOctets = ASMAtomicReadU64(&pThis->au64StatsOutOctets[kVBoxNetAdpWinPacketType_Unicast]);
+ pStats->ifHCOutMulticastOctets = ASMAtomicReadU64(&pThis->au64StatsOutOctets[kVBoxNetAdpWinPacketType_Multicast]);
+ pStats->ifHCOutBroadcastOctets = ASMAtomicReadU64(&pThis->au64StatsOutOctets[kVBoxNetAdpWinPacketType_Broadcast]);
+ break;
+ }
+ case OID_GEN_VENDOR_DESCRIPTION:
+ pInfo = VBOXNETADP_VENDOR_NAME;
+ cbInfo = sizeof(VBOXNETADP_VENDOR_NAME);
+ break;
+ case OID_GEN_VENDOR_DRIVER_VERSION:
+ ulTmp = (VBOXNETADP_VERSION_NDIS_MAJOR << 16) | VBOXNETADP_VERSION_NDIS_MINOR;
+ break;
+ case OID_GEN_VENDOR_ID:
+ ulTmp = VBOXNETADP_VENDOR_ID;
+ break;
+ case OID_802_3_PERMANENT_ADDRESS:
+ case OID_802_3_CURRENT_ADDRESS:
+ pInfo = &pThis->MacAddr;
+ cbInfo = sizeof(pThis->MacAddr);
+ break;
+ //case OID_802_3_MULTICAST_LIST:
+ case OID_802_3_MAXIMUM_LIST_SIZE:
+ ulTmp = VBOXNETADP_MCAST_LIST_SIZE;
+ break;
+ case OID_PNP_CAPABILITIES:
+ pInfo = &pThis->pGlobals->PMCaps;
+ cbInfo = sizeof(pThis->pGlobals->PMCaps);
+ break;
+ case OID_PNP_QUERY_POWER:
+ pInfo = NULL; /* Do not copy */
+ cbInfo = 0;
+ break;
+ default:
+ Status = NDIS_STATUS_NOT_SUPPORTED;
+ break;
+ }
+
+ if (Status == NDIS_STATUS_SUCCESS)
+ {
+ if (cbInfo > pQuery->InformationBufferLength)
+ {
+ pQuery->BytesNeeded = cbInfo;
+ Status = NDIS_STATUS_BUFFER_TOO_SHORT;
+ }
+ else
+ {
+ if (pInfo)
+ NdisMoveMemory(pQuery->InformationBuffer, pInfo, cbInfo);
+ pQuery->BytesWritten = cbInfo;
+ }
+ }
+
+ LogFlow(("<==vboxNetAdpWinOidRqQuery: status=0x%x\n", Status));
+ return Status;
+}
+
+DECLHIDDEN(NDIS_STATUS) vboxNetAdpWinOidRqSet(PVBOXNETADP_ADAPTER pAdapter,
+ PNDIS_OID_REQUEST pRequest)
+{
+ RT_NOREF1(pAdapter);
+ NDIS_STATUS Status = NDIS_STATUS_SUCCESS;
+ struct _NDIS_OID_REQUEST::_REQUEST_DATA::_SET *pSet = &pRequest->DATA.SET_INFORMATION;
+
+ LogFlow(("==>vboxNetAdpWinOidRqSet\n"));
+
+ switch (pSet->Oid)
+ {
+ case OID_GEN_CURRENT_LOOKAHEAD:
+ if (pSet->InformationBufferLength != sizeof(ULONG))
+ {
+ pSet->BytesNeeded = sizeof(ULONG);
+ Status = NDIS_STATUS_INVALID_LENGTH;
+ break;
+ }
+ /// @todo For the time being we simply ignore lookahead settings.
+ pSet->BytesRead = sizeof(ULONG);
+ Status = NDIS_STATUS_SUCCESS;
+ break;
+
+ case OID_GEN_CURRENT_PACKET_FILTER:
+ if (pSet->InformationBufferLength != sizeof(ULONG))
+ {
+ pSet->BytesNeeded = sizeof(ULONG);
+ Status = NDIS_STATUS_INVALID_LENGTH;
+ break;
+ }
+ /// @todo For the time being we simply ignore packet filter settings.
+ pSet->BytesRead = pSet->InformationBufferLength;
+ Status = NDIS_STATUS_SUCCESS;
+ break;
+
+ case OID_GEN_INTERRUPT_MODERATION:
+ pSet->BytesNeeded = 0;
+ pSet->BytesRead = 0;
+ Status = NDIS_STATUS_INVALID_DATA;
+ break;
+
+ case OID_PNP_SET_POWER:
+ if (pSet->InformationBufferLength < sizeof(NDIS_DEVICE_POWER_STATE))
+ {
+ Status = NDIS_STATUS_INVALID_LENGTH;
+ break;
+ }
+ pSet->BytesRead = sizeof(NDIS_DEVICE_POWER_STATE);
+ Status = NDIS_STATUS_SUCCESS;
+ break;
+
+ default:
+ Status = NDIS_STATUS_NOT_SUPPORTED;
+ break;
+ }
+
+ LogFlow(("<==vboxNetAdpWinOidRqSet: status=0x%x\n", Status));
+ return Status;
+}
+
+DECLHIDDEN(NDIS_STATUS) vboxNetAdpWinOidRequest(IN NDIS_HANDLE MiniportAdapterContext,
+ IN PNDIS_OID_REQUEST NdisRequest)
+{
+ NDIS_STATUS Status = NDIS_STATUS_SUCCESS;
+ PVBOXNETADP_ADAPTER pAdapter = (PVBOXNETADP_ADAPTER)MiniportAdapterContext;
+ LogFlow(("==>vboxNetAdpWinOidRequest\n"));
+ vboxNetCmnWinDumpOidRequest(__FUNCTION__, NdisRequest);
+
+ switch (NdisRequest->RequestType)
+ {
+#if 0
+ case NdisRequestMethod:
+ Status = vboxNetAdpWinOidRqMethod(pAdapter, NdisRequest);
+ break;
+#endif
+
+ case NdisRequestSetInformation:
+ Status = vboxNetAdpWinOidRqSet(pAdapter, NdisRequest);
+ break;
+
+ case NdisRequestQueryInformation:
+ case NdisRequestQueryStatistics:
+ Status = vboxNetAdpWinOidRqQuery(pAdapter, NdisRequest);
+ break;
+
+ default:
+ Status = NDIS_STATUS_NOT_SUPPORTED;
+ break;
+ }
+ LogFlow(("<==vboxNetAdpWinOidRequest: status=0x%x\n", Status));
+ return Status;
+}
+
+DECLHIDDEN(VOID) vboxNetAdpWinSendNetBufferLists(IN NDIS_HANDLE MiniportAdapterContext,
+ IN PNET_BUFFER_LIST NetBufferLists,
+ IN NDIS_PORT_NUMBER PortNumber,
+ IN ULONG SendFlags)
+{
+ RT_NOREF1(PortNumber);
+ PVBOXNETADP_ADAPTER pAdapter = (PVBOXNETADP_ADAPTER)MiniportAdapterContext;
+ LogFlow(("==>vboxNetAdpWinSendNetBufferLists\n"));
+ PNET_BUFFER_LIST pNbl = NetBufferLists;
+ vboxNetAdpWinDumpPackets("vboxNetAdpWinSendNetBufferLists: got", pNbl);
+
+ /* We alwast complete all send requests. */
+ for (pNbl = NetBufferLists; pNbl; pNbl = NET_BUFFER_LIST_NEXT_NBL(pNbl))
+ {
+ vboxNetAdpWinForwardToIntNet(pAdapter, pNbl, INTNETTRUNKDIR_HOST);
+ NET_BUFFER_LIST_STATUS(pNbl) = NDIS_STATUS_SUCCESS;
+ }
+ NdisMSendNetBufferListsComplete(pAdapter->hAdapter, NetBufferLists,
+ (SendFlags & NDIS_SEND_FLAGS_DISPATCH_LEVEL) ?
+ NDIS_SEND_COMPLETE_FLAGS_DISPATCH_LEVEL : 0);
+ LogFlow(("<==vboxNetAdpWinSendNetBufferLists\n"));
+}
+
+DECLHIDDEN(VOID) vboxNetAdpWinReturnNetBufferLists(IN NDIS_HANDLE MiniportAdapterContext,
+ IN PNET_BUFFER_LIST NetBufferLists,
+ IN ULONG ReturnFlags)
+{
+ LogFlow(("==>vboxNetAdpWinReturnNetBufferLists\n"));
+ RT_NOREF1(ReturnFlags);
+ PVBOXNETADP_ADAPTER pThis = (PVBOXNETADP_ADAPTER)MiniportAdapterContext;
+ PNET_BUFFER_LIST pList = NetBufferLists;
+ while (pList)
+ {
+ Assert(pList->SourceHandle == pThis->hAdapter);
+ Assert(NET_BUFFER_LIST_FIRST_NB(pList));
+ Assert(NET_BUFFER_FIRST_MDL(NET_BUFFER_LIST_FIRST_NB(pList)));
+
+ PNET_BUFFER_LIST pNextList = NET_BUFFER_LIST_NEXT_NBL(pList);
+
+ vboxNetAdpWinFreeMdlChain(NET_BUFFER_FIRST_MDL(NET_BUFFER_LIST_FIRST_NB(pList)));
+ NdisFreeNetBufferList(pList);
+ Log4(("vboxNetLwfWinReturnNetBufferLists: freed NBL+NB+MDL+Data 0x%p\n", pList));
+ Assert(ASMAtomicReadS32(&pThis->cBusy) > 0);
+ if (ASMAtomicDecS32(&pThis->cBusy) == 0)
+ NdisSetEvent(&pThis->EventIdle);
+
+ pList = pNextList;
+ }
+ LogFlow(("<==vboxNetAdpWinReturnNetBufferLists\n"));
+}
+
+DECLHIDDEN(VOID) vboxNetAdpWinCancelSend(IN NDIS_HANDLE MiniportAdapterContext,
+ IN PVOID CancelId)
+{
+ RT_NOREF2(MiniportAdapterContext, CancelId);
+ LogFlow(("==>vboxNetAdpWinCancelSend\n"));
+ Log(("vboxNetAdpWinCancelSend: We should not be here!\n"));
+ LogFlow(("<==vboxNetAdpWinCancelSend\n"));
+}
+
+
+DECLHIDDEN(BOOLEAN) vboxNetAdpWinCheckForHangEx(IN NDIS_HANDLE MiniportAdapterContext)
+{
+ RT_NOREF1(MiniportAdapterContext);
+ LogFlow(("==>vboxNetAdpWinCheckForHangEx\n"));
+ LogFlow(("<==vboxNetAdpWinCheckForHangEx return false\n"));
+ return FALSE;
+}
+
+DECLHIDDEN(NDIS_STATUS) vboxNetAdpWinResetEx(IN NDIS_HANDLE MiniportAdapterContext,
+ OUT PBOOLEAN AddressingReset)
+{
+ RT_NOREF2(MiniportAdapterContext, AddressingReset);
+ NDIS_STATUS Status = NDIS_STATUS_SUCCESS;
+ LogFlow(("==>vboxNetAdpWinResetEx\n"));
+ LogFlow(("<==vboxNetAdpWinResetEx: status=0x%x\n", Status));
+ return Status;
+}
+
+DECLHIDDEN(VOID) vboxNetAdpWinDevicePnPEventNotify(IN NDIS_HANDLE MiniportAdapterContext,
+ IN PNET_DEVICE_PNP_EVENT NetDevicePnPEvent)
+{
+ RT_NOREF2(MiniportAdapterContext, NetDevicePnPEvent);
+ LogFlow(("==>vboxNetAdpWinDevicePnPEventNotify\n"));
+ Log(("vboxNetAdpWinDevicePnPEventNotify: PnP event=%d\n", NetDevicePnPEvent->DevicePnPEvent));
+ LogFlow(("<==vboxNetAdpWinDevicePnPEventNotify\n"));
+}
+
+
+DECLHIDDEN(VOID) vboxNetAdpWinShutdownEx(IN NDIS_HANDLE MiniportAdapterContext,
+ IN NDIS_SHUTDOWN_ACTION ShutdownAction)
+{
+ RT_NOREF2(MiniportAdapterContext, ShutdownAction);
+ LogFlow(("==>vboxNetAdpWinShutdownEx\n"));
+ Log(("vboxNetAdpWinShutdownEx: action=%d\n", ShutdownAction));
+ LogFlow(("<==vboxNetAdpWinShutdownEx\n"));
+}
+
+DECLHIDDEN(VOID) vboxNetAdpWinCancelOidRequest(IN NDIS_HANDLE MiniportAdapterContext,
+ IN PVOID RequestId)
+{
+ RT_NOREF2(MiniportAdapterContext, RequestId);
+ LogFlow(("==>vboxNetAdpWinCancelOidRequest\n"));
+ Log(("vboxNetAdpWinCancelOidRequest: req id=%p\n", RequestId));
+ LogFlow(("<==vboxNetAdpWinCancelOidRequest\n"));
+}
+
+
+
+DECLHIDDEN(VOID) vboxNetAdpWinUnload(IN PDRIVER_OBJECT DriverObject)
+{
+ RT_NOREF1(DriverObject);
+ LogFlow(("==>vboxNetAdpWinUnload\n"));
+ PVBOXNETADPGLOBALS pGlobals = &g_VBoxNetAdpGlobals;
+ int rc;
+ NDIS_STATUS Status;
+ PKTHREAD pThread = NULL;
+
+ /* We are about to disconnect IDC, let's make it clear so the factories will know */
+ NdisAcquireSpinLock(&pGlobals->Lock);
+ uint32_t enmPrevState = ASMAtomicXchgU32(&g_VBoxNetAdpGlobals.enmIdcState, kVBoxNetAdpWinIdcState_Stopping);
+ NdisReleaseSpinLock(&pGlobals->Lock);
+ Log(("vboxNetAdpWinUnload: IDC state change %s -> Stopping\n", vboxNetAdpWinIdcStateToText(enmPrevState)));
+
+ switch (enmPrevState)
+ {
+ case kVBoxNetAdpWinIdcState_Disconnected:
+ /* Have not even attempted to connect -- nothing to do. */
+ break;
+ case kVBoxNetAdpWinIdcState_Stopping:
+ /* Impossible, but another thread is alreading doing StopIdc, bail out */
+ LogError(("vboxNetAdpWinUnload: called in 'Stopping' state\n"));
+ break;
+ case kVBoxNetAdpWinIdcState_Connecting:
+ /* the worker thread is running, let's wait for it to stop */
+ Status = ObReferenceObjectByHandle(g_VBoxNetAdpGlobals.hInitIdcThread,
+ THREAD_ALL_ACCESS, NULL, KernelMode,
+ (PVOID*)&pThread, NULL);
+ if (Status == STATUS_SUCCESS)
+ {
+ KeWaitForSingleObject(pThread, Executive, KernelMode, FALSE, NULL);
+ ObDereferenceObject(pThread);
+ }
+ else
+ {
+ LogError(("vboxNetAdpWinStopIdc: ObReferenceObjectByHandle(%p) failed with 0x%x\n",
+ g_VBoxNetAdpGlobals.hInitIdcThread, Status));
+ }
+ break;
+ case kVBoxNetAdpWinIdcState_Connected:
+ /* the worker succeeded in IDC init and terminated */
+ /* Make sure nobody uses the trunk factory. Wait half a second if needed. */
+ if (!NdisWaitEvent(&pGlobals->EventUnloadAllowed, 500))
+ LogRel(("VBoxNetAdp: unloading driver while trunk factory is in use!\n"));
+ rc = SUPR0IdcComponentDeregisterFactory(&pGlobals->SupDrvIDC, &pGlobals->SupDrvFactory);
+ AssertRC(rc);
+ SUPR0IdcClose(&pGlobals->SupDrvIDC);
+ Log(("vboxNetAdpWinUnload: closed IDC, rc=0x%x\n", rc));
+ break;
+ }
+ if (pGlobals->hMiniportDriver)
+ NdisMDeregisterMiniportDriver(pGlobals->hMiniportDriver);
+ NdisFreeSpinLock(&pGlobals->Lock);
+ LogFlow(("<==vboxNetAdpWinUnload\n"));
+ RTLogDestroy(RTLogRelSetDefaultInstance(NULL));
+ RTLogDestroy(RTLogSetDefaultInstance(NULL));
+ RTR0Term();
+}
+
+
+/**
+ * register the miniport driver
+ */
+DECLHIDDEN(NDIS_STATUS) vboxNetAdpWinRegister(PDRIVER_OBJECT pDriverObject, PUNICODE_STRING pRegistryPathStr)
+{
+ NDIS_MINIPORT_DRIVER_CHARACTERISTICS MChars;
+
+ NdisZeroMemory(&MChars, sizeof (MChars));
+
+ MChars.Header.Type = NDIS_OBJECT_TYPE_MINIPORT_DRIVER_CHARACTERISTICS;
+ MChars.Header.Size = sizeof(NDIS_MINIPORT_DRIVER_CHARACTERISTICS);
+ MChars.Header.Revision = NDIS_MINIPORT_DRIVER_CHARACTERISTICS_REVISION_1;
+
+ MChars.MajorNdisVersion = VBOXNETADP_VERSION_NDIS_MAJOR;
+ MChars.MinorNdisVersion = VBOXNETADP_VERSION_NDIS_MINOR;
+
+ MChars.MajorDriverVersion = VBOXNETADP_VERSION_MAJOR;
+ MChars.MinorDriverVersion = VBOXNETADP_VERSION_MINOR;
+
+ MChars.InitializeHandlerEx = vboxNetAdpWinInitializeEx;
+ MChars.HaltHandlerEx = vboxNetAdpWinHaltEx;
+ MChars.UnloadHandler = vboxNetAdpWinUnload;
+ MChars.PauseHandler = vboxNetAdpWinPause;
+ MChars.RestartHandler = vboxNetAdpWinRestart;
+ MChars.OidRequestHandler = vboxNetAdpWinOidRequest;
+ MChars.SendNetBufferListsHandler = vboxNetAdpWinSendNetBufferLists;
+ MChars.ReturnNetBufferListsHandler = vboxNetAdpWinReturnNetBufferLists;
+ MChars.CancelSendHandler = vboxNetAdpWinCancelSend;
+ MChars.CheckForHangHandlerEx = vboxNetAdpWinCheckForHangEx;
+ MChars.ResetHandlerEx = vboxNetAdpWinResetEx;
+ MChars.DevicePnPEventNotifyHandler = vboxNetAdpWinDevicePnPEventNotify;
+ MChars.ShutdownHandlerEx = vboxNetAdpWinShutdownEx;
+ MChars.CancelOidRequestHandler = vboxNetAdpWinCancelOidRequest;
+
+ NDIS_STATUS Status;
+ g_VBoxNetAdpGlobals.hMiniportDriver = NULL;
+ Log(("vboxNetAdpWinRegister: registering miniport driver...\n"));
+ Status = NdisMRegisterMiniportDriver(pDriverObject,
+ pRegistryPathStr,
+ (NDIS_HANDLE)&g_VBoxNetAdpGlobals,
+ &MChars,
+ &g_VBoxNetAdpGlobals.hMiniportDriver);
+ Assert(Status == STATUS_SUCCESS);
+ if (Status == STATUS_SUCCESS)
+ {
+ Log(("vboxNetAdpWinRegister: successfully registered miniport driver; registering device...\n"));
+ }
+ else
+ {
+ Log(("ERROR! vboxNetAdpWinRegister: failed to register miniport driver, status=0x%x", Status));
+ }
+ return Status;
+}
+
+
+RT_C_DECLS_BEGIN
+
+NTSTATUS DriverEntry(IN PDRIVER_OBJECT pDriverObject, IN PUNICODE_STRING pRegistryPath);
+
+RT_C_DECLS_END
+
+NTSTATUS DriverEntry(IN PDRIVER_OBJECT pDriverObject, IN PUNICODE_STRING pRegistryPath)
+{
+ NDIS_STATUS Status = NDIS_STATUS_SUCCESS;
+ int rc;
+
+
+ rc = RTR0Init(0);
+ AssertRC(rc);
+ if (RT_SUCCESS(rc))
+ {
+ NdisZeroMemory(&g_VBoxNetAdpGlobals, sizeof (g_VBoxNetAdpGlobals));
+ RTListInit(&g_VBoxNetAdpGlobals.ListOfAdapters);
+ NdisAllocateSpinLock(&g_VBoxNetAdpGlobals.Lock);
+ NdisInitializeEvent(&g_VBoxNetAdpGlobals.EventUnloadAllowed);
+ //g_VBoxNetAdpGlobals.PMCaps.WakeUpCapabilities.Flags = NDIS_DEVICE_WAKE_UP_ENABLE;
+ g_VBoxNetAdpGlobals.PMCaps.WakeUpCapabilities.MinMagicPacketWakeUp = NdisDeviceStateUnspecified;
+ g_VBoxNetAdpGlobals.PMCaps.WakeUpCapabilities.MinPatternWakeUp = NdisDeviceStateUnspecified;
+
+ /* Initialize SupDrv interface */
+ g_VBoxNetAdpGlobals.SupDrvFactory.pfnQueryFactoryInterface = vboxNetAdpWinQueryFactoryInterface;
+ memcpy(g_VBoxNetAdpGlobals.SupDrvFactory.szName, "VBoxNetAdp", sizeof("VBoxNetAdp"));
+ /* Initialize trunk factory interface */
+ g_VBoxNetAdpGlobals.TrunkFactory.pfnRelease = vboxNetAdpWinFactoryRelease;
+ g_VBoxNetAdpGlobals.TrunkFactory.pfnCreateAndConnect = vboxNetAdpWinFactoryCreateAndConnect;
+
+ rc = vboxNetAdpWinStartInitIdcThread(&g_VBoxNetAdpGlobals);
+ if (RT_SUCCESS(rc))
+ {
+ Status = vboxNetAdpWinRegister(pDriverObject, pRegistryPath);
+ Assert(Status == STATUS_SUCCESS);
+ if (Status == NDIS_STATUS_SUCCESS)
+ {
+ Log(("NETADP: started successfully\n"));
+ return STATUS_SUCCESS;
+ }
+ }
+ else
+ Status = NDIS_STATUS_FAILURE;
+ NdisFreeSpinLock(&g_VBoxNetAdpGlobals.Lock);
+ RTLogDestroy(RTLogRelSetDefaultInstance(NULL));
+ RTLogDestroy(RTLogSetDefaultInstance(NULL));
+
+ RTR0Term();
+ }
+ else
+ {
+ Status = NDIS_STATUS_FAILURE;
+ }
+
+ return Status;
+}
+
diff --git a/src/VBox/HostDrivers/VBoxNetAdp/win/VBoxNetAdp-win.h b/src/VBox/HostDrivers/VBoxNetAdp/win/VBoxNetAdp-win.h
new file mode 100644
index 00000000..8d9d15a5
--- /dev/null
+++ b/src/VBox/HostDrivers/VBoxNetAdp/win/VBoxNetAdp-win.h
@@ -0,0 +1,71 @@
+/* $Id: VBoxNetAdp-win.h $ */
+/** @file
+ * VBoxNetAdp-win.h - Host-only Miniport Driver, Windows-specific code.
+ */
+/*
+ * Copyright (C) 2014-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+#ifndef VBOX_INCLUDED_SRC_VBoxNetAdp_win_VBoxNetAdp_win_h
+#define VBOX_INCLUDED_SRC_VBoxNetAdp_win_VBoxNetAdp_win_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+#define VBOXNETADP_VERSION_NDIS_MAJOR 6
+#define VBOXNETADP_VERSION_NDIS_MINOR 0
+
+#define VBOXNETADP_VERSION_MAJOR 1
+#define VBOXNETADP_VERSION_MINOR 0
+
+#define VBOXNETADP_VENDOR_NAME "Oracle"
+#define VBOXNETADP_VENDOR_ID 0xFFFFFF
+#define VBOXNETADP_MCAST_LIST_SIZE 32
+#define VBOXNETADP_MAX_FRAME_SIZE 1518 // TODO: 14+4+1500
+
+//#define VBOXNETADP_NAME_UNIQUE L"{7af6b074-048d-4444-bfce-1ecc8bc5cb76}"
+#define VBOXNETADP_NAME_SERVICE L"VBoxNetAdp"
+
+#define VBOXNETADP_NAME_LINK L"\\DosDevices\\Global\\VBoxNetAdp"
+#define VBOXNETADP_NAME_DEVICE L"\\Device\\VBoxNetAdp"
+
+#define VBOXNETADPWIN_TAG 'ANBV'
+
+#define VBOXNETADPWIN_ATTR_FLAGS NDIS_MINIPORT_ATTRIBUTES_NDIS_WDM | NDIS_MINIPORT_ATTRIBUTES_NO_HALT_ON_SUSPEND
+#define VBOXNETADP_MAC_OPTIONS NDIS_MAC_OPTION_NO_LOOPBACK
+#define VBOXNETADP_SUPPORTED_FILTERS (NDIS_PACKET_TYPE_DIRECTED | \
+ NDIS_PACKET_TYPE_MULTICAST | \
+ NDIS_PACKET_TYPE_BROADCAST | \
+ NDIS_PACKET_TYPE_PROMISCUOUS | \
+ NDIS_PACKET_TYPE_ALL_MULTICAST)
+#define VBOXNETADPWIN_SUPPORTED_STATISTICS 0 //TODO!
+#define VBOXNETADPWIN_HANG_CHECK_TIME 4
+
+#endif /* !VBOX_INCLUDED_SRC_VBoxNetAdp_win_VBoxNetAdp_win_h */
diff --git a/src/VBox/HostDrivers/VBoxNetAdp/win/VBoxNetAdp-win.rc b/src/VBox/HostDrivers/VBoxNetAdp/win/VBoxNetAdp-win.rc
new file mode 100644
index 00000000..61cdb6e7
--- /dev/null
+++ b/src/VBox/HostDrivers/VBoxNetAdp/win/VBoxNetAdp-win.rc
@@ -0,0 +1,72 @@
+/* $Id: VBoxNetAdp-win.rc $ */
+/** @file
+ * VBoxNetAdp6 - Resource file containing version info and icon.
+ */
+/*
+ * Copyright (C) 2014-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+#include <windows.h>
+#include <VBox/version.h>
+
+#define DESCRIPTION_STR "VirtualBox NDIS 6.0 Host-Only Network Adapter Driver\0"
+#define FILENAME_STR "VBoxNetAdp6"
+
+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_NETWORK
+BEGIN
+ BLOCK "StringFileInfo"
+ BEGIN
+ BLOCK "040904b0"
+ BEGIN
+ VALUE "FileDescription", DESCRIPTION_STR
+ VALUE "InternalName", FILENAME_STR "\0"
+ VALUE "OriginalFilename", FILENAME_STR ".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_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/HostDrivers/VBoxNetAdp/win/VBoxNetAdp6.inf b/src/VBox/HostDrivers/VBoxNetAdp/win/VBoxNetAdp6.inf
new file mode 100644
index 00000000..6c18bf6a
--- /dev/null
+++ b/src/VBox/HostDrivers/VBoxNetAdp/win/VBoxNetAdp6.inf
@@ -0,0 +1,101 @@
+; $Id: VBoxNetAdp6.inf $
+;; @file
+; VBoxNetAdp6.inf - VirtualBox NDIS6 Miniport Driver inf file
+;
+; Note: We use the same component id as the old NetAdp implementation ?
+;
+
+;
+; Copyright (C) 2014-2023 Oracle and/or its affiliates.
+;
+; This file is part of VirtualBox base platform packages, as
+; available from https://www.virtualbox.org.
+;
+; This program is free software; you can redistribute it and/or
+; modify it under the terms of the GNU General Public License
+; as published by the Free Software Foundation, in version 3 of the
+; License.
+;
+; This program is distributed in the hope that it will be useful, but
+; WITHOUT ANY WARRANTY; without even the implied warranty of
+; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+; General Public License for more details.
+;
+; You should have received a copy of the GNU General Public License
+; along with this program; if not, see <https://www.gnu.org/licenses>.
+;
+; The contents of this file may alternatively be used under the terms
+; of the Common Development and Distribution License Version 1.0
+; (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+; in the VirtualBox distribution, in which case the provisions of the
+; CDDL are applicable instead of those of the GPL.
+;
+; You may elect to license modified versions of this file under the
+; terms and conditions of either the GPL or the CDDL or both.
+;
+; SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+;
+
+[Version]
+Signature = "$Windows NT$"
+;cat CatalogFile = VBoxNetAdp6.cat
+Class = Net
+ClassGUID = {4D36E972-E325-11CE-BFC1-08002BE10318}
+Provider = %ORACLE%
+;edit-DriverVer=10/23/2014,1.0.1.0
+
+
+[Manufacturer]
+%ORACLE% = VBoxNetAdp6@COMMA-NT-ARCH@
+
+[ControlFlags]
+
+[VBoxNetAdp6@DOT-NT-ARCH@]
+%VBoxNetAdp6_Desc% = VBoxNetAdp6.ndi, sun_VBoxNetAdp
+
+[VBoxNetAdp6.ndi]
+AddReg = VBoxNetAdp6.ndi.AddReg
+Characteristics = 0x1 ; NCF_VIRTUAL
+*IfType = 0x6 ; IF_TYPE_ETHERNET_CSMACD
+*MediaType = 0x0 ; NdisMedium802_3
+*PhysicalMediaType = 14 ; NdisPhysicalMedium802_3
+CopyFiles = VBoxNetAdp6.Files.Sys
+
+[VBoxNetAdp6.ndi.Services]
+AddService = VBoxNetAdp, 2, VBoxNetAdp6.AddService
+
+[VBoxNetAdp6.AddService]
+DisplayName = %VBoxNetAdp6Service_Desc%
+ServiceType = 1 ;SERVICE_KERNEL_DRIVER
+StartType = 3 ;SERVICE_DEMAND_START
+ErrorControl = 1 ;SERVICE_ERROR_NORMAL
+ServiceBinary = %12%\VBoxNetAdp6.sys
+LoadOrderGroup = NDIS
+
+[SourceDisksNames]
+1=%DiskDescription%,"",,
+
+[SourceDisksFiles]
+VBoxNetAdp6.sys=1
+
+[DestinationDirs]
+DefaultDestDir = 12
+VBoxNetAdp6.Files.Sys = 12 ; %windir%\System32\drivers
+
+[VBoxNetAdp6.Files.Sys]
+VBoxNetAdp6.sys,,,2
+
+[VBoxNetAdp6.ndi.AddReg]
+HKR, , *NdisDeviceType, 0x00010001, 1 ; NDIS_DEVICE_TYPE_ENDPOINT
+HKR, , BusNumber, 0, "0"
+HKR, Ndi, Service, 0, VBoxNetAdp
+HKR, Ndi, HelpText, , %VBoxNetAdp6_HELP%
+HKR, Ndi\Interfaces, UpperRange, 0, ndis5
+HKR, Ndi\Interfaces, LowerRange, 0, ethernet
+
+[Strings]
+ORACLE = "Oracle Corporation"
+DiskDescription = "VirtualBox NDIS 6.0 Miniport Driver"
+VBoxNetAdp6_Desc = "VirtualBox Host-Only Ethernet Adapter"
+VBoxNetAdp6_HELP = "VirtualBox NDIS 6.0 Miniport Driver"
+VBoxNetAdp6Service_Desc = "VirtualBox NDIS 6.0 Miniport Service"
diff --git a/src/VBox/HostDrivers/VBoxNetFlt/Makefile.kmk b/src/VBox/HostDrivers/VBoxNetFlt/Makefile.kmk
new file mode 100644
index 00000000..e2704459
--- /dev/null
+++ b/src/VBox/HostDrivers/VBoxNetFlt/Makefile.kmk
@@ -0,0 +1,556 @@
+# $Id: Makefile.kmk $
+## @file
+# Sub-Makefile for the Network Filter Driver (VBoxNetFlt).
+#
+
+#
+# Copyright (C) 2008-2023 Oracle and/or its affiliates.
+#
+# This file is part of VirtualBox base platform packages, as
+# available from https://www.virtualbox.org.
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation, in version 3 of the
+# License.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, see <https://www.gnu.org/licenses>.
+#
+# The contents of this file may alternatively be used under the terms
+# of the Common Development and Distribution License Version 1.0
+# (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+# in the VirtualBox distribution, in which case the provisions of the
+# CDDL are applicable instead of those of the GPL.
+#
+# You may elect to license modified versions of this file under the
+# terms and conditions of either the GPL or the CDDL or both.
+#
+# SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+#
+
+SUB_DEPTH = ../../../..
+include $(KBUILD_PATH)/subheader.kmk
+
+
+if1of ($(KBUILD_TARGET), darwin win) # this ifeq must go, see @todo around elif
+ #
+ # VBoxNetFlt.sys - The mixed case driver.
+ #
+ ifdef VBOX_WITH_VBOXDRV
+ SYSMODS += VBoxNetFlt
+ endif
+ VBoxNetFlt_TEMPLATE = VBoxR0Drv
+ VBoxNetFlt_INST = $(INST_VBOXNETFLT)$(if $(eq $(KBUILD_TARGET),darwin),Contents/MacOS/)
+ VBoxNetFlt_DEBUG_INST.darwin = $(patsubst %/,%,$(INST_VBOXNETFLT))
+ if defined(VBOX_SIGNING_MODE) && "$(KBUILD_TARGET)" == "win"
+ VBoxNetFlt_INSTTYPE = none
+ VBoxNetFlt_DEBUG_INSTTYPE = both
+ endif
+ VBoxNetFlt_DEFS = IN_RT_R0 IN_SUP_STATIC
+ VBoxNetFlt_INCS = .
+ VBoxNetFlt_SOURCES.darwin = \
+ darwin/VBoxNetFlt-darwin.cpp
+ VBoxNetFlt_SDKS.win = ReorderCompilerIncs $(VBOX_WINDDK_WLH) $(VBOX_WINPSDK_INCS)
+ VBoxNetFlt_SOURCES.win = \
+ win/drv/VBoxNetFltP-win.cpp \
+ win/drv/VBoxNetFltM-win.cpp \
+ win/drv/VBoxNetFltRt-win.cpp \
+ win/drv/VBoxNetFlt-win.rc
+ # with WINDDKWLH the WIN9X_COMPAT_SPINLOCK is needed to avoid inline declaration of KeInitializeSpinLock
+ # otherwise the linker would complain about dumplicate _KeInitializeSpinLock@4 definition
+ # in ntoskrnl.lib and our object files
+ VBoxNetFlt_DEFS.win += WIN9X_COMPAT_SPINLOCK=1
+ VBoxNetFlt_DEFS.win += VBOXNETFLT_STATIC_CONFIG
+ VBoxNetFlt_DEFS.win += VBOXNETFLT_NO_PACKET_QUEUE
+ VBoxNetFlt_DEFS.win += NDIS_MINIPORT_DRIVER NDIS_WDM=1 BINARY_COMPATIBLE=0
+ VBoxNetFlt_DEFS.win += NDIS50_MINIPORT=1 NDIS50=1
+ ifdef VBOX_LOOPBACK_USEFLAGS
+ VBoxNetFlt_DEFS.win += VBOX_LOOPBACK_USEFLAGS
+ endif
+ VBoxNetFlt_SOURCES = VBoxNetFlt.c
+ #VBoxNetFlt_LDFLAGS.darwin = -v -Wl,-whyload -Wl,-v -Wl,-whatsloaded
+ VBoxNetFlt_LDFLAGS.win.x86 = -Entry:DriverEntry@8
+ VBoxNetFlt_LDFLAGS.win.amd64 = -Entry:DriverEntry
+ VBoxNetFlt_LIBS.win = \
+ $(PATH_SDK_$(VBOX_WINDDK_WLH)_LIB)/ntoskrnl.lib \
+ $(PATH_SDK_$(VBOX_WINDDK_WLH)_LIB)/hal.lib \
+ $(PATH_SDK_$(VBOX_WINDDK_WLH)_LIB)/ndis.lib \
+ $(PATH_SDK_$(VBOX_WINDDK_WLH)_LIB)/tdi.lib \
+ $(PATH_STAGE_LIB)/RuntimeR0Drv$(VBOX_SUFF_LIB)
+ VBoxNetFlt_LIBS = \
+ $(PATH_STAGE_LIB)/SUPR0IdcClient$(VBOX_SUFF_LIB)
+
+ darwin/VBoxNetFlt-darwin.cpp_DEFS = VBOX_SVN_REV=$(VBOX_SVN_REV)
+
+
+ # Darwin extras.
+ if "$(KBUILD_TARGET)" == "darwin" && defined(VBOX_WITH_VBOXDRV)
+ INSTALLS += VBoxNetFlt.kext
+ VBoxNetFlt.kext_INST = $(INST_VBOXNETFLT)Contents/
+ VBoxNetFlt.kext_SOURCES = $(VBoxNetFlt.kext_0_OUTDIR)/Contents/Info.plist
+ VBoxNetFlt.kext_CLEAN = $(VBoxNetFlt.kext_0_OUTDIR)/Contents/Info.plist
+ VBoxNetFlt.kext_BLDDIRS = $(VBoxNetFlt.kext_0_OUTDIR)/Contents/
+
+ $$(VBoxNetFlt.kext_0_OUTDIR)/Contents/Info.plist: $(PATH_SUB_CURRENT)/darwin/Info.plist $(VBOX_VERSION_MK) | $$(dir $$@)
+ $(call MSG_GENERATE,VBoxNetFlt,$@,$<)
+ $(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,VBoxNetFlt)
+
+ INSTALLS.darwin += Scripts-darwin
+ Scripts-darwin_INST = $(INST_DIST)
+ Scripts-darwin_EXEC_SOURCES = \
+ darwin/loadnetflt.sh
+ endif # darwin && host-drivers
+
+
+ ifeq ($(KBUILD_TARGET),win)
+ #
+ # Windows extras.
+ #
+ INSTALLS.win += VBoxNetFlt-inf
+ VBoxNetFlt-inf_TEMPLATE = VBoxR0DrvInfCat
+ VBoxNetFlt-inf_SOURCES = \
+ $(PATH_TARGET)/VBoxNetFltCat.dir/VBoxNetFlt.inf \
+ $(PATH_TARGET)/VBoxNetFltCat.dir/VBoxNetFltM.inf
+ VBoxNetFlt-inf_CLEAN = $(VBoxNetFlt-inf_SOURCES)
+ VBoxNetFlt-inf_BLDDIRS = $(PATH_TARGET)/VBoxNetFltCat.dir
+
+ $(PATH_TARGET)/VBoxNetFltCat.dir/VBoxNetFlt.inf: $(PATH_SUB_CURRENT)/win/drv/VBoxNetFlt.inf $(MAKEFILE_CURRENT) | $$(dir $$@)
+ $(call MSG_GENERATE,VBoxNetFlt-inf,$@,$<)
+ $(call VBOX_EDIT_INF_FN,$<,$@)
+
+ $(PATH_TARGET)/VBoxNetFltCat.dir/VBoxNetFltM.inf: $(PATH_SUB_CURRENT)/win/drv/VBoxNetFltM.inf $(MAKEFILE_CURRENT) | $$(dir $$@)
+ $(call MSG_GENERATE,VBoxNetFlt-inf,$@,$<)
+ $(call VBOX_EDIT_INF_FN,$<,$@)
+
+ ifdef VBOX_SIGNING_MODE
+ VBoxNetFlt-inf_SOURCES += \
+ $(PATH_TARGET)/VBoxNetFltCat.dir/VBoxNetFlt.sys \
+ $(PATH_TARGET)/VBoxNetFltCat.dir/VBoxNetFltNobj.dll \
+ $(PATH_TARGET)/VBoxNetFltCat.dir/VBoxNetFlt.cat
+
+ $(PATH_TARGET)/VBoxNetFltCat.dir/VBoxNetFlt.sys: $$(VBoxNetFlt_1_TARGET) | $$(dir $$@)
+ $(INSTALL) -m 644 $< $(@D)
+
+ $(PATH_TARGET)/VBoxNetFltCat.dir/VBoxNetFltNobj.dll: $$(VBoxNetFltNobj_1_TARGET) | $$(dir $$@)
+ $(INSTALL) -m 644 $< $(@D)
+
+ $(PATH_TARGET)/VBoxNetFltCat.dir/VBoxNetFlt.cat: \
+ $(PATH_TARGET)/VBoxNetFltCat.dir/VBoxNetFlt.sys \
+ $(PATH_TARGET)/VBoxNetFltCat.dir/VBoxNetFltNobj.dll \
+ $(PATH_TARGET)/VBoxNetFltCat.dir/VBoxNetFlt.inf \
+ $(PATH_TARGET)/VBoxNetFltCat.dir/VBoxNetFltM.inf
+ $(call MSG_TOOL,Inf2Cat,VBoxNetFlt-inf,$@,$<)
+ $(call VBOX_MAKE_CAT_FN, $(@D),$@)
+
+ endif # signing
+
+
+ #
+ # VBoxNetLwf.sys - The light-weight filter driver for NDIS6.
+ #
+ SYSMODS += VBoxNetLwf
+ VBoxNetLwf_TEMPLATE = VBoxR0Drv
+ VBoxNetLwf_INST = $(INST_VBOXNETFLT)
+ if defined(VBOX_SIGNING_MODE)
+ VBoxNetLwf_INSTTYPE = none
+ VBoxNetLwf_DEBUG_INSTTYPE = both
+ endif
+ VBoxNetLwf_DEFS = IN_RT_R0 IN_SUP_STATIC
+ VBoxNetLwf_INCS = .
+ VBoxNetLwf_SOURCES = VBoxNetFlt.c VBoxNetFlt.rc
+ VBoxNetLwf_SDKS.win = ReorderCompilerIncs $(VBOX_WINDDK_WLH) $(VBOX_WINPSDK_INCS)
+ VBoxNetLwf_SOURCES.win = \
+ win/drv/VBoxNetLwf-win.cpp
+ # with WINDDKWLH the WIN9X_COMPAT_SPINLOCK is needed to avoid inline declaration of KeInitializeSpinLock
+ # otherwise the linker would complain about dumplicate _KeInitializeSpinLock@4 definition
+ # in ntoskrnl.lib and our object files
+ VBoxNetLwf_DEFS.win += WIN9X_COMPAT_SPINLOCK=1 NDISLWF=1 DBG=1
+ # VBoxNetLwf_DEFS.win += VBOXNETFLT_STATIC_CONFIG
+ # VBoxNetLwf_DEFS.win += VBOXNETFLT_NO_PACKET_QUEUE
+ # VBoxNetLwf_DEFS.win += NDIS_MINIPORT_DRIVER NDIS_WDM=1 BINARY_COMPATIBLE=0
+ VBoxNetLwf_DEFS.win += NDIS60=1
+ # ifdef VBOX_LOOPBACK_USEFLAGS
+ # VBoxNetLwf_DEFS.win += VBOX_LOOPBACK_USEFLAGS
+ # endif
+ # VBoxNetLwf_SOURCES = VBoxNetLwf.c
+ VBoxNetLwf_LDFLAGS.win.x86 = -Entry:DriverEntry@8
+ VBoxNetLwf_LDFLAGS.win.amd64 = -Entry:DriverEntry
+ VBoxNetLwf_LIBS.win = \
+ $(PATH_SDK_$(VBOX_WINDDK_WLH)_LIB)/ntoskrnl.lib \
+ $(PATH_SDK_$(VBOX_WINDDK_WLH)_LIB)/hal.lib \
+ $(PATH_SDK_$(VBOX_WINDDK_WLH)_LIB)/ndis.lib \
+ $(PATH_SDK_$(VBOX_WINDDK_WLH)_LIB)/netio.lib \
+ $(PATH_STAGE_LIB)/RuntimeR0Drv$(VBOX_SUFF_LIB)
+ VBoxNetLwf_LIBS = \
+ $(PATH_STAGE_LIB)/SUPR0IdcClient$(VBOX_SUFF_LIB)
+ #
+ # VBoxNetLwf installation.
+ #
+ INSTALLS.win += VBoxNetLwf-inf
+ VBoxNetLwf-inf_TEMPLATE = VBoxR0DrvInfCat
+ VBoxNetLwf-inf_SOURCES = \
+ $(PATH_TARGET)/VBoxNetLwfCat.dir/VBoxNetLwf.inf
+ VBoxNetLwf-inf_CLEAN = $(VBoxNetLwf-inf_SOURCES)
+ VBoxNetLwf-inf_BLDDIRS = $(PATH_TARGET)/VBoxNetLwfCat.dir
+
+ $(PATH_TARGET)/VBoxNetLwfCat.dir/VBoxNetLwf.inf: $(PATH_SUB_CURRENT)/win/drv/VBoxNetLwf.inf $(MAKEFILE_CURRENT) | $$(dir $$@)
+ $(call MSG_GENERATE,VBoxNetLwf-inf,$@,$<)
+ $(call VBOX_EDIT_INF_FN,$<,$@)
+
+ ifdef VBOX_SIGNING_MODE
+ VBoxNetLwf-inf_SOURCES += \
+ $(PATH_TARGET)/VBoxNetLwfCat.dir/VBoxNetLwf.sys \
+ $(PATH_TARGET)/VBoxNetLwfCat.dir/VBoxNetLwf.cat \
+ $(PATH_TARGET)/VBoxNetLwfCat.dir/VBoxNetLwf.cat=>VBoxNetLwf-PreW10.cat
+
+ # $(PATH_TARGET)/VBoxNetLwfCat.dir/VBoxNetLwfNobj.dll \
+
+ $(PATH_TARGET)/VBoxNetLwfCat.dir/VBoxNetLwf.sys: $$(VBoxNetLwf_1_TARGET) | $$(dir $$@)
+ $(INSTALL) -m 644 $< $(@D)
+
+ #$(PATH_TARGET)/VBoxNetLwfCat.dir/VBoxNetLwfNobj.dll: $$(VBoxNetLwfNobj_1_TARGET) | $$(dir $$@)
+ # $(INSTALL) -m 644 $< $(@D)
+
+ $(PATH_TARGET)/VBoxNetLwfCat.dir/VBoxNetLwf.cat: \
+ $(PATH_TARGET)/VBoxNetLwfCat.dir/VBoxNetLwf.sys \
+ $(PATH_TARGET)/VBoxNetLwfCat.dir/VBoxNetLwf.inf
+ $(call MSG_TOOL,Inf2Cat,VBoxNetLwf-inf,$@,$<)
+ $(call VBOX_MAKE_CAT_FN, $(@D),$@)
+
+ endif # signing
+
+
+
+ #
+ # WinNetConfig - static library with host network interface config API (for the installer)
+ #
+ LIBRARIES.win += WinNetConfigSharedStatic
+ WinNetConfigSharedStatic_TEMPLATE = VBoxR3StaticDllNoAsan
+ WinNetConfigSharedStatic_SDKS = ReorderCompilerIncs $(VBOX_WINPSDK) $(VBOX_WINDDK)
+ WinNetConfigSharedStatic_DEFS = _UNICODE UNICODE
+ WinNetConfigSharedStatic_SOURCES = \
+ win/cfg/VBoxNetCfg.cpp \
+ ../../Main/glue/string.cpp
+
+ # Version for DLLs:
+ LIBRARIES.win += WinNetConfigDll
+ WinNetConfigDll_TEMPLATE = VBoxR3Dll
+ WinNetConfigDll_EXTENDS = WinNetConfigSharedStatic
+
+ # Version for EXEs:
+ LIBRARIES.win += WinNetConfigExe
+ WinNetConfigExe_TEMPLATE = VBoxR3Exe
+ WinNetConfigExe_EXTENDS = WinNetConfigSharedStatic
+
+
+ #
+ # Template for NetFltInstall and friends.
+ #
+ TEMPLATE_VBoxNetFltR3 = Template for NetFltInstall, NetFltUninstall, NetAdpInstall, NetAdpUninstall, ++
+ TEMPLATE_VBoxNetFltR3_EXTENDS = VBoxR3Exe
+ TEMPLATE_VBoxNetFltR3_SDKS = $(TEMPLATE_VBoxR3Exe_SDKS) ReorderCompilerIncs $(VBOX_WINPSDK) $(VBOX_WINDDK) VBoxNtDll VBoxWinNewDevLib
+ TEMPLATE_VBoxNetFltR3_LIBS = $(TEMPLATE_VBoxR3Exe_LIBS) \
+ $(WinNetConfigExe_1_TARGET) \
+ $(PATH_STAGE_LIB)/VBoxDrvCfgExe$(VBOX_SUFF_LIB) \
+ $(LIB_RUNTIME) \
+ $(PATH_TOOL_$(VBOX_VCC_TOOL)_LIB)/comsupp.lib \
+ $(PATH_SDK_$(VBOX_WINPSDK)_LIB)/WbemUuid.Lib
+
+ #
+ # NetFltInstall
+ #
+ PROGRAMS.win += NetFltInstall
+ NetFltInstall_TEMPLATE = VBoxNetFltR3
+ NetFltInstall_SOURCES = win/tools/VBoxNetFltInstall.cpp
+
+ #
+ # NetFltUninstall
+ #
+ PROGRAMS.win += NetFltUninstall
+ NetFltUninstall_TEMPLATE = VBoxNetFltR3
+ NetFltUninstall_SOURCES = win/tools/VBoxNetFltUninstall.cpp
+
+ #
+ # NetAdpInstall
+ #
+ PROGRAMS.win += NetAdpInstall
+ NetAdpInstall_TEMPLATE = VBoxNetFltR3
+ NetAdpInstall_SOURCES = win/tools/VBoxNetAdpInstall.cpp
+
+ #
+ # NetAdpUninstall
+ #
+ PROGRAMS.win += NetAdpUninstall
+ NetAdpUninstall_TEMPLATE = VBoxNetFltR3
+ NetAdpUninstall_SOURCES = win/tools/VBoxNetAdpUninstall.cpp
+
+ #
+ # NetAdp6Install
+ #
+ PROGRAMS.win += NetAdp6Install
+ NetAdp6Install_TEMPLATE = VBoxNetFltR3
+ NetAdp6Install_SOURCES = win/tools/VBoxNetAdpInstall.cpp
+ NetAdp6Install_DEFS = NDIS60=1
+
+ #
+ # NetAdp6Uninstall
+ #
+ PROGRAMS.win += NetAdp6Uninstall
+ NetAdp6Uninstall_TEMPLATE = VBoxNetFltR3
+ NetAdp6Uninstall_SOURCES = win/tools/VBoxNetAdpUninstall.cpp
+ NetAdp6Uninstall_DEFS = NDIS60=1
+
+ #
+ # NetLwfInstall
+ #
+ PROGRAMS.win += NetLwfInstall
+ NetLwfInstall_TEMPLATE = VBoxNetFltR3
+ NetLwfInstall_SOURCES = win/tools/VBoxNetLwfInstall.cpp
+
+ #
+ # NetLwfUninstall
+ #
+ PROGRAMS.win += NetLwfUninstall
+ NetLwfUninstall_TEMPLATE = VBoxNetFltR3
+ NetLwfUninstall_SOURCES = win/tools/VBoxNetLwfUninstall.cpp
+
+ #
+ # VBoxNetFltNobj
+ #
+ DLLS.win += VBoxNetFltNobj
+ VBoxNetFltNobj_TEMPLATE = VBoxR3StaticDll
+ if defined(VBOX_SIGNING_MODE)
+ VBoxNetFltNobj_INSTTYPE = none
+ VBoxNetFltNobj_DEBUG_INSTTYPE = both
+ endif
+ VBoxNetFltNobj_SDKS = ReorderCompilerIncs $(VBOX_WINPSDK) $(VBOX_WINDDK)
+ VBoxNetFltNobj_DEFS = WIN32 _ATL_STATIC_REGISTRY
+ VBoxNetFltNobj_INCS = \
+ $(VBoxNetFltNobj_0_OUTDIR)
+ VBoxNetFltNobj_SOURCES = \
+ win/nobj/VBoxNetFltNobj.cpp \
+ win/nobj/VBoxNetFltNobj.def \
+ win/nobj/VBoxNetFltNobj.rc
+ #VBoxNetFltNobj_INTERMEDIATES =
+ VBoxNetFltNobj_DEPS = \
+ $(VBoxNetFltNobj_0_OUTDIR)/VBoxNetFltNobjT_i.c \
+ $(VBoxNetFltNobj_0_OUTDIR)/VBoxNetFltNobjT_p.c \
+ $(VBoxNetFltNobj_0_OUTDIR)/VBoxNetFltNobjT.h \
+ $(VBoxNetFltNobj_0_OUTDIR)/dlldata.c \
+ $(VBoxNetFltNobj_0_OUTDIR)/VBoxNetFltNobjT.tlb
+ VBoxNetFltNobj_CLEAN = $(VBoxNetFltNobj_DEPS)
+
+ $$(VBoxNetFltNobj_0_OUTDIR)/VBoxNetFltNobjT_i.c \
+ + $$(VBoxNetFltNobj_0_OUTDIR)/VBoxNetFltNobjT_p.c \
+ + $$(VBoxNetFltNobj_0_OUTDIR)/VBoxNetFltNobjT.h \
+ + $$(VBoxNetFltNobj_0_OUTDIR)/dlldata.c \
+ + $$(VBoxNetFltNobj_0_OUTDIR)/VBoxNetFltNobjT.tlb: \
+ $(PATH_SUB_CURRENT)/win/nobj/VBoxNetFltNobjT.idl \
+ | $$(dir $$@)
+ $(VBOX_MIDL_REDIRECT) $(VBOX_WIN_MIDL) /nologo \
+ /out $(call VBOX_FN_MAKE_WIN_PATH,$(VBoxNetFltNobj_0_OUTDIR)) \
+ /cpp_cmd $(VBOX_MIDL_CPP_CMD) \
+ $(qforeachfile unq, incdir, $(SDK_$(VBOX_WINDDK)_INCS) $(SDK_$(VBOX_WINPSDK)_INCS),/I $(quote-sh $(incdir))) \
+ $(call VBOX_FN_MAKE_WIN_PATH,$<)
+ $(call def_VBoxMidlOutputDisableMscWarnings,$(VBoxNetFltNobj_0_OUTDIR)/VBoxNetFltNobjT.h)
+ $(call def_VBoxMidlOutputDisableMscWarnings,$(VBoxNetFltNobj_0_OUTDIR)/VBoxNetFltNobjT_i.c)
+ $(call def_VBoxMidlOutputDisableMscWarnings,$(VBoxNetFltNobj_0_OUTDIR)/VBoxNetFltNobjT_p.c)
+
+
+ #
+ # VBoxNetAdp.sys - The VirtualBox Adapter miniport driver.
+ #
+ SYSMODS.win += VBoxNetAdp
+ VBoxNetAdp_TEMPLATE = VBoxR0Drv
+ #VBoxNetAdp_INST = $(INST_VBOXNETADP)
+ if defined(VBOX_SIGNING_MODE)
+ VBoxNetAdp_INSTTYPE.win = none
+ VBoxNetAdp_DEBUG_INSTTYPE.win = both
+ endif
+ VBoxNetAdp_DEFS = IN_RT_R0 IN_SUP_STATIC
+ VBoxNetAdp_INCS := $(PATH_SUB_CURRENT)
+ VBoxNetAdp_SDKS = ReorderCompilerIncs $(VBOX_WINDDK_WLH) $(VBOX_WINPSDK_INCS)
+ VBoxNetAdp_SOURCES = \
+ VBoxNetFlt.c \
+ win/drv/VBoxNetFltM-win.cpp \
+ win/drv/VBoxNetFltRt-win.cpp \
+ win/drv/VBoxNetFlt-win.rc
+ VBoxNetAdp_DEFS += VBOXNETFLT_STATIC_CONFIG VBOXNETADP
+ VBoxNetAdp_DEFS.win += VBOXNETFLT_NO_PACKET_QUEUE
+ VBoxNetAdp_DEFS += NDIS_MINIPORT_DRIVER NDIS_WDM=1 BINARY_COMPATIBLE=0
+ VBoxNetAdp_DEFS += NDIS50_MINIPORT=1 NDIS50=1
+ VBoxNetAdp_LDFLAGS.win.x86 = -Entry:DriverEntry@8
+ VBoxNetAdp_LDFLAGS.win.amd64 = -Entry:DriverEntry
+ VBoxNetAdp_LIBS.win = \
+ $(PATH_SDK_$(VBOX_WINDDK)_LIB)/ntoskrnl.lib \
+ $(PATH_SDK_$(VBOX_WINDDK)_LIB)/hal.lib \
+ $(PATH_SDK_$(VBOX_WINDDK)_LIB)/ndis.lib \
+ $(PATH_STAGE_LIB)/RuntimeR0Drv$(VBOX_SUFF_LIB)
+ VBoxNetAdp_LIBS = \
+ $(PATH_STAGE_LIB)/SUPR0IdcClient$(VBOX_SUFF_LIB)
+
+
+ INSTALLS.win += VBoxNetAdp-inf
+ VBoxNetAdp-inf_TEMPLATE = VBoxR0DrvInfCat
+ VBoxNetAdp-inf_SOURCES = \
+ $(PATH_TARGET)/VBoxNetAdpCat.dir/VBoxNetAdp.inf
+ VBoxNetAdp-inf_CLEAN = $(VBoxNetAdp-inf_SOURCES)
+ VBoxNetAdp-inf_BLDDIRS = $(PATH_TARGET)/VBoxNetAdpCat.dir
+
+ $(PATH_TARGET)/VBoxNetAdpCat.dir/VBoxNetAdp.inf: $(PATH_SUB_CURRENT)/win/drv/VBoxNetAdp.inf $(MAKEFILE_CURRENT) | $$(dir $$@)
+ $(call MSG_GENERATE,VBoxNetAdp-inf,$@,$<)
+ $(call VBOX_EDIT_INF_FN,$<,$@)
+
+ ifdef VBOX_SIGNING_MODE
+ VBoxNetAdp-inf_SOURCES += \
+ $(PATH_TARGET)/VBoxNetAdpCat.dir/VBoxNetAdp.sys \
+ $(PATH_TARGET)/VBoxNetAdpCat.dir/VBoxNetAdp.cat
+
+ $(PATH_TARGET)/VBoxNetAdpCat.dir/VBoxNetAdp.sys: $$(VBoxNetAdp_1_TARGET) | $$(dir $$@)
+ $(INSTALL) -m 644 $< $(@D)
+
+ $(PATH_TARGET)/VBoxNetAdpCat.dir/VBoxNetAdp.cat: \
+ $(PATH_TARGET)/VBoxNetAdpCat.dir/VBoxNetAdp.sys \
+ $(PATH_TARGET)/VBoxNetAdpCat.dir/VBoxNetAdp.inf
+ $(call MSG_TOOL,Inf2Cat,VBoxNetFlt-inf,$@,$<)
+ $(call VBOX_MAKE_CAT_FN, $(@D),$@)
+
+ endif # ifdef VBOX_SIGNING_MODE
+
+ endif #ifeq ($(KBUILD_TARGET), win)
+
+else if1of ($(KBUILD_TARGET), solaris freebsd) ## @todo merge this with the mixed case stuff.
+ #
+ # vboxnetflt(.ko/.o/) - The lower case driver.
+ # Note! On Solaris the name has to be <= 8 chars long.
+ # The DEBUG_HASH* stuff is for CONFIG_DYNAMIC_DEBUG-enabled kernels
+ #
+ ifdef VBOX_WITH_VBOXDRV
+ SYSMODS += vboxnetflt
+ vboxnetflt_TEMPLATE = VBoxR0Drv
+ vboxnetflt_NAME.solaris = vboxflt
+ vboxnetflt_DEFS = IN_RT_R0
+ vboxnetflt_DEPS.solaris += $(VBOX_SVN_REV_KMK)
+ vboxnetflt_INCS := \
+ $(PATH_SUB_CURRENT)
+ vboxnetflt_LDFLAGS.solaris += -N drv/vboxdrv -N misc/ctf
+ vboxnetflt_LIBS = \
+ $(PATH_STAGE_LIB)/SUPR0IdcClient$(VBOX_SUFF_LIB)
+ ## @todo vboxflt should resolves all the IPRT bits from vboxdrv.
+ #vboxnetflt_LIBS += \
+ # $(PATH_STAGE_LIB)/RuntimeR0Drv$(VBOX_SUFF_LIB)
+ vboxnetflt_SOURCES.solaris = solaris/VBoxNetFlt-solaris.c
+ vboxnetflt_SOURCES.freebsd = freebsd/VBoxNetFlt-freebsd.c
+ vboxnetflt_SOURCES = VBoxNetFlt.c
+ solaris/VBoxNetFlt-solaris.c_DEFS = VBOX_SVN_REV=$(VBOX_SVN_REV)
+
+ ifdef VBOX_WITH_NETFLT_CROSSBOW
+ SYSMODS += vboxnetbow
+ vboxnetbow_TEMPLATE = VBoxR0Drv
+ vboxnetbow_NAME = vboxbow
+ vboxnetbow_DEFS = vboxnetflt_DEFS VBOX_WITH_NETFLT_CROSSBOW
+ vboxnetbow_INCS := $(PATH_SUB_CURRENT)
+ vboxnetbow_LDFLAGS += -N drv/vboxdrv -N drv/vnic -N misc/mac -N misc/dls
+ vboxnetbow_LIBS = \
+ $(PATH_STAGE_LIB)/SUPR0IdcClient$(VBOX_SUFF_LIB)
+ vboxnetbow_SOURCES.solaris = solaris/VBoxNetFltBow-solaris.c
+ vboxnetbow_SOURCES = VBoxNetFlt.c
+ solaris/VBoxNetFltBow-solaris.c_DEFS = VBOX_SVN_REV=$(VBOX_SVN_REV)
+ endif # VBOX_WITH_NETFLT_CROSSBOW
+ endif # VBOX_WITH_VBOXDRV
+endif # to be removed.
+
+
+ifeq ($(KBUILD_TARGET),linux)
+ #
+ # Install source files for compilation on Linux.
+ # files_vboxnetflt defines VBOX_VBOXNETFLT_SOURCES.
+ #
+ include $(PATH_SUB_CURRENT)/linux/files_vboxnetflt
+ INSTALLS += VBoxNetFlt-src VBoxNetFlt-sh
+ VBoxNetFlt-src_INST = bin/src/vboxnetflt/
+ VBoxNetFlt-src_SOURCES = \
+ $(subst $(DQUOTE),,$(VBOX_VBOXNETFLT_SOURCES)) \
+ $(VBoxNetFlt-src_0_OUTDIR)/Makefile
+ VBoxNetFlt-src_CLEAN = \
+ $(VBoxNetFlt-src_0_OUTDIR)/Makefile \
+ $(PATH_TARGET)/VBoxNetFlt-src-1.dep
+
+ # Scripts needed for building the kernel module.
+
+ includedep $(PATH_TARGET)/VBoxNetFlt-src-1.dep
+ $$(VBoxNetFlt-src_0_OUTDIR)/Makefile: \
+ $(PATH_SUB_CURRENT)/linux/Makefile \
+ $$(if $$(eq $$(VBoxNetFlt/linux/Makefile_VBOX_HARDENED),$$(VBOX_WITH_HARDENING)),,FORCE) \
+ | $$(dir $$@)
+ $(QUIET)$(RM) -f -- $@
+ ifndef VBOX_WITH_HARDENING
+ $(QUIET)$(SED) -e "s;VBOX_WITH_HARDENING;;g" --output $@ $<
+ else
+ $(QUIET)$(CP) -f $< $@
+ endif
+ %$(QUIET2)$(RM) -f -- $(PATH_TARGET)/VBoxNetFlt-src-1.dep
+ %$(QUIET2)$(APPEND) '$(PATH_TARGET)/VBoxNetFlt-src-1.dep' 'VBoxNetFlt/linux/Makefile_VBOX_HARDENED=$(VBOX_WITH_HARDENING)'
+
+ #
+ # Build test for the linux host kernel modules.
+ #
+ $(evalcall2 VBOX_LINUX_KMOD_TEST_BUILD_RULE_FN,VBoxNetFlt-src,vboxdrv-src,)
+endif # linux
+
+# Gross hack for FreeBSD 7, should figure this out properly
+## @todo Properly generate opt_netgraph.h
+ifeq ($(KBUILD_TARGET),freebsd)
+ vboxnetflt_DEPS.freebsd += $(PATH_OUT)/opt_netgraph.h
+ $(PATH_OUT)/opt_netgraph.h:
+ echo > $(PATH_OUT)/opt_netgraph.h
+
+ #
+ # Install source files for compilation on FreeBSD.
+ # files_vboxnetflt defines VBOX_VBOXNETFLT_SOURCES.
+ #
+ include $(PATH_SUB_CURRENT)/freebsd/files_vboxnetflt
+ INSTALLS += VBoxNetFlt-src
+ VBoxNetFlt-src_INST = bin/src/vboxnetflt/
+ VBoxNetFlt-src_SOURCES = \
+ $(subst $(DQUOTE),,$(VBOX_VBOXNETFLT_SOURCES)) \
+ $(VBoxNetFlt-src_0_OUTDIR)/Makefile
+ VBoxNetFlt-src_CLEAN = \
+ $(VBoxNetFlt-src_0_OUTDIR)/Makefile
+
+ $$(VBoxNetFlt-src_0_OUTDIR)/Makefile: \
+ $(PATH_SUB_CURRENT)/freebsd/Makefile \
+ $$(if $$(eq $$(VBoxNetFlt/freebsd/Makefile_VBOX_HARDENED),$$(VBOX_WITH_HARDENING)),,FORCE) \
+ | $$(dir $$@)
+ $(QUIET)$(RM) -f -- $@
+ $(QUIET)$(CP) -f $< $@
+ ifndef VBOX_WITH_HARDENING
+ $(QUIET)$(SED) -e "s;VBOX_WITH_HARDENING;;g" --output $@.tmp $@
+ ${QUIET}$(MV) -f $@.tmp $@
+ endif
+ ifndef VBOX_WITH_NETFLT_VIMAGE
+ $(QUIET)$(SED) -e "s;-DVIMAGE;;g" --output $@.tmp $@
+ ${QUIET}$(MV) -f $@.tmp $@
+ endif
+endif # freebsd
+
+include $(FILE_KBUILD_SUB_FOOTER)
+
diff --git a/src/VBox/HostDrivers/VBoxNetFlt/VBoxNetFlt.c b/src/VBox/HostDrivers/VBoxNetFlt/VBoxNetFlt.c
new file mode 100644
index 00000000..b80af59e
--- /dev/null
+++ b/src/VBox/HostDrivers/VBoxNetFlt/VBoxNetFlt.c
@@ -0,0 +1,1591 @@
+/* $Id: VBoxNetFlt.c $ */
+/** @file
+ * VBoxNetFlt - Network Filter Driver (Host), Common Code.
+ */
+
+/*
+ * Copyright (C) 2008-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+/** @page pg_netflt VBoxNetFlt - Network Interface Filter
+ *
+ * This is a kernel module that attaches to a real interface on the host and
+ * filters and injects packets.
+ *
+ * In the big picture we're one of the three trunk interface on the internal
+ * network, the one named "NIC Filter Driver": @image html Networking_Overview.gif
+ *
+ *
+ * @section sec_netflt_locking Locking and Potential Races
+ *
+ * The main challenge here is to make sure the netfilter and internal network
+ * instances won't be destroyed while someone is calling into them.
+ *
+ * The main calls into or out of of the filter driver are:
+ * - Send.
+ * - Async send completion (not implemented yet)
+ * - Release by the internal network.
+ * - Receive.
+ * - Disappearance of the host networking interface.
+ * - Reappearance of the host networking interface.
+ *
+ * The latter two calls are can be caused by driver unloading/loading or the
+ * device being physical unplugged (e.g. a USB network device). Actually, the
+ * unload scenario must fervently be prevent as it will cause panics because the
+ * internal network will assume the trunk is around until it releases it.
+ * @todo Need to figure which host allow unloading and block/fix it.
+ *
+ * Currently the netfilter instance lives until the internal network releases
+ * it. So, it is the internal networks responsibility to make sure there are no
+ * active calls when it releases the trunk and destroys the network. The
+ * netfilter assists in this by providing INTNETTRUNKIFPORT::pfnSetState and
+ * INTNETTRUNKIFPORT::pfnWaitForIdle. The trunk state is used to enable/disable
+ * promiscuous mode on the hardware NIC (or similar activation) as well
+ * indicating that disconnect is imminent and no further calls shall be made
+ * into the internal network. After changing the state to disconnecting and
+ * prior to invoking INTNETTRUNKIFPORT::pfnDisconnectAndRelease, the internal
+ * network will use INTNETTRUNKIFPORT::pfnWaitForIdle to wait for any still
+ * active calls to complete.
+ *
+ * The netfilter employs a busy counter and an internal state in addition to the
+ * public trunk state. All these variables are protected using a spinlock.
+ *
+ *
+ * @section sec_netflt_msc Locking / Sequence Diagrams - OBSOLETE
+ *
+ * !OBSOLETE! - THIS WAS THE OLD APPROACH!
+ *
+ * This secion contains a few sequence diagrams describing the problematic
+ * transitions of a host interface filter instance.
+ *
+ * The thing that makes it all a bit problematic is that multiple events may
+ * happen at the same time, and that we have to be very careful to avoid
+ * deadlocks caused by mixing our locks with the ones in the host kernel. The
+ * main events are receive, send, async send completion, disappearance of the
+ * host networking interface and its reappearance. The latter two events are
+ * can be caused by driver unloading/loading or the device being physical
+ * unplugged (e.g. a USB network device).
+ *
+ * The strategy for dealing with these issues are:
+ * - Use a simple state machine.
+ * - Require the user (IntNet) to serialize all its calls to us,
+ * while at the same time not owning any lock used by any of the
+ * the callbacks we might call on receive and async send completion.
+ * - Make sure we're 100% idle before disconnecting, and have a
+ * disconnected status on both sides to fend off async calls.
+ * - Protect the host specific interface handle and the state variables
+ * using a spinlock.
+ *
+ *
+ * @subsection subsec_netflt_msc_dis_rel Disconnect from the network and release - OBSOLETE
+ *
+ * @msc
+ * VM, IntNet, NetFlt, Kernel, Wire;
+ *
+ * VM->IntNet [label="pkt0", linecolor="green", textcolor="green"];
+ * IntNet=>IntNet [label="Lock Network", linecolor="green", textcolor="green" ];
+ * IntNet=>IntNet [label="Route packet -> wire", linecolor="green", textcolor="green" ];
+ * IntNet=>IntNet [label="Unlock Network", linecolor="green", textcolor="green" ];
+ * IntNet=>NetFlt [label="pkt0 to wire", linecolor="green", textcolor="green" ];
+ * NetFlt=>Kernel [label="pkt0 to wire", linecolor="green", textcolor="green"];
+ * Kernel->Wire [label="pkt0 to wire", linecolor="green", textcolor="green"];
+ *
+ * --- [label="Suspending the trunk interface"];
+ * IntNet=>IntNet [label="Lock Network"];
+ *
+ * Wire->Kernel [label="pkt1 - racing us", linecolor="red", textcolor="red"];
+ * Kernel=>>NetFlt [label="pkt1 - racing us", linecolor="red", textcolor="red"];
+ * NetFlt=>>IntNet [label="pkt1 recv - blocks", linecolor="red", textcolor="red"];
+ *
+ * IntNet=>IntNet [label="Mark Trunk Suspended"];
+ * IntNet=>IntNet [label="Unlock Network"];
+ *
+ * IntNet=>NetFlt [label="pfnSetActive(false)"];
+ * NetFlt=>NetFlt [label="Mark inactive (atomic)"];
+ * IntNet<<NetFlt;
+ * IntNet=>NetFlt [label="pfnWaitForIdle(forever)"];
+ *
+ * IntNet=>>NetFlt [label="pkt1 to host", linecolor="red", textcolor="red"];
+ * NetFlt=>>Kernel [label="pkt1 to host", linecolor="red", textcolor="red"];
+ *
+ * Kernel<-Wire [label="pkt0 on wire", linecolor="green", textcolor="green"];
+ * NetFlt<<Kernel [label="pkt0 on wire", linecolor="green", textcolor="green"];
+ * IntNet<<=NetFlt [label="pfnSGRelease", linecolor="green", textcolor="green"];
+ * IntNet<<=IntNet [label="Lock Net, free SG, Unlock Net", linecolor="green", textcolor="green"];
+ * IntNet>>NetFlt [label="pfnSGRelease", linecolor="green", textcolor="green"];
+ * NetFlt<-NetFlt [label="idle", linecolor="green", textcolor="green"];
+ *
+ * IntNet<<NetFlt [label="idle (pfnWaitForIdle)"];
+ *
+ * Wire->Kernel [label="pkt2", linecolor="red", textcolor="red"];
+ * Kernel=>>NetFlt [label="pkt2", linecolor="red", textcolor="red"];
+ * NetFlt=>>Kernel [label="pkt2 to host", linecolor="red", textcolor="red"];
+ *
+ * VM->IntNet [label="pkt3", linecolor="green", textcolor="green"];
+ * IntNet=>IntNet [label="Lock Network", linecolor="green", textcolor="green" ];
+ * IntNet=>IntNet [label="Route packet -> drop", linecolor="green", textcolor="green" ];
+ * IntNet=>IntNet [label="Unlock Network", linecolor="green", textcolor="green" ];
+ *
+ * --- [label="The trunk interface is idle now, disconnect it"];
+ * IntNet=>IntNet [label="Lock Network"];
+ * IntNet=>IntNet [label="Unlink Trunk"];
+ * IntNet=>IntNet [label="Unlock Network"];
+ * IntNet=>NetFlt [label="pfnDisconnectAndRelease"];
+ * NetFlt=>Kernel [label="iflt_detach"];
+ * NetFlt<<=Kernel [label="iff_detached"];
+ * NetFlt>>Kernel [label="iff_detached"];
+ * NetFlt<<Kernel [label="iflt_detach"];
+ * NetFlt=>NetFlt [label="Release"];
+ * IntNet<<NetFlt [label="pfnDisconnectAndRelease"];
+ *
+ * @endmsc
+ *
+ *
+ *
+ * @subsection subsec_netflt_msc_hif_rm Host Interface Removal - OBSOLETE
+ *
+ * The ifnet_t (pIf) is a tricky customer as any reference to it can potentially
+ * race the filter detaching. The simple way of solving it on Darwin is to guard
+ * all access to the pIf member with a spinlock. The other host systems will
+ * probably have similar race conditions, so the spinlock is a generic thing.
+ *
+ * @msc
+ * VM, IntNet, NetFlt, Kernel;
+ *
+ * VM->IntNet [label="pkt0", linecolor="green", textcolor="green"];
+ * IntNet=>IntNet [label="Lock Network", linecolor="green", textcolor="green" ];
+ * IntNet=>IntNet [label="Route packet -> wire", linecolor="green", textcolor="green" ];
+ * IntNet=>IntNet [label="Unlock Network", linecolor="green", textcolor="green" ];
+ * IntNet=>NetFlt [label="pkt0 to wire", linecolor="green", textcolor="green" ];
+ * NetFlt=>Kernel [label="ifnet_reference w/ spinlock", linecolor="green", textcolor="green" ];
+ * NetFlt<<Kernel [label="ifnet_reference", linecolor="green", textcolor="green" ];
+ * NetFlt=>Kernel [label="pkt0 to wire (blocks)", linecolor="green", textcolor="green" ];
+ *
+ * --- [label="The host interface is being disconnected"];
+ * Kernel->NetFlt [label="iff_detached"];
+ * NetFlt=>Kernel [label="ifnet_release w/ spinlock"];
+ * NetFlt<<Kernel [label="ifnet_release"];
+ * NetFlt=>NetFlt [label="fDisconnectedFromHost=true"];
+ * NetFlt>>Kernel [label="iff_detached"];
+ *
+ * NetFlt<<Kernel [label="dropped", linecolor="green", textcolor="green"];
+ * NetFlt=>NetFlt [label="Acquire spinlock", linecolor="green", textcolor="green"];
+ * NetFlt=>Kernel [label="ifnet_release", linecolor="green", textcolor="green"];
+ * NetFlt<<Kernel [label="ifnet_release", linecolor="green", textcolor="green"];
+ * NetFlt=>NetFlt [label="pIf=NULL", linecolor="green", textcolor="green"];
+ * NetFlt=>NetFlt [label="Release spinlock", linecolor="green", textcolor="green"];
+ * IntNet<=NetFlt [label="pfnSGRelease", linecolor="green", textcolor="green"];
+ * IntNet>>NetFlt [label="pfnSGRelease", linecolor="green", textcolor="green"];
+ * IntNet<<NetFlt [label="pkt0 to wire", linecolor="green", textcolor="green"];
+ *
+ * @endmsc
+ *
+ *
+ *
+ * @subsection subsec_netflt_msc_hif_rd Host Interface Rediscovery - OBSOLETE
+ *
+ * The rediscovery is performed when we receive a send request and a certain
+ * period have elapsed since the last attempt, i.e. we're polling it. We
+ * synchronize the rediscovery with disconnection from the internal network
+ * by means of the pfnWaitForIdle call, so no special handling is required.
+ *
+ * @msc
+ * VM2, VM1, IntNet, NetFlt, Kernel, Wire;
+ *
+ * --- [label="Rediscovery conditions are not met"];
+ * VM1->IntNet [label="pkt0"];
+ * IntNet=>IntNet [label="Lock Network"];
+ * IntNet=>IntNet [label="Route packet -> wire"];
+ * IntNet=>IntNet [label="Unlock Network"];
+ * IntNet=>NetFlt [label="pkt0 to wire"];
+ * NetFlt=>NetFlt [label="Read pIf(==NULL) w/ spinlock"];
+ * IntNet<<NetFlt [label="pkt0 to wire (dropped)"];
+ *
+ * --- [label="Rediscovery conditions"];
+ * VM1->IntNet [label="pkt1"];
+ * IntNet=>IntNet [label="Lock Network"];
+ * IntNet=>IntNet [label="Route packet -> wire"];
+ * IntNet=>IntNet [label="Unlock Network"];
+ * IntNet=>NetFlt [label="pkt1 to wire"];
+ * NetFlt=>NetFlt [label="Read pIf(==NULL) w/ spinlock"];
+ * NetFlt=>NetFlt [label="fRediscoveryPending=true w/ spinlock"];
+ * NetFlt=>Kernel [label="ifnet_find_by_name"];
+ * NetFlt<<Kernel [label="ifnet_find_by_name (success)"];
+ *
+ * VM2->IntNet [label="pkt2", linecolor="red", textcolor="red"];
+ * IntNet=>IntNet [label="Lock Network", linecolor="red", textcolor="red"];
+ * IntNet=>IntNet [label="Route packet -> wire", linecolor="red", textcolor="red"];
+ * IntNet=>IntNet [label="Unlock Network", linecolor="red", textcolor="red"];
+ * IntNet=>NetFlt [label="pkt2 to wire", linecolor="red", textcolor="red"];
+ * NetFlt=>NetFlt [label="!pIf || fRediscoveryPending (w/ spinlock)", linecolor="red", textcolor="red"];
+ * IntNet<<NetFlt [label="pkt2 to wire (dropped)", linecolor="red", textcolor="red"];
+
+ * NetFlt=>Kernel [label="iflt_attach"];
+ * NetFlt<<Kernel [label="iflt_attach (success)"];
+ * NetFlt=>NetFlt [label="Acquire spinlock"];
+ * NetFlt=>NetFlt [label="Set pIf and update flags"];
+ * NetFlt=>NetFlt [label="Release spinlock"];
+ *
+ * NetFlt=>Kernel [label="pkt1 to wire"];
+ * Kernel->Wire [label="pkt1 to wire"];
+ * NetFlt<<Kernel [label="pkt1 to wire"];
+ * IntNet<<NetFlt [label="pkt1 to wire"];
+ *
+ *
+ * @endmsc
+ *
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#define LOG_GROUP LOG_GROUP_NET_FLT_DRV
+#include "VBoxNetFltInternal.h"
+
+#include <VBox/sup.h>
+#include <VBox/log.h>
+#include <VBox/err.h>
+#include <iprt/assert.h>
+#include <iprt/string.h>
+#include <iprt/spinlock.h>
+#include <iprt/uuid.h>
+#include <iprt/mem.h>
+#include <iprt/time.h>
+#include <iprt/semaphore.h>
+#include <iprt/thread.h>
+
+
+/*********************************************************************************************************************************
+* Defined Constants And Macros *
+*********************************************************************************************************************************/
+#define IFPORT_2_VBOXNETFLTINS(pIfPort) \
+ ( (PVBOXNETFLTINS)((uint8_t *)(pIfPort) - RT_UOFFSETOF(VBOXNETFLTINS, MyPort)) )
+
+
+AssertCompileMemberSize(VBOXNETFLTINS, enmState, sizeof(uint32_t));
+
+/**
+ * Sets the enmState member atomically.
+ *
+ * Used for all updates.
+ *
+ * @param pThis The instance.
+ * @param enmNewState The new value.
+ */
+DECLINLINE(void) vboxNetFltSetState(PVBOXNETFLTINS pThis, VBOXNETFTLINSSTATE enmNewState)
+{
+ ASMAtomicWriteU32((uint32_t volatile *)&pThis->enmState, enmNewState);
+}
+
+
+/**
+ * Gets the enmState member atomically.
+ *
+ * Used for all reads.
+ *
+ * @returns The enmState value.
+ * @param pThis The instance.
+ */
+DECLINLINE(VBOXNETFTLINSSTATE) vboxNetFltGetState(PVBOXNETFLTINS pThis)
+{
+ return (VBOXNETFTLINSSTATE)ASMAtomicUoReadU32((uint32_t volatile *)&pThis->enmState);
+}
+
+
+/**
+ * Finds a instance by its name, the caller does the locking.
+ *
+ * @returns Pointer to the instance by the given name. NULL if not found.
+ * @param pGlobals The globals.
+ * @param pszName The name of the instance.
+ */
+static PVBOXNETFLTINS vboxNetFltFindInstanceLocked(PVBOXNETFLTGLOBALS pGlobals, const char *pszName)
+{
+ PVBOXNETFLTINS pCur;
+ for (pCur = pGlobals->pInstanceHead; pCur; pCur = pCur->pNext)
+ if (!strcmp(pszName, pCur->szName))
+ return pCur;
+ return NULL;
+}
+
+
+/**
+ * Finds a instance by its name, will request the mutex.
+ *
+ * No reference to the instance is retained, we're assuming the caller to
+ * already have one but just for some reason doesn't have the pointer to it.
+ *
+ * @returns Pointer to the instance by the given name. NULL if not found.
+ * @param pGlobals The globals.
+ * @param pszName The name of the instance.
+ */
+DECLHIDDEN(PVBOXNETFLTINS) vboxNetFltFindInstance(PVBOXNETFLTGLOBALS pGlobals, const char *pszName)
+{
+ PVBOXNETFLTINS pRet;
+ int rc = RTSemFastMutexRequest(pGlobals->hFastMtx);
+ AssertRCReturn(rc, NULL);
+
+ pRet = vboxNetFltFindInstanceLocked(pGlobals, pszName);
+
+ rc = RTSemFastMutexRelease(pGlobals->hFastMtx);
+ AssertRC(rc);
+ return pRet;
+}
+
+
+/**
+ * Unlinks an instance from the chain.
+ *
+ * @param pGlobals The globals.
+ * @param pToUnlink The instance to unlink.
+ */
+static void vboxNetFltUnlinkLocked(PVBOXNETFLTGLOBALS pGlobals, PVBOXNETFLTINS pToUnlink)
+{
+ if (pGlobals->pInstanceHead == pToUnlink)
+ pGlobals->pInstanceHead = pToUnlink->pNext;
+ else
+ {
+ PVBOXNETFLTINS pCur;
+ for (pCur = pGlobals->pInstanceHead; pCur; pCur = pCur->pNext)
+ if (pCur->pNext == pToUnlink)
+ {
+ pCur->pNext = pToUnlink->pNext;
+ break;
+ }
+ Assert(pCur);
+ }
+ pToUnlink->pNext = NULL;
+}
+
+
+/**
+ * Performs interface rediscovery if it was disconnected from the host.
+ *
+ * @returns true if successfully rediscovered and connected, false if not.
+ * @param pThis The instance.
+ */
+static bool vboxNetFltMaybeRediscovered(PVBOXNETFLTINS pThis)
+{
+ uint64_t Now;
+ bool fRediscovered;
+ bool fDoIt;
+
+ /*
+ * Don't do rediscovery if we're called with preemption disabled.
+ *
+ * Note! This may cause trouble if we're always called with preemption
+ * disabled and vboxNetFltOsMaybeRediscovered actually does some real
+ * work. For the time being though, only Darwin and FreeBSD depends
+ * on these call outs and neither supports sending with preemption
+ * disabled.
+ */
+ if (!RTThreadPreemptIsEnabled(NIL_RTTHREAD))
+ return false;
+
+ /*
+ * Rediscovered already? Time to try again?
+ */
+ Now = RTTimeNanoTS();
+ RTSpinlockAcquire(pThis->hSpinlock);
+
+ fRediscovered = !ASMAtomicUoReadBool(&pThis->fDisconnectedFromHost);
+ fDoIt = !fRediscovered
+ && !ASMAtomicUoReadBool(&pThis->fRediscoveryPending)
+ && Now - ASMAtomicUoReadU64(&pThis->NanoTSLastRediscovery) > UINT64_C(5000000000); /* 5 sec */
+ if (fDoIt)
+ ASMAtomicWriteBool(&pThis->fRediscoveryPending, true);
+
+ RTSpinlockRelease(pThis->hSpinlock);
+
+ /*
+ * Call the OS specific code to do the job.
+ * Update the state when the call returns, that is everything except for
+ * the fDisconnectedFromHost flag which the OS specific code shall set.
+ */
+ if (fDoIt)
+ {
+ fRediscovered = vboxNetFltOsMaybeRediscovered(pThis);
+
+ Assert(!fRediscovered || !ASMAtomicUoReadBool(&pThis->fDisconnectedFromHost));
+
+ ASMAtomicUoWriteU64(&pThis->NanoTSLastRediscovery, RTTimeNanoTS());
+ ASMAtomicWriteBool(&pThis->fRediscoveryPending, false);
+
+ if (fRediscovered)
+ /** @todo this isn't 100% serialized. */
+ vboxNetFltPortOsSetActive(pThis, pThis->enmTrunkState == INTNETTRUNKIFSTATE_ACTIVE);
+ }
+
+ return fRediscovered;
+}
+
+
+/**
+ * @copydoc INTNETTRUNKIFPORT::pfnXmit
+ */
+static DECLCALLBACK(int) vboxNetFltPortXmit(PINTNETTRUNKIFPORT pIfPort, void *pvIfData, PINTNETSG pSG, uint32_t fDst)
+{
+ PVBOXNETFLTINS pThis = IFPORT_2_VBOXNETFLTINS(pIfPort);
+ int rc = VINF_SUCCESS;
+
+ /*
+ * Input validation.
+ */
+ AssertPtr(pThis);
+ AssertPtr(pSG);
+ Assert(pThis->MyPort.u32Version == INTNETTRUNKIFPORT_VERSION);
+ AssertReturn(vboxNetFltGetState(pThis) == kVBoxNetFltInsState_Connected, VERR_INVALID_STATE);
+
+ /*
+ * Do a busy retain and then make sure we're connected to the interface
+ * before invoking the OS specific code.
+ */
+ if (RT_LIKELY(vboxNetFltTryRetainBusyActive(pThis)))
+ {
+ if ( !ASMAtomicUoReadBool(&pThis->fDisconnectedFromHost)
+ || vboxNetFltMaybeRediscovered(pThis))
+ rc = vboxNetFltPortOsXmit(pThis, pvIfData, pSG, fDst);
+ vboxNetFltRelease(pThis, true /* fBusy */);
+ }
+
+ return rc;
+}
+
+
+/**
+ * @copydoc INTNETTRUNKIFPORT::pfnWaitForIdle
+ */
+static DECLCALLBACK(int) vboxNetFltPortWaitForIdle(PINTNETTRUNKIFPORT pIfPort, uint32_t cMillies)
+{
+ PVBOXNETFLTINS pThis = IFPORT_2_VBOXNETFLTINS(pIfPort);
+ int rc;
+
+ /*
+ * Input validation.
+ */
+ AssertPtr(pThis);
+ Assert(pThis->MyPort.u32Version == INTNETTRUNKIFPORT_VERSION);
+ AssertReturn(vboxNetFltGetState(pThis) == kVBoxNetFltInsState_Connected, VERR_INVALID_STATE);
+ AssertReturn(pThis->enmTrunkState == INTNETTRUNKIFSTATE_DISCONNECTING, VERR_INVALID_STATE);
+
+ /*
+ * Go to sleep on the semaphore after checking the busy count.
+ */
+ vboxNetFltRetain(pThis, false /* fBusy */);
+
+ rc = VINF_SUCCESS;
+ while (pThis->cBusy && RT_SUCCESS(rc))
+ rc = RTSemEventWait(pThis->hEventIdle, cMillies); /** @todo make interruptible? */
+
+ vboxNetFltRelease(pThis, false /* fBusy */);
+
+ return rc;
+}
+
+
+/**
+ * @copydoc INTNETTRUNKIFPORT::pfnSetState
+ */
+static DECLCALLBACK(INTNETTRUNKIFSTATE) vboxNetFltPortSetState(PINTNETTRUNKIFPORT pIfPort, INTNETTRUNKIFSTATE enmState)
+{
+ PVBOXNETFLTINS pThis = IFPORT_2_VBOXNETFLTINS(pIfPort);
+ INTNETTRUNKIFSTATE enmOldTrunkState;
+
+ /*
+ * Input validation.
+ */
+ AssertPtr(pThis);
+ AssertPtr(pThis->pGlobals);
+ Assert(pThis->MyPort.u32Version == INTNETTRUNKIFPORT_VERSION);
+ AssertReturn(vboxNetFltGetState(pThis) == kVBoxNetFltInsState_Connected, INTNETTRUNKIFSTATE_INVALID);
+ AssertReturn(enmState > INTNETTRUNKIFSTATE_INVALID && enmState < INTNETTRUNKIFSTATE_END,
+ INTNETTRUNKIFSTATE_INVALID);
+
+ /*
+ * Take the lock and change the state.
+ */
+ RTSpinlockAcquire(pThis->hSpinlock);
+ enmOldTrunkState = pThis->enmTrunkState;
+ if (enmOldTrunkState != enmState)
+ ASMAtomicWriteU32((uint32_t volatile *)&pThis->enmTrunkState, enmState);
+ RTSpinlockRelease(pThis->hSpinlock);
+
+ /*
+ * If the state change indicates that the trunk has become active or
+ * inactive, call the OS specific part so they can work the promiscuous
+ * settings and such.
+ * Note! The caller makes sure there are no concurrent pfnSetState calls.
+ */
+ if ((enmOldTrunkState == INTNETTRUNKIFSTATE_ACTIVE) != (enmState == INTNETTRUNKIFSTATE_ACTIVE))
+ vboxNetFltPortOsSetActive(pThis, (enmState == INTNETTRUNKIFSTATE_ACTIVE));
+
+ return enmOldTrunkState;
+}
+
+
+/**
+ * @copydoc INTNETTRUNKIFPORT::pfnNotifyMacAddress
+ */
+static DECLCALLBACK(void) vboxNetFltPortNotifyMacAddress(PINTNETTRUNKIFPORT pIfPort, void *pvIfData, PCRTMAC pMac)
+{
+ PVBOXNETFLTINS pThis = IFPORT_2_VBOXNETFLTINS(pIfPort);
+
+ /*
+ * Input validation.
+ */
+ AssertPtr(pThis);
+ Assert(pThis->MyPort.u32Version == INTNETTRUNKIFPORT_VERSION);
+
+ vboxNetFltRetain(pThis, false /* fBusy */);
+ vboxNetFltPortOsNotifyMacAddress(pThis, pvIfData, pMac);
+ vboxNetFltRelease(pThis, false /* fBusy */);
+}
+
+
+/**
+ * @copydoc INTNETTRUNKIFPORT::pfnConnectInterface
+ */
+static DECLCALLBACK(int) vboxNetFltPortConnectInterface(PINTNETTRUNKIFPORT pIfPort, void *pvIf, void **ppvIfData)
+{
+ PVBOXNETFLTINS pThis = IFPORT_2_VBOXNETFLTINS(pIfPort);
+ int rc;
+
+ /*
+ * Input validation.
+ */
+ AssertPtr(pThis);
+ Assert(pThis->MyPort.u32Version == INTNETTRUNKIFPORT_VERSION);
+
+ vboxNetFltRetain(pThis, false /* fBusy */);
+ rc = vboxNetFltPortOsConnectInterface(pThis, pvIf, ppvIfData);
+ vboxNetFltRelease(pThis, false /* fBusy */);
+
+ return rc;
+}
+
+
+/**
+ * @copydoc INTNETTRUNKIFPORT::pfnDisconnectInterface
+ */
+static DECLCALLBACK(void) vboxNetFltPortDisconnectInterface(PINTNETTRUNKIFPORT pIfPort, void *pvIfData)
+{
+ PVBOXNETFLTINS pThis = IFPORT_2_VBOXNETFLTINS(pIfPort);
+ int rc;
+
+ /*
+ * Input validation.
+ */
+ AssertPtr(pThis);
+ Assert(pThis->MyPort.u32Version == INTNETTRUNKIFPORT_VERSION);
+
+ vboxNetFltRetain(pThis, false /* fBusy */);
+ rc = vboxNetFltPortOsDisconnectInterface(pThis, pvIfData);
+ vboxNetFltRelease(pThis, false /* fBusy */);
+ AssertRC(rc); /** @todo fix vboxNetFltPortOsDisconnectInterface. */
+}
+
+
+/**
+ * @copydoc INTNETTRUNKIFPORT::pfnDisconnectAndRelease
+ */
+static DECLCALLBACK(void) vboxNetFltPortDisconnectAndRelease(PINTNETTRUNKIFPORT pIfPort)
+{
+ PVBOXNETFLTINS pThis = IFPORT_2_VBOXNETFLTINS(pIfPort);
+
+ /*
+ * Serious paranoia.
+ */
+ AssertPtr(pThis);
+ Assert(pThis->MyPort.u32Version == INTNETTRUNKIFPORT_VERSION);
+ Assert(pThis->MyPort.u32VersionEnd == INTNETTRUNKIFPORT_VERSION);
+ AssertPtr(pThis->pGlobals);
+ Assert(pThis->hEventIdle != NIL_RTSEMEVENT);
+ Assert(pThis->hSpinlock != NIL_RTSPINLOCK);
+ Assert(pThis->szName[0]);
+
+ Assert(vboxNetFltGetState(pThis) == kVBoxNetFltInsState_Connected);
+ Assert(pThis->enmTrunkState == INTNETTRUNKIFSTATE_DISCONNECTING);
+ Assert(!pThis->fRediscoveryPending);
+ Assert(!pThis->cBusy);
+
+ /*
+ * Disconnect and release it.
+ */
+ RTSpinlockAcquire(pThis->hSpinlock);
+ vboxNetFltSetState(pThis, kVBoxNetFltInsState_Disconnecting);
+ RTSpinlockRelease(pThis->hSpinlock);
+
+ vboxNetFltOsDisconnectIt(pThis);
+ pThis->pSwitchPort = NULL;
+
+#ifdef VBOXNETFLT_STATIC_CONFIG
+ RTSpinlockAcquire(pThis->hSpinlock);
+ vboxNetFltSetState(pThis, kVBoxNetFltInsState_Unconnected);
+ RTSpinlockRelease(pThis->hSpinlock);
+#endif
+
+ vboxNetFltRelease(pThis, false /* fBusy */);
+}
+
+
+/**
+ * Destroy a device that has been disconnected from the switch.
+ *
+ * @returns true if the instance is destroyed, false otherwise.
+ * @param pThis The instance to be destroyed. This is
+ * no longer valid when this function returns.
+ */
+static bool vboxNetFltDestroyInstance(PVBOXNETFLTINS pThis)
+{
+ PVBOXNETFLTGLOBALS pGlobals = pThis->pGlobals;
+ uint32_t cRefs = ASMAtomicUoReadU32((uint32_t volatile *)&pThis->cRefs);
+ int rc;
+ LogFlow(("vboxNetFltDestroyInstance: pThis=%p (%s)\n", pThis, pThis->szName));
+
+ /*
+ * Validate the state.
+ */
+#ifdef VBOXNETFLT_STATIC_CONFIG
+ Assert( vboxNetFltGetState(pThis) == kVBoxNetFltInsState_Disconnecting
+ || vboxNetFltGetState(pThis) == kVBoxNetFltInsState_Unconnected);
+#else
+ Assert(vboxNetFltGetState(pThis) == kVBoxNetFltInsState_Disconnecting);
+#endif
+ Assert(pThis->enmTrunkState == INTNETTRUNKIFSTATE_DISCONNECTING);
+ Assert(!pThis->fRediscoveryPending);
+ Assert(!pThis->cRefs);
+ Assert(!pThis->cBusy);
+ Assert(!pThis->pSwitchPort);
+
+ /*
+ * Make sure the state is 'disconnecting' / 'destroying' and let the OS
+ * specific code do its part of the cleanup outside the mutex.
+ */
+ rc = RTSemFastMutexRequest(pGlobals->hFastMtx); AssertRC(rc);
+ vboxNetFltSetState(pThis, kVBoxNetFltInsState_Disconnecting);
+ RTSemFastMutexRelease(pGlobals->hFastMtx);
+
+ vboxNetFltOsDeleteInstance(pThis);
+
+ /*
+ * Unlink the instance and free up its resources.
+ */
+ rc = RTSemFastMutexRequest(pGlobals->hFastMtx); AssertRC(rc);
+ vboxNetFltSetState(pThis, kVBoxNetFltInsState_Destroyed);
+ vboxNetFltUnlinkLocked(pGlobals, pThis);
+ RTSemFastMutexRelease(pGlobals->hFastMtx);
+
+ RTSemEventDestroy(pThis->hEventIdle);
+ pThis->hEventIdle = NIL_RTSEMEVENT;
+ RTSpinlockDestroy(pThis->hSpinlock);
+ pThis->hSpinlock = NIL_RTSPINLOCK;
+ RTMemFree(pThis);
+
+ NOREF(cRefs);
+
+ return true;
+}
+
+
+/**
+ * Releases a reference to the specified instance.
+ *
+ * This method will destroy the instance when the count reaches 0.
+ * It will also take care of decrementing the counter and idle wakeup.
+ *
+ * @param pThis The instance.
+ * @param fBusy Whether the busy counter should be decremented too.
+ */
+DECLHIDDEN(void) vboxNetFltRelease(PVBOXNETFLTINS pThis, bool fBusy)
+{
+ uint32_t cRefs;
+
+ /*
+ * Paranoid Android.
+ */
+ AssertPtr(pThis);
+ Assert(pThis->MyPort.u32Version == INTNETTRUNKIFPORT_VERSION);
+ Assert(pThis->MyPort.u32VersionEnd == INTNETTRUNKIFPORT_VERSION);
+ Assert( vboxNetFltGetState(pThis) > kVBoxNetFltInsState_Invalid
+ && vboxNetFltGetState(pThis) < kVBoxNetFltInsState_Destroyed);
+ AssertPtr(pThis->pGlobals);
+ Assert(pThis->hEventIdle != NIL_RTSEMEVENT);
+ Assert(pThis->hSpinlock != NIL_RTSPINLOCK);
+ Assert(pThis->szName[0]);
+
+ /*
+ * Work the busy counter.
+ */
+ if (fBusy)
+ {
+ cRefs = ASMAtomicDecU32(&pThis->cBusy);
+ if (!cRefs)
+ {
+ int rc = RTSemEventSignal(pThis->hEventIdle);
+ AssertRC(rc);
+ }
+ else
+ Assert(cRefs < UINT32_MAX / 2);
+ }
+
+ /*
+ * The object reference counting.
+ */
+ cRefs = ASMAtomicDecU32(&pThis->cRefs);
+ if (!cRefs)
+ vboxNetFltDestroyInstance(pThis);
+ else
+ Assert(cRefs < UINT32_MAX / 2);
+}
+
+
+/**
+ * @copydoc INTNETTRUNKIFPORT::pfnRelease
+ */
+static DECLCALLBACK(void) vboxNetFltPortRelease(PINTNETTRUNKIFPORT pIfPort)
+{
+ PVBOXNETFLTINS pThis = IFPORT_2_VBOXNETFLTINS(pIfPort);
+ vboxNetFltRelease(pThis, false /* fBusy */);
+}
+
+
+/**
+ * @callback_method_impl{FNINTNETTRUNKIFPORTRELEASEBUSY}
+ */
+DECL_HIDDEN_CALLBACK(void) vboxNetFltPortReleaseBusy(PINTNETTRUNKIFPORT pIfPort)
+{
+ PVBOXNETFLTINS pThis = IFPORT_2_VBOXNETFLTINS(pIfPort);
+ vboxNetFltRelease(pThis, true /*fBusy*/);
+}
+
+
+/**
+ * Retains a reference to the specified instance and a busy reference too.
+ *
+ * @param pThis The instance.
+ * @param fBusy Whether the busy counter should be incremented as well.
+ */
+DECLHIDDEN(void) vboxNetFltRetain(PVBOXNETFLTINS pThis, bool fBusy)
+{
+ uint32_t cRefs;
+
+ /*
+ * Paranoid Android.
+ */
+ AssertPtr(pThis);
+ Assert(pThis->MyPort.u32Version == INTNETTRUNKIFPORT_VERSION);
+ Assert(pThis->MyPort.u32VersionEnd == INTNETTRUNKIFPORT_VERSION);
+ Assert( vboxNetFltGetState(pThis) > kVBoxNetFltInsState_Invalid
+ && vboxNetFltGetState(pThis) < kVBoxNetFltInsState_Destroyed);
+ AssertPtr(pThis->pGlobals);
+ Assert(pThis->hEventIdle != NIL_RTSEMEVENT);
+ Assert(pThis->hSpinlock != NIL_RTSPINLOCK);
+ Assert(pThis->szName[0]);
+
+ /*
+ * Retain the object.
+ */
+ cRefs = ASMAtomicIncU32(&pThis->cRefs);
+ Assert(cRefs > 1 && cRefs < UINT32_MAX / 2);
+
+ /*
+ * Work the busy counter.
+ */
+ if (fBusy)
+ {
+ cRefs = ASMAtomicIncU32(&pThis->cBusy);
+ Assert(cRefs > 0 && cRefs < UINT32_MAX / 2);
+ }
+
+ NOREF(cRefs);
+}
+
+
+/**
+ * Tries to retain the device as busy if the trunk is active.
+ *
+ * This is used before calling pfnRecv or pfnPreRecv.
+ *
+ * @returns true if we succeeded in retaining a busy reference to the active
+ * device. false if we failed.
+ * @param pThis The instance.
+ */
+DECLHIDDEN(bool) vboxNetFltTryRetainBusyActive(PVBOXNETFLTINS pThis)
+{
+ uint32_t cRefs;
+ bool fRc;
+
+ /*
+ * Paranoid Android.
+ */
+ AssertPtr(pThis);
+ Assert(pThis->MyPort.u32Version == INTNETTRUNKIFPORT_VERSION);
+ Assert(pThis->MyPort.u32VersionEnd == INTNETTRUNKIFPORT_VERSION);
+ Assert( vboxNetFltGetState(pThis) > kVBoxNetFltInsState_Invalid
+ && vboxNetFltGetState(pThis) < kVBoxNetFltInsState_Destroyed);
+ AssertPtr(pThis->pGlobals);
+ Assert(pThis->hEventIdle != NIL_RTSEMEVENT);
+ Assert(pThis->hSpinlock != NIL_RTSPINLOCK);
+ Assert(pThis->szName[0]);
+
+ /*
+ * Do the retaining and checking behind the spinlock.
+ */
+ RTSpinlockAcquire(pThis->hSpinlock);
+ fRc = pThis->enmTrunkState == INTNETTRUNKIFSTATE_ACTIVE;
+ if (fRc)
+ {
+ cRefs = ASMAtomicIncU32(&pThis->cRefs);
+ AssertMsg(cRefs > 1 && cRefs < UINT32_MAX / 2, ("%d\n", cRefs)); NOREF(cRefs);
+
+ cRefs = ASMAtomicIncU32(&pThis->cBusy);
+ AssertMsg(cRefs >= 1 && cRefs < UINT32_MAX / 2, ("%d\n", cRefs)); NOREF(cRefs);
+ }
+ RTSpinlockRelease(pThis->hSpinlock);
+
+ return fRc;
+}
+
+
+/**
+ * Tries to retain the device as busy if the trunk is not disconnecting.
+ *
+ * This is used before reporting stuff to the internal network.
+ *
+ * @returns true if we succeeded in retaining a busy reference to the active
+ * device. false if we failed.
+ * @param pThis The instance.
+ */
+DECLHIDDEN(bool) vboxNetFltTryRetainBusyNotDisconnected(PVBOXNETFLTINS pThis)
+{
+ uint32_t cRefs;
+ bool fRc;
+
+ /*
+ * Paranoid Android.
+ */
+ AssertPtr(pThis);
+ Assert(pThis->MyPort.u32Version == INTNETTRUNKIFPORT_VERSION);
+ Assert(pThis->MyPort.u32VersionEnd == INTNETTRUNKIFPORT_VERSION);
+ Assert( vboxNetFltGetState(pThis) > kVBoxNetFltInsState_Invalid
+ && vboxNetFltGetState(pThis) < kVBoxNetFltInsState_Destroyed);
+ AssertPtr(pThis->pGlobals);
+ Assert(pThis->hEventIdle != NIL_RTSEMEVENT);
+ Assert(pThis->hSpinlock != NIL_RTSPINLOCK);
+ Assert(pThis->szName[0]);
+
+ /*
+ * Do the retaining and checking behind the spinlock.
+ */
+ RTSpinlockAcquire(pThis->hSpinlock);
+ fRc = pThis->enmTrunkState == INTNETTRUNKIFSTATE_ACTIVE
+ || pThis->enmTrunkState == INTNETTRUNKIFSTATE_INACTIVE;
+ if (fRc)
+ {
+ cRefs = ASMAtomicIncU32(&pThis->cRefs);
+ AssertMsg(cRefs > 1 && cRefs < UINT32_MAX / 2, ("%d\n", cRefs)); NOREF(cRefs);
+
+ cRefs = ASMAtomicIncU32(&pThis->cBusy);
+ AssertMsg(cRefs >= 1 && cRefs < UINT32_MAX / 2, ("%d\n", cRefs)); NOREF(cRefs);
+ }
+ RTSpinlockRelease(pThis->hSpinlock);
+
+ return fRc;
+}
+
+
+/**
+ * @copydoc INTNETTRUNKIFPORT::pfnRetain
+ */
+static DECLCALLBACK(void) vboxNetFltPortRetain(PINTNETTRUNKIFPORT pIfPort)
+{
+ PVBOXNETFLTINS pThis = IFPORT_2_VBOXNETFLTINS(pIfPort);
+ vboxNetFltRetain(pThis, false /* fBusy */);
+}
+
+
+/**
+ * Connects the instance to the specified switch port.
+ *
+ * Called while owning the lock. We're ASSUMING that the internal
+ * networking code is already owning an recursive mutex, so, there
+ * will be no deadlocks when vboxNetFltOsConnectIt calls back into
+ * it for setting preferences.
+ *
+ * @returns VBox status code.
+ * @param pThis The instance.
+ * @param pSwitchPort The port on the internal network 'switch'.
+ * @param ppIfPort Where to return our port interface.
+ */
+static int vboxNetFltConnectIt(PVBOXNETFLTINS pThis, PINTNETTRUNKSWPORT pSwitchPort, PINTNETTRUNKIFPORT *ppIfPort)
+{
+ int rc;
+
+ /*
+ * Validate state.
+ */
+ Assert(!pThis->fRediscoveryPending);
+ Assert(!pThis->cBusy);
+#ifdef VBOXNETFLT_STATIC_CONFIG
+ Assert(vboxNetFltGetState(pThis) == kVBoxNetFltInsState_Unconnected);
+#else
+ Assert(vboxNetFltGetState(pThis) == kVBoxNetFltInsState_Initializing);
+#endif
+ Assert(pThis->enmTrunkState == INTNETTRUNKIFSTATE_INACTIVE);
+
+ /*
+ * Do the job.
+ * Note that we're calling the os stuff while owning the semaphore here.
+ */
+ pThis->pSwitchPort = pSwitchPort;
+ rc = vboxNetFltOsConnectIt(pThis);
+ if (RT_SUCCESS(rc))
+ {
+ vboxNetFltSetState(pThis, kVBoxNetFltInsState_Connected);
+ *ppIfPort = &pThis->MyPort;
+ }
+ else
+ pThis->pSwitchPort = NULL;
+
+ Assert(pThis->enmTrunkState == INTNETTRUNKIFSTATE_INACTIVE);
+ return rc;
+}
+
+
+/**
+ * Creates a new instance.
+ *
+ * The new instance will be in the suspended state in a dynamic config and in
+ * the inactive in a static one.
+ *
+ * Called without owning the lock, but will request is several times.
+ *
+ * @returns VBox status code.
+ * @param pGlobals The globals.
+ * @param pszName The instance name.
+ * @param pSwitchPort The port on the switch that we're connected with (dynamic only).
+ * @param fNoPromisc Do not attempt going into promiscuous mode.
+ * @param pvContext Context argument for vboxNetFltOsInitInstance.
+ * @param ppIfPort Where to store the pointer to our port interface (dynamic only).
+ */
+static int vboxNetFltNewInstance(PVBOXNETFLTGLOBALS pGlobals, const char *pszName, PINTNETTRUNKSWPORT pSwitchPort,
+ bool fNoPromisc, void *pvContext, PINTNETTRUNKIFPORT *ppIfPort)
+{
+ /*
+ * Allocate and initialize a new instance before requesting the mutex.
+ * Note! That in a static config we'll initialize the trunk state to
+ * disconnecting and flip it in vboxNetFltFactoryCreateAndConnect
+ * later on. This better reflext the state and it works better with
+ * assertions in the destruction path.
+ */
+ int rc;
+ size_t const cchName = strlen(pszName);
+ PVBOXNETFLTINS pNew = (PVBOXNETFLTINS)RTMemAllocZVar(RT_UOFFSETOF_DYN(VBOXNETFLTINS, szName[cchName + 1]));
+ if (!pNew)
+ return VERR_INTNET_FLT_IF_FAILED;
+ AssertMsg(((uintptr_t)pNew & 7) == 0, ("%p LB %#x\n", pNew, RT_UOFFSETOF_DYN(VBOXNETFLTINS, szName[cchName + 1])));
+ pNew->pNext = NULL;
+ pNew->MyPort.u32Version = INTNETTRUNKIFPORT_VERSION;
+ pNew->MyPort.pfnRetain = vboxNetFltPortRetain;
+ pNew->MyPort.pfnRelease = vboxNetFltPortRelease;
+ pNew->MyPort.pfnDisconnectAndRelease= vboxNetFltPortDisconnectAndRelease;
+ pNew->MyPort.pfnSetState = vboxNetFltPortSetState;
+ pNew->MyPort.pfnWaitForIdle = vboxNetFltPortWaitForIdle;
+ pNew->MyPort.pfnXmit = vboxNetFltPortXmit;
+ pNew->MyPort.pfnNotifyMacAddress = vboxNetFltPortNotifyMacAddress;
+ pNew->MyPort.pfnConnectInterface = vboxNetFltPortConnectInterface;
+ pNew->MyPort.pfnDisconnectInterface = vboxNetFltPortDisconnectInterface;
+ pNew->MyPort.u32VersionEnd = INTNETTRUNKIFPORT_VERSION;
+ pNew->pSwitchPort = pSwitchPort;
+ pNew->pGlobals = pGlobals;
+ pNew->hSpinlock = NIL_RTSPINLOCK;
+ pNew->enmState = kVBoxNetFltInsState_Initializing;
+#ifdef VBOXNETFLT_STATIC_CONFIG
+ pNew->enmTrunkState = INTNETTRUNKIFSTATE_DISCONNECTING;
+#else
+ pNew->enmTrunkState = INTNETTRUNKIFSTATE_INACTIVE;
+#endif
+ pNew->fDisconnectedFromHost = false;
+ pNew->fRediscoveryPending = false;
+ pNew->fDisablePromiscuous = fNoPromisc;
+ pNew->NanoTSLastRediscovery = INT64_MAX;
+ pNew->cRefs = 1;
+ pNew->cBusy = 0;
+ pNew->hEventIdle = NIL_RTSEMEVENT;
+ RT_BCOPY_UNFORTIFIED(pNew->szName, pszName, cchName + 1);
+
+ rc = RTSpinlockCreate(&pNew->hSpinlock, RTSPINLOCK_FLAGS_INTERRUPT_SAFE, "VBoxNetFltNewInstance");
+ if (RT_SUCCESS(rc))
+ {
+ rc = RTSemEventCreate(&pNew->hEventIdle);
+ if (RT_SUCCESS(rc))
+ {
+ rc = vboxNetFltOsPreInitInstance(pNew);
+ if (RT_SUCCESS(rc))
+ {
+ /*
+ * Insert the instance into the chain, checking for
+ * duplicates first of course (race).
+ */
+ rc = RTSemFastMutexRequest(pGlobals->hFastMtx);
+ if (RT_SUCCESS(rc))
+ {
+ if (!vboxNetFltFindInstanceLocked(pGlobals, pszName))
+ {
+ pNew->pNext = pGlobals->pInstanceHead;
+ pGlobals->pInstanceHead = pNew;
+ RTSemFastMutexRelease(pGlobals->hFastMtx);
+
+ /*
+ * Call the OS specific initialization code.
+ */
+ rc = vboxNetFltOsInitInstance(pNew, pvContext);
+ RTSemFastMutexRequest(pGlobals->hFastMtx);
+ if (RT_SUCCESS(rc))
+ {
+#ifdef VBOXNETFLT_STATIC_CONFIG
+ /*
+ * Static instances are unconnected at birth.
+ */
+ Assert(!pSwitchPort);
+ pNew->enmState = kVBoxNetFltInsState_Unconnected;
+ RTSemFastMutexRelease(pGlobals->hFastMtx);
+ *ppIfPort = &pNew->MyPort;
+ return rc;
+
+#else /* !VBOXNETFLT_STATIC_CONFIG */
+ /*
+ * Connect it as well, the OS specific bits has to be done outside
+ * the lock as they may call back to into intnet.
+ */
+ rc = vboxNetFltConnectIt(pNew, pSwitchPort, ppIfPort);
+ if (RT_SUCCESS(rc))
+ {
+ RTSemFastMutexRelease(pGlobals->hFastMtx);
+ Assert(*ppIfPort == &pNew->MyPort);
+ return rc;
+ }
+
+ /* Bail out (failed). */
+ vboxNetFltOsDeleteInstance(pNew);
+#endif /* !VBOXNETFLT_STATIC_CONFIG */
+ }
+ vboxNetFltUnlinkLocked(pGlobals, pNew);
+ }
+ else
+ rc = VERR_INTNET_FLT_IF_BUSY;
+ RTSemFastMutexRelease(pGlobals->hFastMtx);
+ }
+ }
+ RTSemEventDestroy(pNew->hEventIdle);
+ }
+ RTSpinlockDestroy(pNew->hSpinlock);
+ }
+
+ RTMemFree(pNew);
+ return rc;
+}
+
+
+#ifdef VBOXNETFLT_STATIC_CONFIG
+/**
+ * Searches for the NetFlt instance by its name and creates the new one if not found.
+ *
+ * @returns VBox status code.
+ * @retval VINF_SUCCESS and *ppInstance if a new instance was created.
+ * @retval VINF_ALREADY_INITIALIZED and *ppInstance if an instance already exists.
+ *
+ * @param pGlobal Pointer to the globals.
+ * @param pszName The instance name.
+ * @param ppInstance Where to return the instance pointer on success.
+ * @param pvContext Context which needs to be passed along to vboxNetFltOsInitInstance.
+ */
+DECLHIDDEN(int) vboxNetFltSearchCreateInstance(PVBOXNETFLTGLOBALS pGlobals, const char *pszName, PVBOXNETFLTINS *ppInstance, void *pvContext)
+{
+ PINTNETTRUNKIFPORT pIfPort;
+ PVBOXNETFLTINS pCur;
+ VBOXNETFTLINSSTATE enmState;
+ int rc;
+
+ *ppInstance = NULL;
+ rc = RTSemFastMutexRequest(pGlobals->hFastMtx);
+ AssertRCReturn(rc, rc);
+
+ /*
+ * Look for an existing instance in the list.
+ *
+ * There might be an existing one in the list if the driver was unbound
+ * while it was connected to an internal network. We're running into
+ * a destruction race that is a bit similar to the one in
+ * vboxNetFltFactoryCreateAndConnect, only the roles are reversed
+ * and we're not in a position to back down. Instead of backing down
+ * we'll delay a bit giving the other thread time to complete the
+ * destructor.
+ */
+ pCur = vboxNetFltFindInstanceLocked(pGlobals, pszName);
+ while (pCur)
+ {
+ uint32_t cRefs = ASMAtomicIncU32(&pCur->cRefs);
+ if (cRefs > 1)
+ {
+ enmState = vboxNetFltGetState(pCur);
+ switch (enmState)
+ {
+ case kVBoxNetFltInsState_Unconnected:
+ case kVBoxNetFltInsState_Connected:
+ case kVBoxNetFltInsState_Disconnecting:
+ if (pCur->fDisconnectedFromHost)
+ {
+ /* Wait for it to exit the transitional disconnecting
+ state. It might otherwise be running the risk of
+ upsetting the OS specific code... */
+ /** @todo This reconnect stuff should be serialized correctly for static
+ * devices. Shouldn't it? In the dynamic case we're using the INTNET
+ * outbound thunk lock, but that doesn't quite cut it here, or does
+ * it? We could either transition to initializing or make a callback
+ * while owning the mutex here... */
+ if (enmState == kVBoxNetFltInsState_Disconnecting)
+ {
+ do
+ {
+ RTSemFastMutexRelease(pGlobals->hFastMtx);
+ RTThreadSleep(2); /* (2ms) */
+ RTSemFastMutexRequest(pGlobals->hFastMtx);
+ enmState = vboxNetFltGetState(pCur);
+ }
+ while (enmState == kVBoxNetFltInsState_Disconnecting);
+ AssertMsg(enmState == kVBoxNetFltInsState_Unconnected, ("%d\n", enmState));
+ Assert(pCur->fDisconnectedFromHost);
+ }
+
+ RTSemFastMutexRelease(pGlobals->hFastMtx);
+ *ppInstance = pCur;
+ return VINF_ALREADY_INITIALIZED;
+ }
+ /* fall thru */
+
+ default:
+ {
+ bool fDfH = pCur->fDisconnectedFromHost;
+ RTSemFastMutexRelease(pGlobals->hFastMtx);
+ vboxNetFltRelease(pCur, false /* fBusy */);
+ LogRel(("VBoxNetFlt: Huh? An instance of '%s' already exists! [pCur=%p cRefs=%d fDfH=%RTbool enmState=%d]\n",
+ pszName, pCur, cRefs - 1, fDfH, enmState));
+ *ppInstance = NULL;
+ return VERR_INTNET_FLT_IF_BUSY;
+ }
+ }
+ }
+
+ /* Zero references, it's being destroyed. Delay a bit so the destructor
+ can finish its work and try again. (vboxNetFltNewInstance will fail
+ with duplicate name if we don't.) */
+# ifdef RT_STRICT
+ Assert(cRefs == 1);
+ enmState = vboxNetFltGetState(pCur);
+ AssertMsg( enmState == kVBoxNetFltInsState_Unconnected
+ || enmState == kVBoxNetFltInsState_Disconnecting
+ || enmState == kVBoxNetFltInsState_Destroyed, ("%d\n", enmState));
+# endif
+ ASMAtomicDecU32(&pCur->cRefs);
+ RTSemFastMutexRelease(pGlobals->hFastMtx);
+ RTThreadSleep(2); /* (2ms) */
+ rc = RTSemFastMutexRequest(pGlobals->hFastMtx);
+ AssertRCReturn(rc, rc);
+
+ /* try again */
+ pCur = vboxNetFltFindInstanceLocked(pGlobals, pszName);
+ }
+
+ RTSemFastMutexRelease(pGlobals->hFastMtx);
+
+ /*
+ * Try create a new instance.
+ * (fNoPromisc is overridden in the vboxNetFltFactoryCreateAndConnect path, so pass true here.)
+ */
+ rc = vboxNetFltNewInstance(pGlobals, pszName, NULL, true /* fNoPromisc */, pvContext, &pIfPort);
+ if (RT_SUCCESS(rc))
+ *ppInstance = IFPORT_2_VBOXNETFLTINS(pIfPort);
+ else
+ *ppInstance = NULL;
+
+ return rc;
+}
+#endif /* VBOXNETFLT_STATIC_CONFIG */
+
+
+/**
+ * @copydoc INTNETTRUNKFACTORY::pfnCreateAndConnect
+ */
+static DECLCALLBACK(int) vboxNetFltFactoryCreateAndConnect(PINTNETTRUNKFACTORY pIfFactory, const char *pszName,
+ PINTNETTRUNKSWPORT pSwitchPort, uint32_t fFlags,
+ PINTNETTRUNKIFPORT *ppIfPort)
+{
+ PVBOXNETFLTGLOBALS pGlobals = (PVBOXNETFLTGLOBALS)((uint8_t *)pIfFactory - RT_UOFFSETOF(VBOXNETFLTGLOBALS, TrunkFactory));
+ PVBOXNETFLTINS pCur;
+ int rc;
+
+ LogFlow(("vboxNetFltFactoryCreateAndConnect: pszName=%p:{%s} fFlags=%#x\n", pszName, pszName, fFlags));
+ Assert(pGlobals->cFactoryRefs > 0);
+ AssertMsgReturn(!(fFlags & ~(INTNETTRUNKFACTORY_FLAG_NO_PROMISC)),
+ ("%#x\n", fFlags), VERR_INVALID_PARAMETER);
+
+ /*
+ * Static: Find instance, check if busy, connect if not.
+ * Dynamic: Check for duplicate / busy interface instance.
+ */
+ rc = RTSemFastMutexRequest(pGlobals->hFastMtx);
+ AssertRCReturn(rc, rc);
+
+//#if defined(VBOXNETADP) && defined(RT_OS_WINDOWS)
+// /* temporary hack to pick up the first adapter */
+// pCur = pGlobals->pInstanceHead; /** @todo Don't for get to remove this temporary hack... :-) */
+//#else
+ pCur = vboxNetFltFindInstanceLocked(pGlobals, pszName);
+//#endif
+ if (pCur)
+ {
+#ifdef VBOXNETFLT_STATIC_CONFIG
+ /* Try grab a reference. If the count had already reached zero we're racing the
+ destructor code and must back down. */
+ uint32_t cRefs = ASMAtomicIncU32(&pCur->cRefs);
+ if (cRefs > 1)
+ {
+ if (vboxNetFltGetState(pCur) == kVBoxNetFltInsState_Unconnected)
+ {
+ pCur->enmTrunkState = INTNETTRUNKIFSTATE_INACTIVE; /** @todo protect me? */
+ pCur->fDisablePromiscuous = !!(fFlags & INTNETTRUNKFACTORY_FLAG_NO_PROMISC);
+ rc = vboxNetFltConnectIt(pCur, pSwitchPort, ppIfPort);
+ if (RT_SUCCESS(rc))
+ pCur = NULL; /* Don't release it, reference given to the caller. */
+ else
+ pCur->enmTrunkState = INTNETTRUNKIFSTATE_DISCONNECTING;
+ }
+ else
+ rc = VERR_INTNET_FLT_IF_BUSY;
+ }
+ else
+ {
+ Assert(cRefs == 1);
+ ASMAtomicDecU32(&pCur->cRefs);
+ pCur = NULL; /* nothing to release */
+ rc = VERR_INTNET_FLT_IF_NOT_FOUND;
+ }
+
+ RTSemFastMutexRelease(pGlobals->hFastMtx);
+ if (pCur)
+ vboxNetFltRelease(pCur, false /* fBusy */);
+#else
+ rc = VERR_INTNET_FLT_IF_BUSY;
+ RTSemFastMutexRelease(pGlobals->hFastMtx);
+#endif
+ LogFlow(("vboxNetFltFactoryCreateAndConnect: returns %Rrc\n", rc));
+ return rc;
+ }
+
+ RTSemFastMutexRelease(pGlobals->hFastMtx);
+
+#ifdef VBOXNETFLT_STATIC_CONFIG
+ rc = VERR_INTNET_FLT_IF_NOT_FOUND;
+#else
+ /*
+ * Dynamically create a new instance.
+ */
+ rc = vboxNetFltNewInstance(pGlobals,
+ pszName,
+ pSwitchPort,
+ !!(fFlags & INTNETTRUNKFACTORY_FLAG_NO_PROMISC),
+ NULL,
+ ppIfPort);
+#endif
+ LogFlow(("vboxNetFltFactoryCreateAndConnect: returns %Rrc\n", rc));
+ return rc;
+}
+
+
+/**
+ * @copydoc INTNETTRUNKFACTORY::pfnRelease
+ */
+static DECLCALLBACK(void) vboxNetFltFactoryRelease(PINTNETTRUNKFACTORY pIfFactory)
+{
+ PVBOXNETFLTGLOBALS pGlobals = (PVBOXNETFLTGLOBALS)((uint8_t *)pIfFactory - RT_UOFFSETOF(VBOXNETFLTGLOBALS, TrunkFactory));
+
+ int32_t cRefs = ASMAtomicDecS32(&pGlobals->cFactoryRefs);
+ Assert(cRefs >= 0); NOREF(cRefs);
+ LogFlow(("vboxNetFltFactoryRelease: cRefs=%d (new)\n", cRefs));
+}
+
+
+/**
+ * Implements the SUPDRV component factor interface query method.
+ *
+ * @returns Pointer to an interface. NULL if not supported.
+ *
+ * @param pSupDrvFactory Pointer to the component factory registration structure.
+ * @param pSession The session - unused.
+ * @param pszInterfaceUuid The factory interface id.
+ */
+static DECLCALLBACK(void *) vboxNetFltQueryFactoryInterface(PCSUPDRVFACTORY pSupDrvFactory, PSUPDRVSESSION pSession,
+ const char *pszInterfaceUuid)
+{
+ PVBOXNETFLTGLOBALS pGlobals = (PVBOXNETFLTGLOBALS)((uint8_t *)pSupDrvFactory - RT_UOFFSETOF(VBOXNETFLTGLOBALS, SupDrvFactory));
+
+ /*
+ * Convert the UUID strings and compare them.
+ */
+ RTUUID UuidReq;
+ int rc = RTUuidFromStr(&UuidReq, pszInterfaceUuid);
+ if (RT_SUCCESS(rc))
+ {
+ if (!RTUuidCompareStr(&UuidReq, INTNETTRUNKFACTORY_UUID_STR))
+ {
+ ASMAtomicIncS32(&pGlobals->cFactoryRefs);
+ return &pGlobals->TrunkFactory;
+ }
+#ifdef LOG_ENABLED
+ /* log legacy queries */
+ /* else if (!RTUuidCompareStr(&UuidReq, INTNETTRUNKFACTORY_V1_UUID_STR))
+ Log(("VBoxNetFlt: V1 factory query\n"));
+ */
+ else
+ Log(("VBoxNetFlt: unknown factory interface query (%s)\n", pszInterfaceUuid));
+#endif
+ }
+ else
+ Log(("VBoxNetFlt: rc=%Rrc, uuid=%s\n", rc, pszInterfaceUuid));
+
+ RT_NOREF1(pSession);
+ return NULL;
+}
+
+
+/**
+ * Checks whether the VBoxNetFlt wossname can be unloaded.
+ *
+ * This will return false if someone is currently using the module.
+ *
+ * @returns true if it's relatively safe to unload it, otherwise false.
+ * @param pGlobals Pointer to the globals.
+ */
+DECLHIDDEN(bool) vboxNetFltCanUnload(PVBOXNETFLTGLOBALS pGlobals)
+{
+ int rc = RTSemFastMutexRequest(pGlobals->hFastMtx);
+ bool fRc = !pGlobals->pInstanceHead
+ && pGlobals->cFactoryRefs <= 0;
+ RTSemFastMutexRelease(pGlobals->hFastMtx);
+ AssertRC(rc);
+ return fRc;
+}
+
+
+/**
+ * Try to close the IDC connection to SUPDRV if established.
+ *
+ * @returns VBox status code.
+ * @retval VINF_SUCCESS on success.
+ * @retval VERR_WRONG_ORDER if we're busy.
+ *
+ * @param pGlobals Pointer to the globals.
+ *
+ * @sa vboxNetFltTryDeleteIdcAndGlobals()
+ */
+DECLHIDDEN(int) vboxNetFltTryDeleteIdc(PVBOXNETFLTGLOBALS pGlobals)
+{
+ int rc;
+
+ Assert(pGlobals->hFastMtx != NIL_RTSEMFASTMUTEX);
+
+ /*
+ * Check before trying to deregister the factory.
+ */
+ if (!vboxNetFltCanUnload(pGlobals))
+ return VERR_WRONG_ORDER;
+
+ if (!pGlobals->fIDCOpen)
+ rc = VINF_SUCCESS;
+ else
+ {
+ /*
+ * Disconnect from SUPDRV and check that nobody raced us,
+ * reconnect if that should happen.
+ */
+ rc = SUPR0IdcComponentDeregisterFactory(&pGlobals->SupDrvIDC, &pGlobals->SupDrvFactory);
+ AssertRC(rc);
+ if (!vboxNetFltCanUnload(pGlobals))
+ {
+ rc = SUPR0IdcComponentRegisterFactory(&pGlobals->SupDrvIDC, &pGlobals->SupDrvFactory);
+ AssertRC(rc);
+ return VERR_WRONG_ORDER;
+ }
+
+ SUPR0IdcClose(&pGlobals->SupDrvIDC);
+ pGlobals->fIDCOpen = false;
+ }
+
+ return rc;
+}
+
+
+/**
+ * Establishes the IDC connection to SUPDRV and registers our component factory.
+ *
+ * @returns VBox status code.
+ * @param pGlobals Pointer to the globals.
+ * @sa vboxNetFltInitGlobalsAndIdc().
+ */
+DECLHIDDEN(int) vboxNetFltInitIdc(PVBOXNETFLTGLOBALS pGlobals)
+{
+ int rc;
+ Assert(!pGlobals->fIDCOpen);
+
+ /*
+ * Establish a connection to SUPDRV and register our component factory.
+ */
+ rc = SUPR0IdcOpen(&pGlobals->SupDrvIDC, 0 /* iReqVersion = default */, 0 /* iMinVersion = default */, NULL, NULL, NULL);
+ if (RT_SUCCESS(rc))
+ {
+ rc = SUPR0IdcComponentRegisterFactory(&pGlobals->SupDrvIDC, &pGlobals->SupDrvFactory);
+ if (RT_SUCCESS(rc))
+ {
+ pGlobals->fIDCOpen = true;
+ Log(("VBoxNetFlt: pSession=%p\n", SUPR0IdcGetSession(&pGlobals->SupDrvIDC)));
+ return rc;
+ }
+
+ /* bail out. */
+ LogRel(("VBoxNetFlt: Failed to register component factory, rc=%Rrc\n", rc));
+ SUPR0IdcClose(&pGlobals->SupDrvIDC);
+ }
+
+ return rc;
+}
+
+
+/**
+ * Deletes the globals.
+ *
+ * This must be called after the IDC connection has been closed,
+ * see vboxNetFltTryDeleteIdc().
+ *
+ * @param pGlobals Pointer to the globals.
+ * @sa vboxNetFltTryDeleteIdcAndGlobals()
+ */
+DECLHIDDEN(void) vboxNetFltDeleteGlobals(PVBOXNETFLTGLOBALS pGlobals)
+{
+ Assert(!pGlobals->fIDCOpen);
+
+ /*
+ * Release resources.
+ */
+ RTSemFastMutexDestroy(pGlobals->hFastMtx);
+ pGlobals->hFastMtx = NIL_RTSEMFASTMUTEX;
+}
+
+
+/**
+ * Initializes the globals.
+ *
+ * @returns VBox status code.
+ * @param pGlobals Pointer to the globals.
+ * @sa vboxNetFltInitGlobalsAndIdc().
+ */
+DECLHIDDEN(int) vboxNetFltInitGlobals(PVBOXNETFLTGLOBALS pGlobals)
+{
+ /*
+ * Initialize the common portions of the structure.
+ */
+ int rc = RTSemFastMutexCreate(&pGlobals->hFastMtx);
+ if (RT_SUCCESS(rc))
+ {
+ pGlobals->pInstanceHead = NULL;
+
+ pGlobals->TrunkFactory.pfnRelease = vboxNetFltFactoryRelease;
+ pGlobals->TrunkFactory.pfnCreateAndConnect = vboxNetFltFactoryCreateAndConnect;
+#if defined(RT_OS_WINDOWS) && defined(VBOXNETADP)
+ memcpy(pGlobals->SupDrvFactory.szName, "VBoxNetAdp", sizeof("VBoxNetAdp"));
+#else
+ memcpy(pGlobals->SupDrvFactory.szName, "VBoxNetFlt", sizeof("VBoxNetFlt"));
+#endif
+ pGlobals->SupDrvFactory.pfnQueryFactoryInterface = vboxNetFltQueryFactoryInterface;
+ pGlobals->fIDCOpen = false;
+
+ return rc;
+ }
+
+ return rc;
+}
+
+
+/**
+ * Called by the native part when the OS wants the driver to unload.
+ *
+ * @returns VINF_SUCCESS on success, VERR_WRONG_ORDER if we're busy.
+ *
+ * @param pGlobals Pointer to the globals.
+ */
+DECLHIDDEN(int) vboxNetFltTryDeleteIdcAndGlobals(PVBOXNETFLTGLOBALS pGlobals)
+{
+ int rc = vboxNetFltTryDeleteIdc(pGlobals);
+ if (RT_SUCCESS(rc))
+ vboxNetFltDeleteGlobals(pGlobals);
+ return rc;
+}
+
+
+/**
+ * Called by the native driver/kext module initialization routine.
+ *
+ * It will initialize the common parts of the globals, assuming the caller
+ * has already taken care of the OS specific bits, and establish the IDC
+ * connection to SUPDRV.
+ *
+ * @returns VBox status code.
+ * @param pGlobals Pointer to the globals.
+ */
+DECLHIDDEN(int) vboxNetFltInitGlobalsAndIdc(PVBOXNETFLTGLOBALS pGlobals)
+{
+ /*
+ * Initialize the common portions of the structure.
+ */
+ int rc = vboxNetFltInitGlobals(pGlobals);
+ if (RT_SUCCESS(rc))
+ {
+ rc = vboxNetFltInitIdc(pGlobals);
+ if (RT_SUCCESS(rc))
+ return rc;
+
+ /* bail out. */
+ vboxNetFltDeleteGlobals(pGlobals);
+ }
+
+ return rc;
+}
+
diff --git a/src/VBox/HostDrivers/VBoxNetFlt/VBoxNetFlt.rc b/src/VBox/HostDrivers/VBoxNetFlt/VBoxNetFlt.rc
new file mode 100644
index 00000000..0205307a
--- /dev/null
+++ b/src/VBox/HostDrivers/VBoxNetFlt/VBoxNetFlt.rc
@@ -0,0 +1,69 @@
+/* $Id: VBoxNetFlt.rc $ */
+/** @file
+ * VBoxNetFlt - Resource file containing version info and icon.
+ */
+/*
+ * Copyright (C) 2014-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+#include <windows.h>
+#include <VBox/version.h>
+
+LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
+
+VS_VERSION_INFO VERSIONINFO
+ FILEVERSION VBOX_RC_FILE_VERSION
+ PRODUCTVERSION VBOX_RC_FILE_VERSION
+ FILEFLAGSMASK VS_FFI_FILEFLAGSMASK
+ FILEFLAGS VBOX_RC_FILE_FLAGS
+ FILEOS VBOX_RC_FILE_OS
+ FILETYPE VBOX_RC_TYPE_DRV
+ FILESUBTYPE VFT2_DRV_NETWORK
+BEGIN
+ BLOCK "StringFileInfo"
+ BEGIN
+ BLOCK "040904b0"
+ BEGIN
+ VALUE "FileDescription", "VirtualBox NDIS 6.0 Lightweight Filter Driver\0"
+ VALUE "InternalName", "VBoxNetLwf\0"
+ VALUE "OriginalFilename", "VBoxNetLwf.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_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/HostDrivers/VBoxNetFlt/VBoxNetFltInternal.h b/src/VBox/HostDrivers/VBoxNetFlt/VBoxNetFltInternal.h
new file mode 100644
index 00000000..1cff0981
--- /dev/null
+++ b/src/VBox/HostDrivers/VBoxNetFlt/VBoxNetFltInternal.h
@@ -0,0 +1,499 @@
+/* $Id: VBoxNetFltInternal.h $ */
+/** @file
+ * VBoxNetFlt - Network Filter Driver (Host), Internal Header.
+ */
+
+/*
+ * Copyright (C) 2008-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+#ifndef VBOX_INCLUDED_SRC_VBoxNetFlt_VBoxNetFltInternal_h
+#define VBOX_INCLUDED_SRC_VBoxNetFlt_VBoxNetFltInternal_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+#include <VBox/sup.h>
+#include <VBox/intnet.h>
+#include <iprt/semaphore.h>
+#include <iprt/assert.h>
+
+
+RT_C_DECLS_BEGIN
+
+/** Pointer to the globals. */
+typedef struct VBOXNETFLTGLOBALS *PVBOXNETFLTGLOBALS;
+
+
+/**
+ * The state of a filter driver instance.
+ *
+ * The state machine differs a bit between the platforms because of
+ * the way we hook into the stack. On some hosts we can dynamically
+ * attach when required (on CreateInstance) and on others we will
+ * have to connect when the network stack is bound up. These modes
+ * are called static and dynamic config and governed at compile time
+ * by the VBOXNETFLT_STATIC_CONFIG define.
+ *
+ * See sec_netflt_msc for more details on locking and synchronization.
+ */
+typedef enum VBOXNETFTLINSSTATE
+{
+ /** The usual invalid state. */
+ kVBoxNetFltInsState_Invalid = 0,
+ /** Initialization.
+ * We've reserved the interface name but need to attach to the actual
+ * network interface outside the lock to avoid deadlocks.
+ * In the dynamic case this happens during a Create(Instance) call.
+ * In the static case it happens during driver initialization. */
+ kVBoxNetFltInsState_Initializing,
+#ifdef VBOXNETFLT_STATIC_CONFIG
+ /** Unconnected, not hooked up to a switch (static only).
+ * The filter driver instance has been instantiated and hooked up,
+ * waiting to be connected to an internal network. */
+ kVBoxNetFltInsState_Unconnected,
+#endif
+ /** Connected to an internal network. */
+ kVBoxNetFltInsState_Connected,
+ /** Disconnecting from the internal network and possibly the host network interface.
+ * Partly for reasons of deadlock avoidance again. */
+ kVBoxNetFltInsState_Disconnecting,
+ /** The instance has been disconnected from both the host and the internal network. */
+ kVBoxNetFltInsState_Destroyed,
+
+ /** The habitual 32-bit enum hack. */
+ kVBoxNetFltInsState_32BitHack = 0x7fffffff
+} VBOXNETFTLINSSTATE;
+
+
+/**
+ * The per-instance data of the VBox filter driver.
+ *
+ * This is data associated with a network interface / NIC / wossname which
+ * the filter driver has been or may be attached to. When possible it is
+ * attached dynamically, but this may not be possible on all OSes so we have
+ * to be flexible about things.
+ *
+ * A network interface / NIC / wossname can only have one filter driver
+ * instance attached to it. So, attempts at connecting an internal network
+ * to an interface that's already in use (connected to another internal network)
+ * will result in a VERR_SHARING_VIOLATION.
+ *
+ * Only one internal network can connect to a filter driver instance.
+ */
+typedef struct VBOXNETFLTINS
+{
+ /** Pointer to the next interface in the list. (VBOXNETFLTGLOBAL::pInstanceHead) */
+ struct VBOXNETFLTINS *pNext;
+ /** Our RJ-45 port.
+ * This is what the internal network plugs into. */
+ INTNETTRUNKIFPORT MyPort;
+ /** The RJ-45 port on the INTNET "switch".
+ * This is what we're connected to. */
+ PINTNETTRUNKSWPORT pSwitchPort;
+ /** Pointer to the globals. */
+ PVBOXNETFLTGLOBALS pGlobals;
+
+ /** The spinlock protecting the state variables and host interface handle. */
+ RTSPINLOCK hSpinlock;
+ /** The current interface state. */
+ VBOXNETFTLINSSTATE volatile enmState;
+ /** The trunk state. */
+ INTNETTRUNKIFSTATE volatile enmTrunkState;
+ bool volatile fActive;
+ /** Disconnected from the host network interface. */
+ bool volatile fDisconnectedFromHost;
+ /** Rediscovery is pending.
+ * cBusy will never reach zero during rediscovery, so which
+ * takes care of serializing rediscovery and disconnecting. */
+ bool volatile fRediscoveryPending;
+ /** Whether we should not attempt to set promiscuous mode at all. */
+ bool fDisablePromiscuous;
+#if (ARCH_BITS == 32) && defined(__GNUC__)
+#if 0
+ uint32_t u32Padding; /**< Alignment padding, will assert in ASMAtomicUoWriteU64 otherwise. */
+#endif
+#endif
+ /** The timestamp of the last rediscovery. */
+ uint64_t volatile NanoTSLastRediscovery;
+ /** Reference count. */
+ uint32_t volatile cRefs;
+ /** The busy count.
+ * This counts the number of current callers and pending packet. */
+ uint32_t volatile cBusy;
+ /** The event that is signaled when we go idle and that pfnWaitForIdle blocks on. */
+ RTSEMEVENT hEventIdle;
+
+ /** @todo move MacAddr out of this structure! */
+ union
+ {
+#ifdef VBOXNETFLT_OS_SPECFIC
+ struct
+ {
+# if defined(RT_OS_DARWIN)
+ /** @name Darwin instance data.
+ * @{ */
+ /** Pointer to the darwin network interface we're attached to.
+ * This is treated as highly volatile and should only be read and retained
+ * while owning hSpinlock. Releasing references to this should not be done
+ * while owning it though as we might end up destroying it in some paths. */
+ ifnet_t volatile pIfNet;
+ /** The interface filter handle.
+ * Same access rules as with pIfNet. */
+ interface_filter_t volatile pIfFilter;
+ /** Whether we've need to set promiscuous mode when the interface comes up. */
+ bool volatile fNeedSetPromiscuous;
+ /** Whether we've successfully put the interface into to promiscuous mode.
+ * This is for dealing with the ENETDOWN case. */
+ bool volatile fSetPromiscuous;
+ /** The MAC address of the interface. */
+ RTMAC MacAddr;
+ /** PF_SYSTEM socket to listen for events (XXX: globals?) */
+ socket_t pSysSock;
+ /** @} */
+# elif defined(RT_OS_LINUX)
+ /** @name Linux instance data
+ * @{ */
+ /** Pointer to the device. */
+ struct net_device * volatile pDev;
+ /** MTU of host's interface. */
+ uint32_t cbMtu;
+ /** Whether we've successfully put the interface into to promiscuous mode.
+ * This is for dealing with the ENETDOWN case. */
+ bool volatile fPromiscuousSet;
+ /** Whether device exists and physically attached. */
+ bool volatile fRegistered;
+ /** Whether our packet handler is installed. */
+ bool volatile fPacketHandler;
+ /** The MAC address of the interface. */
+ RTMAC MacAddr;
+ struct notifier_block Notifier; /* netdevice */
+ struct notifier_block NotifierIPv4;
+ struct notifier_block NotifierIPv6;
+ struct packet_type PacketType;
+# ifndef VBOXNETFLT_LINUX_NO_XMIT_QUEUE
+ struct sk_buff_head XmitQueue;
+ struct work_struct XmitTask;
+# endif
+ /** @} */
+# elif defined(RT_OS_SOLARIS)
+ /** @name Solaris instance data.
+ * @{ */
+# ifdef VBOX_WITH_NETFLT_CROSSBOW
+ /** Whether the underlying interface is a VNIC or not. */
+ bool fIsVNIC;
+ /** Whether the underlying interface is a VNIC template or not. */
+ bool fIsVNICTemplate;
+ /** Handle to list of created VNICs. */
+ list_t hVNICs;
+ /** The MAC address of the host interface. */
+ RTMAC MacAddr;
+ /** Handle of this interface (lower MAC). */
+ mac_handle_t hInterface;
+ /** Handle to link state notifier. */
+ mac_notify_handle_t hNotify;
+# else
+ /** Pointer to the bound IPv4 stream. */
+ struct vboxnetflt_stream_t * volatile pIp4Stream;
+ /** Pointer to the bound IPv6 stream. */
+ struct vboxnetflt_stream_t * volatile pIp6Stream;
+ /** Pointer to the bound ARP stream. */
+ struct vboxnetflt_stream_t * volatile pArpStream;
+ /** Pointer to the unbound promiscuous stream. */
+ struct vboxnetflt_promisc_stream_t * volatile pPromiscStream;
+ /** Whether we are attaching to IPv6 stream dynamically now. */
+ bool volatile fAttaching;
+ /** Whether this is a VLAN interface or not. */
+ bool volatile fVLAN;
+ /** Layered device handle to the interface. */
+ ldi_handle_t hIface;
+ /** The MAC address of the interface. */
+ RTMAC MacAddr;
+ /** Mutex protection used for loopback. */
+ kmutex_t hMtx;
+ /** Mutex protection used for dynamic IPv6 attaches. */
+ RTSEMFASTMUTEX hPollMtx;
+# endif
+ /** @} */
+# elif defined(RT_OS_FREEBSD)
+ /** @name FreeBSD instance data.
+ * @{ */
+ /** Interface handle */
+ struct ifnet *ifp;
+ /** Netgraph node handle */
+ node_p node;
+ /** Input hook */
+ hook_p input;
+ /** Output hook */
+ hook_p output;
+ /** Original interface flags */
+ unsigned int flags;
+ /** Input queue */
+ struct ifqueue inq;
+ /** Output queue */
+ struct ifqueue outq;
+ /** Input task */
+ struct task tskin;
+ /** Output task */
+ struct task tskout;
+ /** The MAC address of the interface. */
+ RTMAC MacAddr;
+ /** @} */
+# elif defined(RT_OS_WINDOWS)
+ /** @name Windows instance data.
+ * @{ */
+ /** Filter driver device context. */
+ VBOXNETFLTWIN WinIf;
+
+ volatile uint32_t cModeNetFltRefs;
+ volatile uint32_t cModePassThruRefs;
+#ifndef VBOXNETFLT_NO_PACKET_QUEUE
+ /** Packet worker thread info */
+ PACKET_QUEUE_WORKER PacketQueueWorker;
+#endif
+ /** The MAC address of the interface. Caching MAC for performance reasons. */
+ RTMAC MacAddr;
+ /** mutex used to synchronize WinIf init/deinit */
+ RTSEMMUTEX hWinIfMutex;
+ /** @} */
+# else
+# error "PORTME"
+# endif
+ } s;
+#endif
+ /** Padding. */
+#if defined(RT_OS_WINDOWS)
+# if defined(VBOX_NETFLT_ONDEMAND_BIND)
+ uint8_t abPadding[192];
+# elif defined(VBOXNETADP)
+ uint8_t abPadding[256];
+# else
+ uint8_t abPadding[1024];
+# endif
+#elif defined(RT_OS_LINUX)
+ uint8_t abPadding[320];
+#elif defined(RT_OS_FREEBSD)
+ uint8_t abPadding[320];
+#else
+ uint8_t abPadding[128];
+#endif
+ } u;
+
+ /** The interface name. */
+ char szName[1];
+} VBOXNETFLTINS;
+/** Pointer to the instance data of a host network filter driver. */
+typedef struct VBOXNETFLTINS *PVBOXNETFLTINS;
+
+AssertCompileMemberAlignment(VBOXNETFLTINS, NanoTSLastRediscovery, 8);
+#ifdef VBOXNETFLT_OS_SPECFIC
+AssertCompile(RT_SIZEOFMEMB(VBOXNETFLTINS, u.s) <= RT_SIZEOFMEMB(VBOXNETFLTINS, u.abPadding));
+#endif
+
+
+/**
+ * The global data of the VBox filter driver.
+ *
+ * This contains the bit required for communicating with support driver, VBoxDrv
+ * (start out as SupDrv).
+ */
+typedef struct VBOXNETFLTGLOBALS
+{
+ /** Mutex protecting the list of instances and state changes. */
+ RTSEMFASTMUTEX hFastMtx;
+ /** Pointer to a list of instance data. */
+ PVBOXNETFLTINS pInstanceHead;
+
+ /** The INTNET trunk network interface factory. */
+ INTNETTRUNKFACTORY TrunkFactory;
+ /** The SUPDRV component factory registration. */
+ SUPDRVFACTORY SupDrvFactory;
+ /** The number of current factory references. */
+ int32_t volatile cFactoryRefs;
+ /** Whether the IDC connection is open or not.
+ * This is only for cleaning up correctly after the separate IDC init on Windows. */
+ bool fIDCOpen;
+ /** The SUPDRV IDC handle (opaque struct). */
+ SUPDRVIDCHANDLE SupDrvIDC;
+} VBOXNETFLTGLOBALS;
+
+
+DECLHIDDEN(int) vboxNetFltInitGlobalsAndIdc(PVBOXNETFLTGLOBALS pGlobals);
+DECLHIDDEN(int) vboxNetFltInitGlobals(PVBOXNETFLTGLOBALS pGlobals);
+DECLHIDDEN(int) vboxNetFltInitIdc(PVBOXNETFLTGLOBALS pGlobals);
+DECLHIDDEN(int) vboxNetFltTryDeleteIdcAndGlobals(PVBOXNETFLTGLOBALS pGlobals);
+DECLHIDDEN(void) vboxNetFltDeleteGlobals(PVBOXNETFLTGLOBALS pGlobals);
+DECLHIDDEN(int) vboxNetFltTryDeleteIdc(PVBOXNETFLTGLOBALS pGlobals);
+
+DECLHIDDEN(bool) vboxNetFltCanUnload(PVBOXNETFLTGLOBALS pGlobals);
+DECLHIDDEN(PVBOXNETFLTINS) vboxNetFltFindInstance(PVBOXNETFLTGLOBALS pGlobals, const char *pszName);
+
+DECL_HIDDEN_CALLBACK(void) vboxNetFltPortReleaseBusy(PINTNETTRUNKIFPORT pIfPort);
+DECLHIDDEN(void) vboxNetFltRetain(PVBOXNETFLTINS pThis, bool fBusy);
+DECLHIDDEN(bool) vboxNetFltTryRetainBusyActive(PVBOXNETFLTINS pThis);
+DECLHIDDEN(bool) vboxNetFltTryRetainBusyNotDisconnected(PVBOXNETFLTINS pThis);
+DECLHIDDEN(void) vboxNetFltRelease(PVBOXNETFLTINS pThis, bool fBusy);
+
+#ifdef VBOXNETFLT_STATIC_CONFIG
+DECLHIDDEN(int) vboxNetFltSearchCreateInstance(PVBOXNETFLTGLOBALS pGlobals, const char *pszName, PVBOXNETFLTINS *ppInstance, void * pContext);
+#endif
+
+
+
+/** @name The OS specific interface.
+ * @{ */
+/**
+ * Try rediscover the host interface.
+ *
+ * This is called periodically from the transmit path if we're marked as
+ * disconnected from the host. There is no chance of a race here.
+ *
+ * @returns true if the interface was successfully rediscovered and reattach,
+ * otherwise false.
+ * @param pThis The new instance.
+ */
+DECLHIDDEN(bool) vboxNetFltOsMaybeRediscovered(PVBOXNETFLTINS pThis);
+
+/**
+ * Transmits a frame.
+ *
+ * @return IPRT status code.
+ * @param pThis The new instance.
+ * @param pvIfData Pointer to the host-private interface data.
+ * @param pSG The (scatter/)gather list.
+ * @param fDst The destination mask. At least one bit will be set.
+ *
+ * @remarks Owns the out-bound trunk port semaphore.
+ */
+DECLHIDDEN(int) vboxNetFltPortOsXmit(PVBOXNETFLTINS pThis, void *pvIfData, PINTNETSG pSG, uint32_t fDst);
+
+/**
+ * This is called when activating or suspending the instance.
+ *
+ * Use this method to enable and disable promiscuous mode on
+ * the interface to prevent unnecessary interrupt load.
+ *
+ * It is only called when the state changes.
+ *
+ * @param pThis The instance.
+ * @param fActive Whether to active (@c true) or deactive.
+ *
+ * @remarks Owns the lock for the out-bound trunk port.
+ */
+DECLHIDDEN(void) vboxNetFltPortOsSetActive(PVBOXNETFLTINS pThis, bool fActive);
+
+/**
+ * This is called when a network interface has obtained a new MAC address.
+ *
+ * @param pThis The instance.
+ * @param pvIfData Pointer to the private interface data.
+ * @param pMac Pointer to the new MAC address.
+ */
+DECLHIDDEN(void) vboxNetFltPortOsNotifyMacAddress(PVBOXNETFLTINS pThis, void *pvIfData, PCRTMAC pMac);
+
+/**
+ * This is called when an interface is connected to the network.
+ *
+ * @return IPRT status code.
+ * @param pThis The instance.
+ * @param pvIf Pointer to the interface.
+ * @param ppvIfData Where to store the private interface data.
+ */
+DECLHIDDEN(int) vboxNetFltPortOsConnectInterface(PVBOXNETFLTINS pThis, void *pvIf, void **ppvIfData);
+
+/**
+ * This is called when a VM host disconnects from the network.
+ *
+ * @param pThis The instance.
+ * @param pvIfData Pointer to the private interface data.
+ */
+DECLHIDDEN(int) vboxNetFltPortOsDisconnectInterface(PVBOXNETFLTINS pThis, void *pvIfData);
+
+/**
+ * This is called to when disconnecting from a network.
+ *
+ * @return IPRT status code.
+ * @param pThis The new instance.
+ *
+ * @remarks May own the semaphores for the global list, the network lock and the out-bound trunk port.
+ */
+DECLHIDDEN(int) vboxNetFltOsDisconnectIt(PVBOXNETFLTINS pThis);
+
+/**
+ * This is called to when connecting to a network.
+ *
+ * @return IPRT status code.
+ * @param pThis The new instance.
+ *
+ * @remarks Owns the semaphores for the global list, the network lock and the out-bound trunk port.
+ */
+DECLHIDDEN(int) vboxNetFltOsConnectIt(PVBOXNETFLTINS pThis);
+
+/**
+ * Counter part to vboxNetFltOsInitInstance().
+ *
+ * @param pThis The new instance.
+ *
+ * @remarks May own the semaphores for the global list, the network lock and the out-bound trunk port.
+ */
+DECLHIDDEN(void) vboxNetFltOsDeleteInstance(PVBOXNETFLTINS pThis);
+
+/**
+ * This is called to attach to the actual host interface
+ * after linking the instance into the list.
+ *
+ * The MAC address as well promiscuousness and GSO capabilities should be
+ * reported by this function.
+ *
+ * @return IPRT status code.
+ * @param pThis The new instance.
+ * @param pvContext The user supplied context in the static config only.
+ * NULL in the dynamic config.
+ *
+ * @remarks Owns no locks.
+ */
+DECLHIDDEN(int) vboxNetFltOsInitInstance(PVBOXNETFLTINS pThis, void *pvContext);
+
+/**
+ * This is called to perform structure initializations.
+ *
+ * @return IPRT status code.
+ * @param pThis The new instance.
+ *
+ * @remarks Owns no locks.
+ */
+DECLHIDDEN(int) vboxNetFltOsPreInitInstance(PVBOXNETFLTINS pThis);
+/** @} */
+
+
+RT_C_DECLS_END
+
+#endif /* !VBOX_INCLUDED_SRC_VBoxNetFlt_VBoxNetFltInternal_h */
+
diff --git a/src/VBox/HostDrivers/VBoxNetFlt/darwin/Info.plist b/src/VBox/HostDrivers/VBoxNetFlt/darwin/Info.plist
new file mode 100644
index 00000000..20338308
--- /dev/null
+++ b/src/VBox/HostDrivers/VBoxNetFlt/darwin/Info.plist
@@ -0,0 +1,38 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+ <key>CFBundleDevelopmentRegion</key> <string>English</string>
+ <key>CFBundleExecutable</key> <string>VBoxNetFlt</string>
+ <key>CFBundleIdentifier</key> <string>org.virtualbox.kext.VBoxNetFlt</string>
+ <key>CFBundleInfoDictionaryVersion</key> <string>6.0</string>
+ <key>CFBundleName</key> <string>VBoxNetFlt</string>
+ <key>CFBundlePackageType</key> <string>KEXT</string>
+ <key>CFBundleSignature</key> <string>????</string>
+ <key>CFBundleGetInfoString</key> <string>@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>IOKitPersonalities</key>
+ <dict>
+ </dict>
+ <key>OSBundleLibraries</key>
+ <dict>
+ <key>com.apple.kpi.bsd</key> <string>8.8.1</string>
+ <key>com.apple.kpi.mach</key> <string>8.8.1</string>
+ <key>com.apple.kpi.libkern</key> <string>8.8.1</string>
+ <key>com.apple.kpi.unsupported</key> <string>8.8.1</string>
+ <key>com.apple.kpi.iokit</key> <string>8.8.1</string>
+ <key>org.virtualbox.kext.VBoxDrv</key> <string>@VBOX_VERSION_MAJOR@.@VBOX_VERSION_MINOR@.@VBOX_VERSION_BUILD@</string>
+ </dict>
+ <key>OSBundleLibraries_x86_64</key>
+ <dict>
+ <key>com.apple.kpi.bsd</key> <string>10.0.0d3</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.unsupported</key> <string>10.0.0d3</string>
+ <key>com.apple.kpi.iokit</key> <string>10.0.0d3</string>
+ <key>org.virtualbox.kext.VBoxDrv</key> <string>@VBOX_VERSION_MAJOR@.@VBOX_VERSION_MINOR@.@VBOX_VERSION_BUILD@</string>
+ </dict>
+</dict>
+</plist>
+
diff --git a/src/VBox/HostDrivers/VBoxNetFlt/darwin/Makefile.kup b/src/VBox/HostDrivers/VBoxNetFlt/darwin/Makefile.kup
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/src/VBox/HostDrivers/VBoxNetFlt/darwin/Makefile.kup
diff --git a/src/VBox/HostDrivers/VBoxNetFlt/darwin/VBoxNetFlt-darwin.cpp b/src/VBox/HostDrivers/VBoxNetFlt/darwin/VBoxNetFlt-darwin.cpp
new file mode 100644
index 00000000..3a14a8c5
--- /dev/null
+++ b/src/VBox/HostDrivers/VBoxNetFlt/darwin/VBoxNetFlt-darwin.cpp
@@ -0,0 +1,1754 @@
+/* $Id: VBoxNetFlt-darwin.cpp $ */
+/** @file
+ * VBoxNetFlt - Network Filter Driver (Host), Darwin Specific Code.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#define LOG_GROUP LOG_GROUP_NET_FLT_DRV
+#include "../../../Runtime/r0drv/darwin/the-darwin-kernel.h"
+
+#include <VBox/log.h>
+#include <VBox/err.h>
+#include <VBox/intnetinline.h>
+#include <VBox/version.h>
+#include <iprt/initterm.h>
+#include <iprt/assert.h>
+#include <iprt/spinlock.h>
+#include <iprt/semaphore.h>
+#include <iprt/process.h>
+#include <iprt/alloc.h>
+#include <iprt/alloca.h>
+#include <iprt/time.h>
+#include <iprt/net.h>
+#include <iprt/thread.h>
+
+#include "../../darwin/VBoxNetSend.h"
+
+#include <mach/kmod.h>
+#include <sys/conf.h>
+#include <sys/errno.h>
+#include <sys/ioccom.h>
+#include <sys/filio.h>
+#include <sys/malloc.h>
+#include <sys/proc.h>
+#include <sys/socket.h>
+#include <sys/sockio.h>
+#include <sys/kern_event.h>
+#if MAC_OS_X_VERSION_MIN_REQUIRED >= 101500 /* The 10.15 SDK has a slightly butchered API deprecation attempt. */
+# pragma clang diagnostic push
+# pragma clang diagnostic ignored "-Wmacro-redefined" /* Each header redefines __NKE_API_DEPRECATED. */
+# pragma clang diagnostic ignored "-Wmissing-declarations" /* Misplaced __NKE_API_DEPRECATED; in kpi_mbuf.h. */
+# include <sys/kpi_socket.h>
+# include <net/kpi_interface.h>
+# include <sys/kpi_mbuf.h>
+# include <net/kpi_interfacefilter.h>
+# pragma clang diagnostic pop
+#else /* < 10.15*/
+# include <sys/kpi_socket.h>
+# include <net/kpi_interface.h>
+RT_C_DECLS_BEGIN /* Buggy 10.4 headers, fixed in 10.5. */
+# include <sys/kpi_mbuf.h>
+# include <net/kpi_interfacefilter.h>
+RT_C_DECLS_END
+#endif /* < 10.15*/
+
+
+#include <net/if.h>
+#include <net/if_var.h>
+RT_C_DECLS_BEGIN
+#include <net/bpf.h>
+RT_C_DECLS_END
+#include <netinet/in.h>
+#include <netinet/in_var.h>
+#include <netinet6/in6_var.h>
+
+#define VBOXNETFLT_OS_SPECFIC 1
+#include "../VBoxNetFltInternal.h"
+
+
+/*********************************************************************************************************************************
+* Defined Constants And Macros *
+*********************************************************************************************************************************/
+/** The maximum number of SG segments.
+ * Used to prevent stack overflow and similar bad stuff. */
+#define VBOXNETFLT_DARWIN_MAX_SEGS 32
+
+#if 0
+/** For testing extremely segmented frames. */
+#define VBOXNETFLT_DARWIN_TEST_SEG_SIZE 14
+#endif
+
+/* XXX: hidden undef #ifdef __APPLE__ */
+#define VBOX_IN_LOOPBACK(addr) (((addr) & IN_CLASSA_NET) == 0x7f000000)
+#define VBOX_IN_LINKLOCAL(addr) (((addr) & IN_CLASSB_NET) == 0xa9fe0000)
+
+
+
+/*********************************************************************************************************************************
+* Internal Functions *
+*********************************************************************************************************************************/
+RT_C_DECLS_BEGIN
+static kern_return_t VBoxNetFltDarwinStart(struct kmod_info *pKModInfo, void *pvData);
+static kern_return_t VBoxNetFltDarwinStop(struct kmod_info *pKModInfo, void *pvData);
+
+static void vboxNetFltDarwinSysSockUpcall(socket_t pSysSock, void *pvData, int fWait);
+RT_C_DECLS_END
+
+
+/*********************************************************************************************************************************
+* Structures and Typedefs *
+*********************************************************************************************************************************/
+/**
+ * The mbuf tag data.
+ *
+ * We have to associate the ethernet header with each packet we're sending
+ * because things like icmp will inherit the tag it self so the tag along
+ * isn't sufficient to identify our mbufs. For the icmp scenario the ethernet
+ * header naturally changes before the packet is send pack, so let check it.
+ */
+typedef struct VBOXNETFLTTAG
+{
+ /** The ethernet header of the outgoing frame. */
+ RTNETETHERHDR EthHdr;
+} VBOXNETFLTTAG;
+/** Pointer to a VBoxNetFlt mbuf tag. */
+typedef VBOXNETFLTTAG *PVBOXNETFLTTAG;
+/** Pointer to a const VBoxNetFlt mbuf tag. */
+typedef VBOXNETFLTTAG const *PCVBOXNETFLTTAG;
+
+
+/*********************************************************************************************************************************
+* Global Variables *
+*********************************************************************************************************************************/
+/**
+ * Declare the module stuff.
+ */
+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(VBoxNetFlt, VBOX_VERSION_STRING, _start, _stop)
+DECL_HIDDEN_DATA(kmod_start_func_t *) _realmain = VBoxNetFltDarwinStart;
+DECL_HIDDEN_DATA(kmod_stop_func_t *) _antimain = VBoxNetFltDarwinStop;
+DECL_HIDDEN_DATA(int) _kext_apple_cc = __APPLE_CC__;
+RT_C_DECLS_END
+
+
+/**
+ * The (common) global data.
+ */
+static VBOXNETFLTGLOBALS g_VBoxNetFltGlobals;
+
+/** The unique tag id for this module.
+ * This is basically a unique string hash that lives on until reboot.
+ * It is used for tagging mbufs. */
+static mbuf_tag_id_t g_idTag;
+
+/** The offset of the struct ifnet::if_pcount variable.
+ * @remarks Initial value is valid for Lion and earlier. We adjust it on attach
+ * for later releases. */
+static unsigned g_offIfNetPCount = sizeof(void *) * (1 /*if_softc*/ + 1 /*if_name*/ + 2 /*if_link*/ + 2 /*if_addrhead*/ + 1 /*if_check_multi*/)
+ + sizeof(u_long) /*if_refcnt*/;
+/** Macro for accessing ifnet::if_pcount. */
+#define VBOX_GET_PCOUNT(pIfNet) ( *(int *)((uintptr_t)pIfNet + g_offIfNetPCount) )
+/** The size of area of ifnet structure we try to locate if_pcount in. */
+#define VBOXNETFLT_DARWIN_IFNET_SIZE 256
+/** Indicates whether g_offIfNetPCount has been adjusted already (no point in
+ * doing it more than once). */
+static bool g_fNetPCountFound = false;
+
+
+/**
+ * Change the promiscuous setting and try spot the changed in @a pIfNet.
+ *
+ * @returns Offset of potential p_count field.
+ * @param pIfNet The interface we're attaching to.
+ * @param iPromisc Whether to enable (1) or disable (0) promiscuous mode.
+ *
+ * @note This implementation relies on if_pcount to be aligned on sizeof(int).
+ */
+static unsigned vboxNetFltDarwinSetAndDiff(ifnet_t pIfNet, int iPromisc)
+{
+ int aiSavedState[VBOXNETFLT_DARWIN_IFNET_SIZE / sizeof(int)];
+ memcpy(aiSavedState, pIfNet, sizeof(aiSavedState));
+
+ ifnet_set_promiscuous(pIfNet, iPromisc);
+
+ int const iDiff = iPromisc ? 1 : -1;
+
+ /*
+ * We assume that ifnet structure will never have less members in front of if_pcount
+ * than it used to have in Lion. If this turns out to be false assumption we will
+ * have to start from zero offset.
+ */
+ for (unsigned i = g_offIfNetPCount / sizeof(int); i < RT_ELEMENTS(aiSavedState); i++)
+ if (((int*)pIfNet)[i] - aiSavedState[i] == iDiff)
+ return i * sizeof(int);
+
+ return 0;
+}
+
+
+/**
+ * Detect and adjust the offset of ifnet::if_pcount.
+ *
+ * @param pIfNet The interface we're attaching to.
+ */
+static void vboxNetFltDarwinDetectPCountOffset(ifnet_t pIfNet)
+{
+ if (g_fNetPCountFound)
+ return;
+
+ /*
+ * It would be nice to use locking at this point, but it is not available via KPI.
+ * This is why we try several times. At each attempt we modify if_pcount four times
+ * to rule out false detections.
+ */
+ unsigned offTry1, offTry2, offTry3, offTry4;
+ for (int iAttempt = 0; iAttempt < 3; iAttempt++)
+ {
+ offTry1 = vboxNetFltDarwinSetAndDiff(pIfNet, 1);
+ offTry2 = vboxNetFltDarwinSetAndDiff(pIfNet, 1);
+ offTry3 = vboxNetFltDarwinSetAndDiff(pIfNet, 0);
+ offTry4 = vboxNetFltDarwinSetAndDiff(pIfNet, 0);
+ if (offTry1 == offTry2 && offTry2 == offTry3 && offTry3 == offTry4)
+ {
+ if (g_offIfNetPCount != offTry1)
+ {
+ Log(("VBoxNetFltDarwinDetectPCountOffset: Adjusted if_pcount offset to %x from %x.\n", offTry1, g_offIfNetPCount));
+ g_offIfNetPCount = offTry1;
+ g_fNetPCountFound = true;
+ }
+ break;
+ }
+ }
+
+ if (g_offIfNetPCount != offTry1)
+ LogRel(("VBoxNetFlt: Failed to detect promiscuous count, all traffic may reach wire (%x != %x).\n", g_offIfNetPCount, offTry1));
+}
+
+
+/**
+ * Start the kernel module.
+ */
+static kern_return_t VBoxNetFltDarwinStart(struct kmod_info *pKModInfo, void *pvData)
+{
+ RT_NOREF(pKModInfo, pvData);
+
+ /*
+ * Initialize IPRT and find our module tag id.
+ * (IPRT is shared with VBoxDrv, it creates the loggers.)
+ */
+ int rc = RTR0Init(0);
+ if (RT_SUCCESS(rc))
+ {
+ Log(("VBoxNetFltDarwinStart\n"));
+ errno_t err = mbuf_tag_id_find("org.VirtualBox.kext.VBoxFltDrv", &g_idTag);
+ if (!err)
+ {
+ /*
+ * Initialize the globals and connect to the support driver.
+ *
+ * This will call back vboxNetFltOsOpenSupDrv (and maybe vboxNetFltOsCloseSupDrv)
+ * for establishing the connect to the support driver.
+ */
+ memset(&g_VBoxNetFltGlobals, 0, sizeof(g_VBoxNetFltGlobals));
+ rc = vboxNetFltInitGlobalsAndIdc(&g_VBoxNetFltGlobals);
+ if (RT_SUCCESS(rc))
+ {
+ LogRel(("VBoxFltDrv: version " VBOX_VERSION_STRING " r%d\n", VBOX_SVN_REV));
+ return KMOD_RETURN_SUCCESS;
+ }
+
+ LogRel(("VBoxFltDrv: failed to initialize device extension (rc=%d)\n", rc));
+ }
+ else
+ LogRel(("VBoxFltDrv: mbuf_tag_id_find failed, err=%d\n", err));
+ RTR0Term();
+ }
+ else
+ printf("VBoxFltDrv: failed to initialize IPRT (rc=%d)\n", rc);
+
+ memset(&g_VBoxNetFltGlobals, 0, sizeof(g_VBoxNetFltGlobals));
+ return KMOD_RETURN_FAILURE;
+}
+
+
+/**
+ * Stop the kernel module.
+ */
+static kern_return_t VBoxNetFltDarwinStop(struct kmod_info *pKModInfo, void *pvData)
+{
+ RT_NOREF(pKModInfo, pvData);
+ Log(("VBoxNetFltDarwinStop\n"));
+
+ /*
+ * Refuse to unload if anyone is currently using the filter driver.
+ * This is important as I/O kit / xnu will to be able to do usage
+ * tracking for us!
+ */
+ int rc = vboxNetFltTryDeleteIdcAndGlobals(&g_VBoxNetFltGlobals);
+ if (RT_FAILURE(rc))
+ {
+ Log(("VBoxNetFltDarwinStop - failed, busy.\n"));
+ return KMOD_RETURN_FAILURE;
+ }
+
+ /*
+ * Undo the work done during start (in reverse order).
+ */
+ memset(&g_VBoxNetFltGlobals, 0, sizeof(g_VBoxNetFltGlobals));
+
+ RTR0Term();
+
+ return KMOD_RETURN_SUCCESS;
+}
+
+
+/**
+ * Reads and retains the host interface handle.
+ *
+ * @returns The handle, NULL if detached.
+ * @param pThis
+ */
+DECLINLINE(ifnet_t) vboxNetFltDarwinRetainIfNet(PVBOXNETFLTINS pThis)
+{
+ ifnet_t pIfNet = NULL;
+
+ /*
+ * Be careful here to avoid problems racing the detached callback.
+ */
+ RTSpinlockAcquire(pThis->hSpinlock);
+ if (!ASMAtomicUoReadBool(&pThis->fDisconnectedFromHost))
+ {
+ pIfNet = ASMAtomicUoReadPtrT(&pThis->u.s.pIfNet, ifnet_t);
+ if (pIfNet)
+ ifnet_reference(pIfNet);
+ }
+ RTSpinlockRelease(pThis->hSpinlock);
+
+ return pIfNet;
+}
+
+
+/**
+ * Release the host interface handle previously retained
+ * by vboxNetFltDarwinRetainIfNet.
+ *
+ * @param pThis The instance.
+ * @param pIfNet The vboxNetFltDarwinRetainIfNet return value, NULL is fine.
+ */
+DECLINLINE(void) vboxNetFltDarwinReleaseIfNet(PVBOXNETFLTINS pThis, ifnet_t pIfNet)
+{
+ NOREF(pThis);
+ if (pIfNet)
+ ifnet_release(pIfNet);
+}
+
+
+/**
+ * Checks whether this is an mbuf created by vboxNetFltDarwinMBufFromSG,
+ * i.e. a buffer which we're pushing and should be ignored by the filter callbacks.
+ *
+ * @returns true / false accordingly.
+ * @param pThis The instance.
+ * @param pMBuf The mbuf.
+ * @param pvFrame The frame pointer, optional.
+ */
+DECLINLINE(bool) vboxNetFltDarwinMBufIsOur(PVBOXNETFLTINS pThis, mbuf_t pMBuf, void *pvFrame)
+{
+ NOREF(pThis);
+
+ /*
+ * Lookup the tag set by vboxNetFltDarwinMBufFromSG.
+ */
+ PCVBOXNETFLTTAG pTagData;
+ size_t cbTagData;
+ errno_t err = mbuf_tag_find(pMBuf, g_idTag, 0 /* type */, &cbTagData, (void **)&pTagData);
+ if (err)
+ return false;
+ AssertReturn(cbTagData == sizeof(*pTagData), false);
+
+ /*
+ * Dig out the ethernet header from the mbuf.
+ */
+ PCRTNETETHERHDR pEthHdr = (PCRTNETETHERHDR)pvFrame;
+ if (!pEthHdr)
+ pEthHdr = (PCRTNETETHERHDR)mbuf_pkthdr_header(pMBuf);
+ if (!pEthHdr)
+ pEthHdr = (PCRTNETETHERHDR)mbuf_data(pMBuf);
+ /* ASSUMING that there is enough data to work on! */
+ if ( pEthHdr->DstMac.au8[0] != pTagData->EthHdr.DstMac.au8[0]
+ || pEthHdr->DstMac.au8[1] != pTagData->EthHdr.DstMac.au8[1]
+ || pEthHdr->DstMac.au8[2] != pTagData->EthHdr.DstMac.au8[2]
+ || pEthHdr->DstMac.au8[3] != pTagData->EthHdr.DstMac.au8[3]
+ || pEthHdr->DstMac.au8[4] != pTagData->EthHdr.DstMac.au8[4]
+ || pEthHdr->DstMac.au8[5] != pTagData->EthHdr.DstMac.au8[5]
+ || pEthHdr->SrcMac.au8[0] != pTagData->EthHdr.SrcMac.au8[0]
+ || pEthHdr->SrcMac.au8[1] != pTagData->EthHdr.SrcMac.au8[1]
+ || pEthHdr->SrcMac.au8[2] != pTagData->EthHdr.SrcMac.au8[2]
+ || pEthHdr->SrcMac.au8[3] != pTagData->EthHdr.SrcMac.au8[3]
+ || pEthHdr->SrcMac.au8[4] != pTagData->EthHdr.SrcMac.au8[4]
+ || pEthHdr->SrcMac.au8[5] != pTagData->EthHdr.SrcMac.au8[5]
+ || pEthHdr->EtherType != pTagData->EthHdr.EtherType)
+ {
+ Log3(("tagged, but the ethernet header has changed\n"));
+ return false;
+ }
+
+ return true;
+}
+
+
+/**
+ * Internal worker that create a darwin mbuf for a (scatter/)gather list.
+ *
+ * @returns Pointer to the mbuf.
+ * @param pThis The instance.
+ * @param pSG The (scatter/)gather list.
+ */
+static mbuf_t vboxNetFltDarwinMBufFromSG(PVBOXNETFLTINS pThis, PINTNETSG pSG)
+{
+ /// @todo future? mbuf_how_t How = preemption enabled ? MBUF_DONTWAIT : MBUF_WAITOK;
+ mbuf_how_t How = MBUF_WAITOK;
+
+ /*
+ * We need some way of getting back to our instance data when
+ * the mbuf is freed, so use pvUserData for this.
+ * -- this is not relevant anylonger! --
+ */
+ Assert(!pSG->pvUserData || pSG->pvUserData == pThis);
+ Assert(!pSG->pvUserData2);
+ pSG->pvUserData = pThis;
+
+ /*
+ * Allocate a packet and copy over the data.
+ *
+ * Using mbuf_attachcluster() here would've been nice but there are two
+ * issues with it: (1) it's 10.5.x only, and (2) the documentation indicates
+ * that it's not supposed to be used for really external buffers. The 2nd
+ * point might be argued against considering that the only m_clattach user
+ * is mallocs memory for the ext mbuf and not doing what's stated in the docs.
+ * However, it's hard to tell if these m_clattach buffers actually makes it
+ * to the NICs or not, and even if they did, the NIC would need the physical
+ * addresses for the pages they contain and might end up copying the data
+ * to a new mbuf anyway.
+ *
+ * So, in the end it's better to just do it the simple way that will work
+ * 100%, even if it involves some extra work (alloc + copy) we really wished
+ * to avoid.
+ *
+ * Note. We can't make use of the physical addresses on darwin because the
+ * way the mbuf / cluster stuff works (see mbuf_data_to_physical and
+ * mcl_to_paddr).
+ */
+ mbuf_t pPkt = NULL;
+ errno_t err = mbuf_allocpacket(How, pSG->cbTotal, NULL, &pPkt);
+ if (!err)
+ {
+ /* Skip zero sized memory buffers (paranoia). */
+ mbuf_t pCur = pPkt;
+ while (pCur && !mbuf_maxlen(pCur))
+ pCur = mbuf_next(pCur);
+ Assert(pCur);
+
+ /* Set the required packet header attributes. */
+ mbuf_pkthdr_setlen(pPkt, pSG->cbTotal);
+ mbuf_pkthdr_setheader(pPkt, mbuf_data(pCur));
+
+ /* Special case the single buffer copy. */
+ if ( mbuf_next(pCur)
+ && mbuf_maxlen(pCur) >= pSG->cbTotal)
+ {
+ mbuf_setlen(pCur, pSG->cbTotal);
+ IntNetSgRead(pSG, mbuf_data(pCur));
+ }
+ else
+ {
+ /* Multi buffer copying. */
+ size_t cbLeft = pSG->cbTotal;
+ size_t offSrc = 0;
+ while (cbLeft > 0 && pCur)
+ {
+ size_t cb = mbuf_maxlen(pCur);
+ if (cb > cbLeft)
+ cb = cbLeft;
+ mbuf_setlen(pCur, cb);
+ IntNetSgReadEx(pSG, offSrc, cb, mbuf_data(pCur));
+
+ /* advance */
+ offSrc += cb;
+ cbLeft -= cb;
+ pCur = mbuf_next(pCur);
+ }
+ Assert(cbLeft == 0);
+ }
+ if (!err)
+ {
+ /*
+ * Tag the packet and return successfully.
+ */
+ PVBOXNETFLTTAG pTagData;
+ err = mbuf_tag_allocate(pPkt, g_idTag, 0 /* type */, sizeof(VBOXNETFLTTAG) /* tag len */, How, (void **)&pTagData);
+ if (!err)
+ {
+ Assert(pSG->aSegs[0].cb >= sizeof(pTagData->EthHdr));
+ memcpy(&pTagData->EthHdr, pSG->aSegs[0].pv, sizeof(pTagData->EthHdr));
+ return pPkt;
+ }
+
+ /* bailout: */
+ AssertMsg(err == ENOMEM || err == EWOULDBLOCK, ("err=%d\n", err));
+ }
+
+ mbuf_freem(pPkt);
+ }
+ else
+ AssertMsg(err == ENOMEM || err == EWOULDBLOCK, ("err=%d\n", err));
+ pSG->pvUserData = NULL;
+
+ return NULL;
+}
+
+
+/**
+ * Calculates the number of segments required to represent the mbuf.
+ *
+ * @returns Number of segments.
+ * @param pThis The instance.
+ * @param pMBuf The mbuf.
+ * @param pvFrame The frame pointer, optional.
+ */
+DECLINLINE(unsigned) vboxNetFltDarwinMBufCalcSGSegs(PVBOXNETFLTINS pThis, mbuf_t pMBuf, void *pvFrame)
+{
+ NOREF(pThis);
+
+ /*
+ * Count the buffers in the chain.
+ */
+ unsigned cSegs = 0;
+ for (mbuf_t pCur = pMBuf; pCur; pCur = mbuf_next(pCur))
+ if (mbuf_len(pCur))
+ cSegs++;
+ else if ( !cSegs
+ && pvFrame
+ && (uintptr_t)pvFrame - (uintptr_t)mbuf_datastart(pMBuf) < mbuf_maxlen(pMBuf))
+ cSegs++;
+
+#ifdef PADD_RUNT_FRAMES_FROM_HOST
+ /*
+ * Add one buffer if the total is less than the ethernet minimum 60 bytes.
+ * This may allocate a segment too much if the ethernet header is separated,
+ * but that shouldn't harm us much.
+ */
+ if (mbuf_pkthdr_len(pMBuf) < 60)
+ cSegs++;
+#endif
+
+#ifdef VBOXNETFLT_DARWIN_TEST_SEG_SIZE
+ /* maximize the number of segments. */
+ cSegs = RT_MAX(VBOXNETFLT_DARWIN_MAX_SEGS - 1, cSegs);
+#endif
+
+ return cSegs ? cSegs : 1;
+}
+
+
+/**
+ * Initializes a SG list from an mbuf.
+ *
+ * @param pThis The instance.
+ * @param pMBuf The mbuf.
+ * @param pSG The SG.
+ * @param pvFrame The frame pointer, optional.
+ * @param cSegs The number of segments allocated for the SG.
+ * This should match the number in the mbuf exactly!
+ * @param fSrc The source of the frame.
+ */
+DECLINLINE(void) vboxNetFltDarwinMBufToSG(PVBOXNETFLTINS pThis, mbuf_t pMBuf, void *pvFrame, PINTNETSG pSG, unsigned cSegs,
+ uint32_t fSrc)
+{
+ RT_NOREF(pThis, fSrc);
+
+ /*
+ * Walk the chain and convert the buffers to segments. Works INTNETSG::cbTotal.
+ */
+ unsigned iSeg = 0;
+ IntNetSgInitTempSegs(pSG, 0 /*cbTotal*/, cSegs, 0 /*cSegsUsed*/);
+ for (mbuf_t pCur = pMBuf; pCur; pCur = mbuf_next(pCur))
+ {
+ size_t cbSeg = mbuf_len(pCur);
+ if (cbSeg)
+ {
+ void *pvSeg = mbuf_data(pCur);
+
+ /* deal with pvFrame */
+ if (!iSeg && pvFrame && pvFrame != pvSeg)
+ {
+ void *pvStart = mbuf_datastart(pMBuf);
+ uintptr_t offSeg = (uintptr_t)pvSeg - (uintptr_t)pvStart;
+ uintptr_t offSegEnd = offSeg + cbSeg;
+ Assert(pvStart && pvSeg && offSeg < mbuf_maxlen(pMBuf) && offSegEnd <= mbuf_maxlen(pMBuf)); NOREF(offSegEnd);
+ uintptr_t offFrame = (uintptr_t)pvFrame - (uintptr_t)pvStart;
+ if (RT_LIKELY(offFrame < offSeg))
+ {
+ pvSeg = pvFrame;
+ cbSeg += offSeg - offFrame;
+ }
+ else
+ AssertMsgFailed(("pvFrame=%p pvStart=%p pvSeg=%p offSeg=%p cbSeg=%#zx offSegEnd=%p offFrame=%p maxlen=%#zx\n",
+ pvFrame, pvStart, pvSeg, offSeg, cbSeg, offSegEnd, offFrame, mbuf_maxlen(pMBuf)));
+ pvFrame = NULL;
+ }
+
+ AssertBreak(iSeg < cSegs);
+ pSG->cbTotal += cbSeg;
+ pSG->aSegs[iSeg].cb = cbSeg;
+ pSG->aSegs[iSeg].pv = pvSeg;
+ pSG->aSegs[iSeg].Phys = NIL_RTHCPHYS;
+ iSeg++;
+ }
+ /* The pvFrame might be in a now empty buffer. */
+ else if ( !iSeg
+ && pvFrame
+ && (uintptr_t)pvFrame - (uintptr_t)mbuf_datastart(pMBuf) < mbuf_maxlen(pMBuf))
+ {
+ cbSeg = (uintptr_t)mbuf_datastart(pMBuf) + mbuf_maxlen(pMBuf) - (uintptr_t)pvFrame;
+ pSG->cbTotal += cbSeg;
+ pSG->aSegs[iSeg].cb = cbSeg;
+ pSG->aSegs[iSeg].pv = pvFrame;
+ pSG->aSegs[iSeg].Phys = NIL_RTHCPHYS;
+ iSeg++;
+ pvFrame = NULL;
+ }
+ }
+
+ Assert(iSeg && iSeg <= cSegs);
+ pSG->cSegsUsed = iSeg;
+
+#ifdef PADD_RUNT_FRAMES_FROM_HOST
+ /*
+ * Add a trailer if the frame is too small.
+ *
+ * Since we're getting to the packet before it is framed, it has not
+ * yet been padded. The current solution is to add a segment pointing
+ * to a buffer containing all zeros and pray that works for all frames...
+ */
+ if (pSG->cbTotal < 60 && (fSrc == INTNETTRUNKDIR_HOST))
+ {
+ AssertReturnVoid(iSeg < cSegs);
+
+ static uint8_t const s_abZero[128] = {0};
+ pSG->aSegs[iSeg].Phys = NIL_RTHCPHYS;
+ pSG->aSegs[iSeg].pv = (void *)&s_abZero[0];
+ pSG->aSegs[iSeg].cb = 60 - pSG->cbTotal;
+ pSG->cbTotal = 60;
+ pSG->cSegsUsed++;
+ }
+#endif
+
+#ifdef VBOXNETFLT_DARWIN_TEST_SEG_SIZE
+ /*
+ * Redistribute the segments.
+ */
+ if (pSG->cSegsUsed < pSG->cSegsAlloc)
+ {
+ /* copy the segments to the end. */
+ int iSrc = pSG->cSegsUsed;
+ int iDst = pSG->cSegsAlloc;
+ while (iSrc > 0)
+ {
+ iDst--;
+ iSrc--;
+ pSG->aSegs[iDst] = pSG->aSegs[iSrc];
+ }
+
+ /* create small segments from the start. */
+ pSG->cSegsUsed = pSG->cSegsAlloc;
+ iSrc = iDst;
+ iDst = 0;
+ while ( iDst < iSrc
+ && iDst < pSG->cSegsAlloc)
+ {
+ pSG->aSegs[iDst].Phys = NIL_RTHCPHYS;
+ pSG->aSegs[iDst].pv = pSG->aSegs[iSrc].pv;
+ pSG->aSegs[iDst].cb = RT_MIN(pSG->aSegs[iSrc].cb, VBOXNETFLT_DARWIN_TEST_SEG_SIZE);
+ if (pSG->aSegs[iDst].cb != pSG->aSegs[iSrc].cb)
+ {
+ pSG->aSegs[iSrc].cb -= pSG->aSegs[iDst].cb;
+ pSG->aSegs[iSrc].pv = (uint8_t *)pSG->aSegs[iSrc].pv + pSG->aSegs[iDst].cb;
+ }
+ else if (++iSrc >= pSG->cSegsAlloc)
+ {
+ pSG->cSegsUsed = iDst + 1;
+ break;
+ }
+ iDst++;
+ }
+ }
+#endif
+
+ AssertMsg(!pvFrame, ("pvFrame=%p pMBuf=%p iSeg=%d\n", pvFrame, pMBuf, iSeg));
+}
+
+
+/**
+ * Helper for determining whether the host wants the interface to be
+ * promiscuous.
+ */
+static bool vboxNetFltDarwinIsPromiscuous(PVBOXNETFLTINS pThis)
+{
+ bool fRc = false;
+ ifnet_t pIfNet = vboxNetFltDarwinRetainIfNet(pThis);
+ if (pIfNet)
+ {
+ /* gather the data */
+ uint16_t fIf = ifnet_flags(pIfNet);
+ unsigned cPromisc = VBOX_GET_PCOUNT(pIfNet);
+ bool fSetPromiscuous = ASMAtomicUoReadBool(&pThis->u.s.fSetPromiscuous);
+ vboxNetFltDarwinReleaseIfNet(pThis, pIfNet);
+
+ /* calc the return. */
+ fRc = (fIf & IFF_PROMISC)
+ && cPromisc > fSetPromiscuous;
+ }
+ return fRc;
+}
+
+
+
+/**
+ *
+ * @see iff_detached_func in the darwin kpi.
+ */
+static void vboxNetFltDarwinIffDetached(void *pvThis, ifnet_t pIfNet)
+{
+ PVBOXNETFLTINS pThis = (PVBOXNETFLTINS)pvThis;
+ uint64_t NanoTS = RTTimeSystemNanoTS();
+ LogFlow(("vboxNetFltDarwinIffDetached: pThis=%p NanoTS=%RU64 (%d)\n",
+ pThis, NanoTS, RT_VALID_PTR(pIfNet) ? VBOX_GET_PCOUNT(pIfNet) : -1));
+
+ Assert(!pThis->fDisconnectedFromHost);
+ Assert(!pThis->fRediscoveryPending);
+
+ /*
+ * If we've put it into promiscuous mode, undo that now. If we don't
+ * the if_pcount will go all wrong when it's replugged.
+ */
+ if (ASMAtomicXchgBool(&pThis->u.s.fSetPromiscuous, false))
+ ifnet_set_promiscuous(pIfNet, 0);
+
+ /*
+ * We carefully take the spinlock and increase the interface reference
+ * behind it in order to avoid problematic races with the detached callback.
+ */
+ RTSpinlockAcquire(pThis->hSpinlock);
+
+ pIfNet = ASMAtomicUoReadPtrT(&pThis->u.s.pIfNet, ifnet_t);
+ int cPromisc = RT_VALID_PTR(pIfNet) ? VBOX_GET_PCOUNT(pIfNet) : - 1;
+
+ ASMAtomicUoWriteNullPtr(&pThis->u.s.pIfNet);
+ ASMAtomicUoWriteNullPtr(&pThis->u.s.pIfFilter);
+ ASMAtomicWriteBool(&pThis->u.s.fNeedSetPromiscuous, false);
+ pThis->u.s.fSetPromiscuous = false;
+ ASMAtomicUoWriteU64(&pThis->NanoTSLastRediscovery, NanoTS);
+ ASMAtomicUoWriteBool(&pThis->fRediscoveryPending, false);
+ ASMAtomicWriteBool(&pThis->fDisconnectedFromHost, true);
+
+ RTSpinlockRelease(pThis->hSpinlock);
+
+ if (pIfNet)
+ ifnet_release(pIfNet);
+ LogRel(("VBoxNetFlt: was detached from '%s' (%d)\n", pThis->szName, cPromisc));
+}
+
+
+/**
+ *
+ * @see iff_ioctl_func in the darwin kpi.
+ */
+static errno_t vboxNetFltDarwinIffIoCtl(void *pvThis, ifnet_t pIfNet, protocol_family_t eProtocol, u_long uCmd, void *pvArg)
+{
+ RT_NOREF(pIfNet);
+ PVBOXNETFLTINS pThis = (PVBOXNETFLTINS)pvThis;
+ LogFlow(("vboxNetFltDarwinIffIoCtl: pThis=%p uCmd=%lx\n", pThis, uCmd));
+
+ /*
+ * Update fOtherPromiscuous.
+ */
+ /** @todo we'll have to find the offset of if_pcount to get this right! */
+ //if (uCmd == SIOCSIFFLAGS)
+ //{
+ //
+ //}
+
+ /*
+ * We didn't handle it, continue processing.
+ */
+ NOREF(pThis);
+ NOREF(eProtocol);
+ NOREF(uCmd);
+ NOREF(pvArg);
+ return EOPNOTSUPP;
+}
+
+
+/**
+ *
+ * @see iff_event_func in the darwin kpi.
+ */
+static void vboxNetFltDarwinIffEvent(void *pvThis, ifnet_t pIfNet, protocol_family_t eProtocol, const struct kev_msg *pEvMsg)
+{
+ PVBOXNETFLTINS pThis = (PVBOXNETFLTINS)pvThis;
+ LogFlow(("vboxNetFltDarwinIffEvent: pThis=%p\n", pThis));
+
+ NOREF(pThis);
+ NOREF(pIfNet);
+ NOREF(eProtocol);
+ NOREF(pEvMsg);
+
+ /*
+ * Watch out for the interface going online / offline.
+ */
+ if ( RT_VALID_PTR(pThis)
+ && RT_VALID_PTR(pEvMsg)
+ && pEvMsg->vendor_code == KEV_VENDOR_APPLE
+ && pEvMsg->kev_class == KEV_NETWORK_CLASS
+ && pEvMsg->kev_subclass == KEV_DL_SUBCLASS)
+ {
+ if (pThis->u.s.pIfNet == pIfNet)
+ {
+ if (pEvMsg->event_code == KEV_DL_LINK_ON)
+ {
+ if (ASMAtomicUoReadBool(&pThis->u.s.fNeedSetPromiscuous))
+ {
+ /* failed to bring it online. */
+ errno_t err = ifnet_set_promiscuous(pIfNet, 1);
+ if (!err)
+ {
+ ASMAtomicWriteBool(&pThis->u.s.fSetPromiscuous, true);
+ ASMAtomicWriteBool(&pThis->u.s.fNeedSetPromiscuous, false);
+ Log(("vboxNetFltDarwinIffEvent: enabled promiscuous mode on %s (%d)\n", pThis->szName, VBOX_GET_PCOUNT(pIfNet)));
+ }
+ else
+ Log(("vboxNetFltDarwinIffEvent: ifnet_set_promiscuous failed on %s, err=%d (%d)\n", pThis->szName, err, VBOX_GET_PCOUNT(pIfNet)));
+ }
+ else if ( ASMAtomicUoReadBool(&pThis->u.s.fSetPromiscuous)
+ && !(ifnet_flags(pIfNet) & IFF_PROMISC))
+ {
+ /* Try fix the inconsistency. */
+ errno_t err = ifnet_set_flags(pIfNet, IFF_PROMISC, IFF_PROMISC);
+ if (!err)
+ err = ifnet_ioctl(pIfNet, 0, SIOCSIFFLAGS, NULL);
+ if (!err && (ifnet_flags(pIfNet) & IFF_PROMISC))
+ Log(("vboxNetFltDarwinIffEvent: fixed IFF_PROMISC on %s (%d)\n", pThis->szName, VBOX_GET_PCOUNT(pIfNet)));
+ else
+ Log(("vboxNetFltDarwinIffEvent: failed to fix IFF_PROMISC on %s, err=%d flags=%#x (%d)\n",
+ pThis->szName, err, ifnet_flags(pIfNet), VBOX_GET_PCOUNT(pIfNet)));
+ }
+ else
+ Log(("vboxNetFltDarwinIffEvent: online, '%s'. flags=%#x (%d)\n", pThis->szName, ifnet_flags(pIfNet), VBOX_GET_PCOUNT(pIfNet)));
+ }
+ else if (pEvMsg->event_code == KEV_DL_LINK_OFF)
+ Log(("vboxNetFltDarwinIffEvent: %s goes down (%d)\n", pThis->szName, VBOX_GET_PCOUNT(pIfNet)));
+/** @todo KEV_DL_LINK_ADDRESS_CHANGED -> pfnReportMacAddress */
+/** @todo KEV_DL_SIFFLAGS -> pfnReportPromiscuousMode */
+ }
+ else
+ Log(("vboxNetFltDarwinIffEvent: pThis->u.s.pIfNet=%p pIfNet=%p (%d)\n", pThis->u.s.pIfNet, pIfNet, RT_VALID_PTR(pIfNet) ? VBOX_GET_PCOUNT(pIfNet) : -1));
+ }
+ else if (RT_VALID_PTR(pEvMsg))
+ Log(("vboxNetFltDarwinIffEvent: vendor_code=%#x kev_class=%#x kev_subclass=%#x event_code=%#x\n",
+ pEvMsg->vendor_code, pEvMsg->kev_class, pEvMsg->kev_subclass, pEvMsg->event_code));
+}
+
+
+/**
+ * Internal worker for vboxNetFltDarwinIffInput and vboxNetFltDarwinIffOutput,
+ *
+ * @returns 0 or EJUSTRETURN.
+ * @param pThis The instance.
+ * @param pMBuf The mbuf.
+ * @param pvFrame The start of the frame, optional.
+ * @param fSrc Where the packet (allegedly) comes from, one INTNETTRUNKDIR_* value.
+ * @param eProtocol The protocol.
+ */
+static errno_t vboxNetFltDarwinIffInputOutputWorker(PVBOXNETFLTINS pThis, mbuf_t pMBuf, void *pvFrame,
+ uint32_t fSrc, protocol_family_t eProtocol)
+{
+ /*
+ * Drop it immediately?
+ */
+ Log2(("vboxNetFltDarwinIffInputOutputWorker: pThis=%p pMBuf=%p pvFrame=%p fSrc=%#x cbPkt=%x\n",
+ pThis, pMBuf, pvFrame, fSrc, pMBuf ? mbuf_pkthdr_len(pMBuf) : -1));
+ if (!pMBuf)
+ return 0;
+#if 0 /* debugging lost icmp packets */
+ if (mbuf_pkthdr_len(pMBuf) > 0x300)
+ {
+ uint8_t *pb = (uint8_t *)(pvFrame ? pvFrame : mbuf_data(pMBuf));
+ Log3(("D=%.6Rhxs S=%.6Rhxs T=%04x IFF\n", pb, pb + 6, RT_BE2H_U16(*(uint16_t *)(pb + 12))));
+ }
+#endif
+ if (vboxNetFltDarwinMBufIsOur(pThis, pMBuf, pvFrame))
+ return 0;
+
+ /*
+ * Active? Retain the instance and increment the busy counter.
+ */
+ if (!vboxNetFltTryRetainBusyActive(pThis))
+ return 0;
+
+ /*
+ * Finalize out-bound packets since the stack puts off finalizing
+ * TCP/IP checksums as long as possible.
+ * ASSUMES this only applies to outbound IP packets.
+ */
+ if (fSrc == INTNETTRUNKDIR_HOST)
+ {
+ Assert(!pvFrame);
+ mbuf_outbound_finalize(pMBuf, eProtocol, sizeof(RTNETETHERHDR));
+ }
+
+ /*
+ * Create a (scatter/)gather list for the mbuf and feed it to the internal network.
+ */
+ bool fDropIt = false;
+ unsigned cSegs = vboxNetFltDarwinMBufCalcSGSegs(pThis, pMBuf, pvFrame);
+ if (cSegs < VBOXNETFLT_DARWIN_MAX_SEGS)
+ {
+ PINTNETSG pSG = (PINTNETSG)alloca(RT_UOFFSETOF_DYN(INTNETSG, aSegs[cSegs]));
+ vboxNetFltDarwinMBufToSG(pThis, pMBuf, pvFrame, pSG, cSegs, fSrc);
+
+ fDropIt = pThis->pSwitchPort->pfnRecv(pThis->pSwitchPort, NULL /* pvIf */, pSG, fSrc);
+ if (fDropIt)
+ {
+ /*
+ * If the interface is in promiscuous mode we should let
+ * all inbound packets (this one was for a bridged guest)
+ * reach the driver as it passes them to tap callbacks in
+ * order for BPF to work properly.
+ */
+ if ( fSrc == INTNETTRUNKDIR_WIRE
+ && vboxNetFltDarwinIsPromiscuous(pThis))
+ {
+ fDropIt = false;
+ }
+
+ /*
+ * A packet from the host to a guest. As we won't pass it
+ * to the drvier/wire we need to feed it to bpf ourselves.
+ *
+ * XXX: TODO: bpf should be done before; use pfnPreRecv?
+ */
+ if (fSrc == INTNETTRUNKDIR_HOST)
+ {
+ bpf_tap_out(pThis->u.s.pIfNet, DLT_EN10MB, pMBuf, NULL, 0);
+ ifnet_stat_increment_out(pThis->u.s.pIfNet, 1, mbuf_len(pMBuf), 0);
+ }
+ }
+ }
+
+ vboxNetFltRelease(pThis, true /* fBusy */);
+
+ if (fDropIt)
+ {
+ mbuf_freem(pMBuf);
+ return EJUSTRETURN;
+ }
+ return 0;
+}
+
+
+/**
+ * From the host.
+ *
+ * @see iff_output_func in the darwin kpi.
+ */
+static errno_t vboxNetFltDarwinIffOutput(void *pvThis, ifnet_t pIfNet, protocol_family_t eProtocol, mbuf_t *ppMBuf)
+{
+ /** @todo there was some note about the ethernet header here or something like that... */
+
+ NOREF(eProtocol);
+ NOREF(pIfNet);
+ return vboxNetFltDarwinIffInputOutputWorker((PVBOXNETFLTINS)pvThis, *ppMBuf, NULL, INTNETTRUNKDIR_HOST, eProtocol);
+}
+
+
+/**
+ * From the wire.
+ *
+ * @see iff_input_func in the darwin kpi.
+ */
+static errno_t vboxNetFltDarwinIffInput(void *pvThis, ifnet_t pIfNet, protocol_family_t eProtocol, mbuf_t *ppMBuf, char **ppchFrame)
+{
+ RT_NOREF(eProtocol, pIfNet);
+ return vboxNetFltDarwinIffInputOutputWorker((PVBOXNETFLTINS)pvThis, *ppMBuf, *ppchFrame, INTNETTRUNKDIR_WIRE, eProtocol);
+}
+
+
+/** A worker thread for vboxNetFltSendDummy(). */
+static DECLCALLBACK(int) vboxNetFltSendDummyWorker(RTTHREAD hThreadSelf, void *pvUser)
+{
+ RT_NOREF(hThreadSelf);
+ Assert(pvUser);
+ ifnet_t pIfNet = (ifnet_t)pvUser;
+ return VBoxNetSendDummy(pIfNet);
+}
+
+
+/**
+ * Prevent GUI icon freeze issue when VirtualBoxVM process terminates.
+ *
+ * This function is a workaround for stuck-in-dock issue. The idea here is to
+ * send a dummy packet to an interface from the context of a kernel thread.
+ * Therefore, an XNU's receive thread (which is created as a result if we are
+ * the first who is communicating with the interface) will be associated with
+ * the kernel thread instead of VirtualBoxVM process.
+ *
+ * @param pIfNet Interface to be used to send data.
+ */
+static void vboxNetFltSendDummy(ifnet_t pIfNet)
+{
+ RTTHREAD hThread;
+ int rc = RTThreadCreate(&hThread, vboxNetFltSendDummyWorker, (void *)pIfNet, 0,
+ RTTHREADTYPE_DEFAULT, RTTHREADFLAGS_WAITABLE, "DummyThread");
+ if (RT_SUCCESS(rc))
+ {
+ RTThreadWait(hThread, RT_INDEFINITE_WAIT, NULL);
+ LogFlow(("vboxNetFltSendDummy: a dummy packet has been successfully sent in order to prevent stuck-in-dock issue\n"));
+ }
+ else
+ LogFlow(("vboxNetFltSendDummy: unable to send dummy packet in order to prevent stuck-in-dock issue\n"));
+}
+
+
+/**
+ * Internal worker for vboxNetFltOsInitInstance and vboxNetFltOsMaybeRediscovered.
+ *
+ * @returns VBox status code.
+ * @param pThis The instance.
+ * @param fRediscovery If set we're doing a rediscovery attempt, so, don't
+ * flood the release log.
+ */
+static int vboxNetFltDarwinAttachToInterface(PVBOXNETFLTINS pThis, bool fRediscovery)
+{
+ LogFlow(("vboxNetFltDarwinAttachToInterface: pThis=%p (%s)\n", pThis, pThis->szName));
+ IPRT_DARWIN_SAVE_EFL_AC();
+
+ /*
+ * Locate the interface first.
+ *
+ * The pIfNet member is updated before iflt_attach is called and used
+ * to deal with the hypothetical case where someone rips out the
+ * interface immediately after our iflt_attach call.
+ */
+ ifnet_t pIfNet = NULL;
+ errno_t err = ifnet_find_by_name(pThis->szName, &pIfNet);
+ if (err)
+ {
+ Assert(err == ENXIO);
+ if (!fRediscovery)
+ LogRel(("VBoxFltDrv: failed to find ifnet '%s' (err=%d)\n", pThis->szName, err));
+ else
+ Log(("VBoxFltDrv: failed to find ifnet '%s' (err=%d)\n", pThis->szName, err));
+ IPRT_DARWIN_RESTORE_EFL_AC();
+ return VERR_INTNET_FLT_IF_NOT_FOUND;
+ }
+
+ AssertCompileMemberAlignment(VBOXNETFLTINS, u.s.pIfNet, ARCH_BITS / 8);
+ AssertMsg(!((uintptr_t)&pThis->u.s.pIfNet & (ARCH_BITS / 8 - 1)), ("pThis=%p\n", pThis));
+ RTSpinlockAcquire(pThis->hSpinlock);
+ ASMAtomicUoWritePtr(&pThis->u.s.pIfNet, pIfNet);
+ RTSpinlockRelease(pThis->hSpinlock);
+
+ /* Adjust g_offIfNetPCount as it varies for different versions of xnu. */
+ vboxNetFltDarwinDetectPCountOffset(pIfNet);
+
+ /* Prevent stuck-in-dock issue by associating interface receive thread with kernel thread. */
+ vboxNetFltSendDummy(pIfNet);
+
+ /*
+ * Get the mac address while we still have a valid ifnet reference.
+ */
+ err = ifnet_lladdr_copy_bytes(pIfNet, &pThis->u.s.MacAddr, sizeof(pThis->u.s.MacAddr));
+ if (!err)
+ {
+ /*
+ * Try attach the filter.
+ */
+ struct iff_filter RegRec;
+ RegRec.iff_cookie = pThis;
+ RegRec.iff_name = "VBoxNetFlt";
+ RegRec.iff_protocol = 0;
+ RegRec.iff_input = vboxNetFltDarwinIffInput;
+ RegRec.iff_output = vboxNetFltDarwinIffOutput;
+ RegRec.iff_event = vboxNetFltDarwinIffEvent;
+ RegRec.iff_ioctl = vboxNetFltDarwinIffIoCtl;
+ RegRec.iff_detached = vboxNetFltDarwinIffDetached;
+ interface_filter_t pIfFilter = NULL;
+ err = iflt_attach(pIfNet, &RegRec, &pIfFilter);
+ Assert(err || pIfFilter);
+
+ RTSpinlockAcquire(pThis->hSpinlock);
+ pIfNet = ASMAtomicUoReadPtrT(&pThis->u.s.pIfNet, ifnet_t);
+ if (pIfNet && !err)
+ {
+ ASMAtomicUoWriteBool(&pThis->fDisconnectedFromHost, false);
+ ASMAtomicUoWritePtr(&pThis->u.s.pIfFilter, pIfFilter);
+ pIfNet = NULL; /* don't dereference it */
+ }
+ RTSpinlockRelease(pThis->hSpinlock);
+
+ /* Report capabilities. */
+ if ( !pIfNet
+ && vboxNetFltTryRetainBusyNotDisconnected(pThis))
+ {
+ Assert(pThis->pSwitchPort);
+ pThis->pSwitchPort->pfnReportMacAddress(pThis->pSwitchPort, &pThis->u.s.MacAddr);
+#if 0
+ /*
+ * XXX: Don't tell SrvIntNetR0 if the interface is
+ * promiscuous, because there's no code yet to update that
+ * information and we don't want it stuck, spamming all
+ * traffic to the host.
+ */
+ pThis->pSwitchPort->pfnReportPromiscuousMode(pThis->pSwitchPort, vboxNetFltDarwinIsPromiscuous(pThis));
+#endif
+ pThis->pSwitchPort->pfnReportGsoCapabilities(pThis->pSwitchPort, 0, INTNETTRUNKDIR_WIRE | INTNETTRUNKDIR_HOST);
+ pThis->pSwitchPort->pfnReportNoPreemptDsts(pThis->pSwitchPort, 0 /* none */);
+ vboxNetFltRelease(pThis, true /*fBusy*/);
+ }
+ }
+
+ /* Release the interface on failure. */
+ if (pIfNet)
+ ifnet_release(pIfNet);
+
+ int rc = RTErrConvertFromErrno(err);
+ if (RT_SUCCESS(rc))
+ LogRel(("VBoxFltDrv: attached to '%s' / %RTmac\n", pThis->szName, &pThis->u.s.MacAddr));
+ else
+ LogRel(("VBoxFltDrv: failed to attach to ifnet '%s' (err=%d)\n", pThis->szName, err));
+ IPRT_DARWIN_RESTORE_EFL_AC();
+ return rc;
+}
+
+
+bool vboxNetFltOsMaybeRediscovered(PVBOXNETFLTINS pThis)
+{
+ vboxNetFltDarwinAttachToInterface(pThis, true /* fRediscovery */);
+ return !ASMAtomicUoReadBool(&pThis->fDisconnectedFromHost);
+}
+
+
+int vboxNetFltPortOsXmit(PVBOXNETFLTINS pThis, void *pvIfData, PINTNETSG pSG, uint32_t fDst)
+{
+ IPRT_DARWIN_SAVE_EFL_AC();
+ NOREF(pvIfData);
+
+ int rc = VINF_SUCCESS;
+ ifnet_t pIfNet = vboxNetFltDarwinRetainIfNet(pThis);
+ if (pIfNet)
+ {
+ /*
+ * Create a mbuf for the gather list and push it onto the wire.
+ * BPF tap and stats will be taken care of by the driver.
+ */
+ if (fDst & INTNETTRUNKDIR_WIRE)
+ {
+ mbuf_t pMBuf = vboxNetFltDarwinMBufFromSG(pThis, pSG);
+ if (pMBuf)
+ {
+ errno_t err = ifnet_output_raw(pIfNet, PF_LINK, pMBuf);
+ if (err)
+ rc = RTErrConvertFromErrno(err);
+ }
+ else
+ rc = VERR_NO_MEMORY;
+ }
+
+ /*
+ * Create a mbuf for the gather list and push it onto the host stack.
+ * BPF tap and stats are on us.
+ */
+ if (fDst & INTNETTRUNKDIR_HOST)
+ {
+ mbuf_t pMBuf = vboxNetFltDarwinMBufFromSG(pThis, pSG);
+ if (pMBuf)
+ {
+ void *pvEthHdr = mbuf_data(pMBuf);
+ unsigned const cbEthHdr = 14;
+ struct ifnet_stat_increment_param stats;
+
+ RT_ZERO(stats);
+ stats.packets_in = 1;
+ stats.bytes_in = mbuf_len(pMBuf); /* full ethernet frame */
+
+ mbuf_pkthdr_setrcvif(pMBuf, pIfNet);
+ mbuf_pkthdr_setheader(pMBuf, pvEthHdr); /* link-layer header */
+ mbuf_adj(pMBuf, cbEthHdr); /* move to payload */
+
+#if 0 /* XXX: disabled since we don't request promiscuous from intnet */
+ /*
+ * TODO: Since intnet knows whether it forwarded us
+ * this packet because it's for us or because we are
+ * promiscuous, it can perhaps set a flag for us in
+ * INTNETSG::fFlags so that we don't have to re-check
+ * it here.
+ */
+ PCRTNETETHERHDR pcEthHdr = (PCRTNETETHERHDR)pvEthHdr;
+ if ( (pcEthHdr->DstMac.au8[0] & 1) == 0 /* unicast? */
+ && memcmp(&pcEthHdr->DstMac, &pThis->u.s.MacAddr, sizeof(RTMAC)) != 0)
+ {
+ mbuf_setflags_mask(pMBuf, MBUF_PROMISC, MBUF_PROMISC);
+ }
+#endif
+
+ bpf_tap_in(pIfNet, DLT_EN10MB, pMBuf, pvEthHdr, cbEthHdr);
+ errno_t err = ifnet_input(pIfNet, pMBuf, &stats);
+ if (err)
+ rc = RTErrConvertFromErrno(err);
+ }
+ else
+ rc = VERR_NO_MEMORY;
+ }
+
+ vboxNetFltDarwinReleaseIfNet(pThis, pIfNet);
+ }
+
+ IPRT_DARWIN_RESTORE_EFL_AC();
+ return rc;
+}
+
+
+void vboxNetFltPortOsSetActive(PVBOXNETFLTINS pThis, bool fActive)
+{
+ IPRT_DARWIN_SAVE_EFL_AC();
+ ifnet_t pIfNet = vboxNetFltDarwinRetainIfNet(pThis);
+ if (pIfNet)
+ {
+ if (pThis->fDisablePromiscuous)
+ {
+ /*
+ * Promiscuous mode should not be used (wireless), we just need to
+ * make sure the interface is up.
+ */
+ if (fActive)
+ {
+ u_int16_t fIf = ifnet_flags(pIfNet);
+ if ((fIf & (IFF_UP | IFF_RUNNING)) != (IFF_UP | IFF_RUNNING))
+ {
+ ifnet_set_flags(pIfNet, IFF_UP, IFF_UP);
+ ifnet_ioctl(pIfNet, 0, SIOCSIFFLAGS, NULL);
+ }
+ }
+ }
+ else
+ {
+ /*
+ * This api is a bit weird, the best reference is the code.
+ *
+ * Also, we have a bit or race conditions wrt the maintenance of
+ * host the interface promiscuity for vboxNetFltPortOsIsPromiscuous.
+ */
+ unsigned const cPromiscBefore = VBOX_GET_PCOUNT(pIfNet);
+ u_int16_t fIf;
+ if (fActive)
+ {
+ Assert(!pThis->u.s.fSetPromiscuous);
+ errno_t err = ENETDOWN;
+ ASMAtomicWriteBool(&pThis->u.s.fNeedSetPromiscuous, true);
+
+ /*
+ * Try bring the interface up and running if it's down.
+ */
+ fIf = ifnet_flags(pIfNet);
+ if ((fIf & (IFF_UP | IFF_RUNNING)) != (IFF_UP | IFF_RUNNING))
+ {
+ err = ifnet_set_flags(pIfNet, IFF_UP, IFF_UP);
+ errno_t err2 = ifnet_ioctl(pIfNet, 0, SIOCSIFFLAGS, NULL);
+ if (!err)
+ err = err2;
+ fIf = ifnet_flags(pIfNet);
+ }
+
+ /*
+ * Is it already up? If it isn't, leave it to the link event or
+ * we'll upset if_pcount (as stated above, ifnet_set_promiscuous is weird).
+ */
+ if ((fIf & (IFF_UP | IFF_RUNNING)) == (IFF_UP | IFF_RUNNING))
+ {
+ err = ifnet_set_promiscuous(pIfNet, 1);
+ pThis->u.s.fSetPromiscuous = err == 0;
+ if (!err)
+ {
+ ASMAtomicWriteBool(&pThis->u.s.fNeedSetPromiscuous, false);
+
+ /* check if it actually worked, this stuff is not always behaving well. */
+ if (!(ifnet_flags(pIfNet) & IFF_PROMISC))
+ {
+ err = ifnet_set_flags(pIfNet, IFF_PROMISC, IFF_PROMISC);
+ if (!err)
+ err = ifnet_ioctl(pIfNet, 0, SIOCSIFFLAGS, NULL);
+ if (!err)
+ Log(("vboxNetFlt: fixed IFF_PROMISC on %s (%d->%d)\n", pThis->szName, cPromiscBefore, VBOX_GET_PCOUNT(pIfNet)));
+ else
+ Log(("VBoxNetFlt: failed to fix IFF_PROMISC on %s, err=%d (%d->%d)\n",
+ pThis->szName, err, cPromiscBefore, VBOX_GET_PCOUNT(pIfNet)));
+ }
+ }
+ else
+ Log(("VBoxNetFlt: ifnet_set_promiscuous -> err=%d grr! (%d->%d)\n", err, cPromiscBefore, VBOX_GET_PCOUNT(pIfNet)));
+ }
+ else if (!err)
+ Log(("VBoxNetFlt: Waiting for the link to come up... (%d->%d)\n", cPromiscBefore, VBOX_GET_PCOUNT(pIfNet)));
+ if (err)
+ LogRel(("VBoxNetFlt: Failed to put '%s' into promiscuous mode, err=%d (%d->%d)\n", pThis->szName, err, cPromiscBefore, VBOX_GET_PCOUNT(pIfNet)));
+ }
+ else
+ {
+ ASMAtomicWriteBool(&pThis->u.s.fNeedSetPromiscuous, false);
+ if (pThis->u.s.fSetPromiscuous)
+ {
+ errno_t err = ifnet_set_promiscuous(pIfNet, 0);
+ AssertMsg(!err, ("%d\n", err)); NOREF(err);
+ }
+ pThis->u.s.fSetPromiscuous = false;
+
+ fIf = ifnet_flags(pIfNet);
+ Log(("VBoxNetFlt: fIf=%#x; %d->%d\n", fIf, cPromiscBefore, VBOX_GET_PCOUNT(pIfNet)));
+ }
+ }
+
+ vboxNetFltDarwinReleaseIfNet(pThis, pIfNet);
+ }
+ IPRT_DARWIN_RESTORE_EFL_AC();
+}
+
+
+int vboxNetFltOsDisconnectIt(PVBOXNETFLTINS pThis)
+{
+ /* Nothing to do here. */
+ RT_NOREF(pThis);
+ return VINF_SUCCESS;
+}
+
+
+int vboxNetFltOsConnectIt(PVBOXNETFLTINS pThis)
+{
+ /* Nothing to do here. */
+ RT_NOREF(pThis);
+ return VINF_SUCCESS;
+}
+
+
+void vboxNetFltOsDeleteInstance(PVBOXNETFLTINS pThis)
+{
+ IPRT_DARWIN_SAVE_EFL_AC();
+
+ /*
+ * Carefully obtain the interface filter reference and detach it.
+ */
+ RTSpinlockAcquire(pThis->hSpinlock);
+ interface_filter_t pIfFilter = ASMAtomicUoReadPtrT(&pThis->u.s.pIfFilter, interface_filter_t);
+ if (pIfFilter)
+ ASMAtomicUoWriteNullPtr(&pThis->u.s.pIfFilter);
+ RTSpinlockRelease(pThis->hSpinlock);
+
+ if (pIfFilter)
+ iflt_detach(pIfFilter);
+
+ if (pThis->u.s.pSysSock != NULL)
+ {
+ RT_GCC_NO_WARN_DEPRECATED_BEGIN
+
+ sock_close(pThis->u.s.pSysSock);
+ pThis->u.s.pSysSock = NULL;
+
+ RT_GCC_NO_WARN_DEPRECATED_END
+ }
+
+ IPRT_DARWIN_RESTORE_EFL_AC();
+}
+
+
+int vboxNetFltOsInitInstance(PVBOXNETFLTINS pThis, void *pvContext)
+{
+ NOREF(pvContext);
+
+ int rc = vboxNetFltDarwinAttachToInterface(pThis, false /* fRediscovery */);
+ if (RT_FAILURE(rc))
+ return rc;
+
+ if (pThis->pSwitchPort->pfnNotifyHostAddress == NULL)
+ return rc;
+
+ /*
+ * XXX: uwe
+ *
+ * Learn host's IP addresses and set up notifications for changes.
+ * To avoid racing, set up notifications first.
+ *
+ * XXX: This should probably be global, since the only thing
+ * specific to ifnet here is its IPv6 link-local address.
+ */
+ IPRT_DARWIN_SAVE_EFL_AC();
+ errno_t error;
+
+ /** @todo Figure out how to replace the socket stuff we use to detect
+ * addresses here as 10.5 deprecates it. */
+ RT_GCC_NO_WARN_DEPRECATED_BEGIN
+
+ /** @todo reorg code to not have numerous returns with duplicate code... */
+ /** @todo reorg code to not have numerous returns with duplicate code... */
+ /** @todo reorg code to not have numerous returns with duplicate code... */
+ /** @todo reorg code to not have numerous returns with duplicate code... */
+ /** @todo reorg code to not have numerous returns with duplicate code... */
+ /** @todo reorg code to not have numerous returns with duplicate code... */
+ /** @todo reorg code to not have numerous returns with duplicate code... */
+ /** @todo reorg code to not have numerous returns with duplicate code... */
+ /** @todo reorg code to not have numerous returns with duplicate code... */
+
+ error = sock_socket(PF_SYSTEM, SOCK_RAW, SYSPROTO_EVENT,
+ vboxNetFltDarwinSysSockUpcall, pThis,
+ &pThis->u.s.pSysSock);
+ if (error != 0)
+ {
+ LogRel(("sock_socket(SYSPROTO_EVENT): error %d\n", error));
+ IPRT_DARWIN_RESTORE_EFL_AC();
+ return rc;
+ }
+
+ int nbio = 1;
+ error = sock_ioctl(pThis->u.s.pSysSock, FIONBIO, &nbio);
+ if (error != 0)
+ {
+ LogRel(("FIONBIO: error %d\n", error));
+ sock_close(pThis->u.s.pSysSock);
+ IPRT_DARWIN_RESTORE_EFL_AC();
+ return rc;
+ }
+
+ if (!sock_isnonblocking(pThis->u.s.pSysSock))
+ {
+ LogRel(("FIONBIO ok, but socket is blocking?!\n"));
+ sock_close(pThis->u.s.pSysSock);
+ IPRT_DARWIN_RESTORE_EFL_AC();
+ return rc;
+ }
+
+ struct kev_request req;
+ req.vendor_code = KEV_VENDOR_APPLE;
+ req.kev_class = KEV_NETWORK_CLASS;
+ req.kev_subclass = KEV_ANY_SUBCLASS; /* need both INET and INET6, so have to request all */
+
+ error = sock_ioctl(pThis->u.s.pSysSock, SIOCSKEVFILT, &req);
+ if (error != 0)
+ {
+ LogRel(("SIOCSKEVFILT: error %d\n", error));
+ sock_close(pThis->u.s.pSysSock);
+ IPRT_DARWIN_RESTORE_EFL_AC();
+ return rc;
+ }
+ RT_GCC_NO_WARN_DEPRECATED_END
+
+ ifnet_t pIfNet = pThis->u.s.pIfNet; /* already retained */
+
+ ifaddr_t *pIfAddrList;
+ error = ifnet_get_address_list(/* all interfaces*/ NULL, &pIfAddrList);
+ if (error != 0)
+ {
+ LogRel(("ifnet_get_address_list: error %d\n", error));
+ IPRT_DARWIN_RESTORE_EFL_AC();
+ return rc;
+ }
+
+ for (ifaddr_t *pIfAddr = pIfAddrList; *pIfAddr != NULL; ++pIfAddr)
+ {
+ ifaddr_t ifa = *pIfAddr;
+ sa_family_t family = ifaddr_address_family(ifa);
+ struct sockaddr_storage ss;
+
+ error = ifaddr_address(ifa, (struct sockaddr *)&ss, sizeof(ss));
+ if (error != 0)
+ {
+ LogRel(("getting address family %d: error %d\n", family, error));
+ continue;
+ }
+
+ if (family == AF_INET)
+ {
+ struct sockaddr_in *sin = (struct sockaddr_in *)&ss;
+ u_int32_t u32Addr = ntohl(sin->sin_addr.s_addr);
+
+ if (VBOX_IN_LOOPBACK(u32Addr))
+ continue;
+
+ if (ifaddr_ifnet(ifa) != pIfNet && VBOX_IN_LINKLOCAL(u32Addr))
+ continue;
+
+ Log(("> inet %RTnaipv4\n", sin->sin_addr.s_addr));
+ pThis->pSwitchPort->pfnNotifyHostAddress(pThis->pSwitchPort,
+ /* :fAdded */ true, kIntNetAddrType_IPv4, &sin->sin_addr);
+ }
+ else if (family == AF_INET6)
+ {
+ struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)&ss;
+
+ if (IN6_IS_ADDR_LOOPBACK(&sin6->sin6_addr))
+ continue;
+
+ /* link-local from other interfaces are out of scope */
+ if (ifaddr_ifnet(ifa) != pIfNet && IN6_IS_ADDR_LINKLOCAL(&sin6->sin6_addr))
+ continue;
+
+ Log(("> inet6 %RTnaipv6\n", &sin6->sin6_addr));
+ pThis->pSwitchPort->pfnNotifyHostAddress(pThis->pSwitchPort,
+ /* :fAdded */ true, kIntNetAddrType_IPv6, &sin6->sin6_addr);
+ }
+ }
+
+ ifnet_free_address_list(pIfAddrList);
+
+ /*
+ * Now that we've got current addresses, check for events that
+ * might have happened while we were working.
+ */
+ vboxNetFltDarwinSysSockUpcall(pThis->u.s.pSysSock, pThis, MBUF_DONTWAIT);
+
+ IPRT_DARWIN_RESTORE_EFL_AC();
+ return rc;
+}
+
+
+static void vboxNetFltDarwinSysSockUpcall(socket_t pSysSock, void *pvData, int fWait)
+{
+ PVBOXNETFLTINS pThis = (PVBOXNETFLTINS)pvData;
+ errno_t error;
+
+ NOREF(fWait);
+
+ if (RT_UNLIKELY(pSysSock != pThis->u.s.pSysSock))
+ {
+ Log(("vboxNetFltDarwinSysSockUpcall: %p != %p?\n", pSysSock, pThis->u.s.pSysSock));
+ return;
+ }
+
+ ifnet_t pIfNet = pThis->u.s.pIfNet; /* XXX: retain? */
+ ifnet_family_t if_family = ifnet_family(pIfNet);
+ u_int32_t if_unit = ifnet_unit(pIfNet);
+
+ for (;;)
+ {
+ mbuf_t m;
+ size_t len = sizeof(struct kern_event_msg) - sizeof(u_int32_t) + sizeof(struct kev_in6_data);
+
+ RT_GCC_NO_WARN_DEPRECATED_BEGIN
+ error = sock_receivembuf(pSysSock, NULL, &m, 0, &len);
+ RT_GCC_NO_WARN_DEPRECATED_END
+ if (error != 0)
+ {
+ if (error == EWOULDBLOCK)
+ {
+ Log(("vboxNetFltDarwinSysSockUpcall: EWOULDBLOCK - we are done\n"));
+ error = 0;
+ }
+ else
+ Log(("sock_receivembuf: error %d\n", error));
+ break;
+ }
+
+ if (len < sizeof(struct kern_event_msg) - sizeof(u_int32_t))
+ {
+ Log(("vboxNetFltDarwinSysSockUpcall: %u bytes is too short\n", (unsigned int)len));
+ mbuf_freem(m);
+ return;
+ }
+
+ struct kern_event_msg *msg = (struct kern_event_msg *)mbuf_data(m);
+ if (msg->kev_subclass == KEV_INET_SUBCLASS)
+ {
+ if (len - (sizeof(struct kern_event_msg) - sizeof(u_int32_t)) < sizeof(struct kev_in_data))
+ {
+ Log(("vboxNetFltDarwinSysSockUpcall: %u bytes is too short for KEV_INET_SUBCLASS\n", (unsigned int)len));
+ mbuf_freem(m);
+ return;
+ }
+
+ struct kev_in_data *iev = (struct kev_in_data *)msg->event_data;
+ struct net_event_data *link = &iev->link_data;
+ PCRTNETADDRU pAddr = (PCRTNETADDRU)&iev->ia_addr;
+ u_int32_t u32Addr = ntohl(pAddr->IPv4.u);
+
+ if (VBOX_IN_LOOPBACK(u32Addr))
+ {
+ mbuf_freem(m);
+ continue;
+ }
+
+ if ( (link->if_family != if_family || link->if_unit != if_unit)
+ && VBOX_IN_LINKLOCAL(u32Addr))
+ {
+ mbuf_freem(m);
+ continue;
+ }
+
+ switch (msg->event_code)
+ {
+ case KEV_INET_NEW_ADDR:
+ Log(("KEV_INET_NEW_ADDR %.*s%d: %RTnaipv4\n", IFNAMSIZ, link->if_name, link->if_unit, pAddr->IPv4.u));
+ pThis->pSwitchPort->pfnNotifyHostAddress(pThis->pSwitchPort, true /*fAdded*/, kIntNetAddrType_IPv4, pAddr);
+ break;
+
+ case KEV_INET_ADDR_DELETED:
+ Log(("KEV_INET_ADDR_DELETED %.*s%d: %RTnaipv4\n", IFNAMSIZ, link->if_name, link->if_unit, pAddr->IPv4.u));
+ pThis->pSwitchPort->pfnNotifyHostAddress(pThis->pSwitchPort, false /*fAdded*/, kIntNetAddrType_IPv4, pAddr);
+ break;
+
+ default:
+ Log(("KEV INET event %u %.*s%d: addr %RTnaipv4\n",
+ msg->event_code, IFNAMSIZ, link->if_name, link->if_unit, pAddr->IPv4.u));
+ break;
+ }
+ }
+ else if (msg->kev_subclass == KEV_INET6_SUBCLASS)
+ {
+ if (len - (sizeof(struct kern_event_msg) - sizeof(u_int32_t)) < sizeof(struct kev_in6_data))
+ {
+ Log(("vboxNetFltDarwinSysSockUpcall: %u bytes is too short for KEV_INET6_SUBCLASS\n",
+ (unsigned int)len));
+ mbuf_freem(m);
+ return;
+ }
+
+ struct kev_in6_data *iev6 = (struct kev_in6_data *)msg->event_data;
+ struct net_event_data *link = &iev6->link_data;
+ PCRTNETADDRU pAddr = (PCRTNETADDRU)&iev6->ia_addr.sin6_addr;
+
+ if (IN6_IS_ADDR_LOOPBACK(&iev6->ia_addr.sin6_addr))
+ {
+ mbuf_freem(m);
+ continue;
+ }
+
+ if ( (link->if_family != if_family || link->if_unit != if_unit)
+ && IN6_IS_ADDR_LINKLOCAL(&iev6->ia_addr.sin6_addr))
+ {
+ mbuf_freem(m);
+ continue;
+ }
+
+ switch (msg->event_code)
+ {
+ case KEV_INET6_NEW_USER_ADDR:
+ Log(("KEV_INET6_NEW_USER_ADDR %.*s%d: %RTnaipv6\n",
+ IFNAMSIZ, link->if_name, link->if_unit, pAddr));
+ goto kev_inet6_new;
+
+ case KEV_INET6_NEW_LL_ADDR:
+ Log(("KEV_INET6_NEW_LL_ADDR %.*s%d: %RTnaipv6\n",
+ IFNAMSIZ, link->if_name, link->if_unit, pAddr));
+ goto kev_inet6_new;
+
+ case KEV_INET6_NEW_RTADV_ADDR:
+ Log(("KEV_INET6_NEW_RTADV_ADDR %.*s%d: %RTnaipv6\n",
+ IFNAMSIZ, link->if_name, link->if_unit, pAddr));
+ goto kev_inet6_new;
+
+ kev_inet6_new:
+ pThis->pSwitchPort->pfnNotifyHostAddress(pThis->pSwitchPort, true /*fAdded*/, kIntNetAddrType_IPv6, pAddr);
+ break;
+
+ case KEV_INET6_ADDR_DELETED:
+ Log(("KEV_INET6_ADDR_DELETED %.*s%d: %RTnaipv6\n",
+ IFNAMSIZ, link->if_name, link->if_unit, pAddr));
+
+ pThis->pSwitchPort->pfnNotifyHostAddress(pThis->pSwitchPort, false /*fAdded*/, kIntNetAddrType_IPv6, pAddr);
+ break;
+
+ default:
+ Log(("KEV INET6 event %u %.*s%d: addr %RTnaipv6\n",
+ msg->event_code, IFNAMSIZ, link->if_name, link->if_unit, pAddr));
+ break;
+ }
+ }
+ else
+ Log(("vboxNetFltDarwinSysSockUpcall: subclass %u ignored\n", (unsigned)msg->kev_subclass));
+
+ mbuf_freem(m);
+ }
+}
+
+
+int vboxNetFltOsPreInitInstance(PVBOXNETFLTINS pThis)
+{
+ /*
+ * Init the darwin specific members.
+ */
+ pThis->u.s.pIfNet = NULL;
+ pThis->u.s.pIfFilter = NULL;
+ pThis->u.s.fSetPromiscuous = false;
+ pThis->u.s.fNeedSetPromiscuous = false;
+ //pThis->u.s.MacAddr = {0};
+ pThis->u.s.pSysSock = NULL;
+
+ return VINF_SUCCESS;
+}
+
+
+void vboxNetFltPortOsNotifyMacAddress(PVBOXNETFLTINS pThis, void *pvIfData, PCRTMAC pMac)
+{
+ NOREF(pThis); NOREF(pvIfData); NOREF(pMac);
+}
+
+
+int vboxNetFltPortOsConnectInterface(PVBOXNETFLTINS pThis, void *pvIf, void **ppvIfData)
+{
+ /* Nothing to do */
+ NOREF(pThis); NOREF(pvIf); NOREF(ppvIfData);
+ return VINF_SUCCESS;
+}
+
+
+int vboxNetFltPortOsDisconnectInterface(PVBOXNETFLTINS pThis, void *pvIfData)
+{
+ /* Nothing to do */
+ NOREF(pThis); NOREF(pvIfData);
+ return VINF_SUCCESS;
+}
+
diff --git a/src/VBox/HostDrivers/VBoxNetFlt/darwin/loadnetflt.sh b/src/VBox/HostDrivers/VBoxNetFlt/darwin/loadnetflt.sh
new file mode 100755
index 00000000..0fede55c
--- /dev/null
+++ b/src/VBox/HostDrivers/VBoxNetFlt/darwin/loadnetflt.sh
@@ -0,0 +1,133 @@
+#!/bin/bash
+## @file
+# For development.
+#
+
+#
+# Copyright (C) 2006-2023 Oracle and/or its affiliates.
+#
+# This file is part of VirtualBox base platform packages, as
+# available from https://www.virtualbox.org.
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation, in version 3 of the
+# License.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, see <https://www.gnu.org/licenses>.
+#
+# The contents of this file may alternatively be used under the terms
+# of the Common Development and Distribution License Version 1.0
+# (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+# in the VirtualBox distribution, in which case the provisions of the
+# CDDL are applicable instead of those of the GPL.
+#
+# You may elect to license modified versions of this file under the
+# terms and conditions of either the GPL or the CDDL or both.
+#
+# SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+#
+
+SCRIPT_NAME="loadnetflt"
+XNU_VERSION=`LC_ALL=C uname -r | LC_ALL=C cut -d . -f 1`
+
+DRVNAME="VBoxNetFlt.kext"
+BUNDLE="org.virtualbox.kext.VBoxNetFlt"
+
+DEP_DRVNAME="VBoxDrv.kext"
+DEP_BUNDLE="org.virtualbox.kext.VBoxDrv"
+
+
+DIR=`dirname "$0"`
+DIR=`cd "$DIR" && pwd`
+DEP_DIR="$DIR/$DEP_DRVNAME"
+DIR="$DIR/$DRVNAME"
+if [ ! -d "$DIR" ]; then
+ echo "Cannot find $DIR or it's not a directory..."
+ exit 1;
+fi
+if [ ! -d "$DEP_DIR" ]; then
+ echo "Cannot find $DEP_DIR or it's not a directory... (dependency)"
+ exit 1;
+fi
+if [ -n "$*" ]; then
+ OPTS="$*"
+else
+ OPTS="-t"
+fi
+
+trap "sudo chown -R `whoami` $DIR $DEP_DIR; exit 1" INT
+
+# Try unload any existing instance first.
+LOADED=`kextstat -b $BUNDLE -l`
+if test -n "$LOADED"; then
+ echo "${SCRIPT_NAME}.sh: Unloading $BUNDLE..."
+ sudo kextunload -v 6 -b $BUNDLE
+ LOADED=`kextstat -b $BUNDLE -l`
+ if test -n "$LOADED"; then
+ echo "${SCRIPT_NAME}.sh: failed to unload $BUNDLE, see above..."
+ exit 1;
+ fi
+ echo "${SCRIPT_NAME}.sh: Successfully unloaded $BUNDLE"
+fi
+
+set -e
+
+# Copy the .kext to the symbols directory and tweak the kextload options.
+if test -n "$VBOX_DARWIN_SYMS"; then
+ echo "${SCRIPT_NAME}.sh: copying the extension the symbol area..."
+ rm -Rf "$VBOX_DARWIN_SYMS/$DRVNAME"
+ mkdir -p "$VBOX_DARWIN_SYMS"
+ cp -R "$DIR" "$VBOX_DARWIN_SYMS/"
+ OPTS="$OPTS -s $VBOX_DARWIN_SYMS/ "
+ sync
+fi
+
+# On smbfs, this might succeed just fine but make no actual changes,
+# so we might have to temporarily copy the driver to a local directory.
+if sudo chown -R root:wheel "$DIR" "$DEP_DIR"; then
+ OWNER=`/usr/bin/stat -f "%u" "$DIR"`
+else
+ OWNER=1000
+fi
+if test "$OWNER" -ne 0; then
+ TMP_DIR=/tmp/${SCRIPT_NAME}.tmp
+ echo "${SCRIPT_NAME}.sh: chown didn't work on $DIR, using temp location $TMP_DIR/$DRVNAME"
+
+ # clean up first (no sudo rm)
+ if test -e "$TMP_DIR"; then
+ sudo chown -R `whoami` "$TMP_DIR"
+ rm -Rf "$TMP_DIR"
+ fi
+
+ # make a copy and switch over DIR
+ mkdir -p "$TMP_DIR/"
+ sudo cp -Rp "$DIR" "$TMP_DIR/"
+ DIR="$TMP_DIR/$DRVNAME"
+
+ # load.sh puts it here.
+ DEP_DIR="/tmp/loaddrv.tmp/$DEP_DRVNAME"
+
+ # retry
+ sudo chown -R root:wheel "$DIR" "$DEP_DIR"
+fi
+
+sudo chmod -R o-rwx "$DIR"
+sync
+if [ "$XNU_VERSION" -ge "10" ]; then
+ echo "${SCRIPT_NAME}.sh: loading $DIR... (kextutil $OPTS -d \"$DEP_DIR\" \"$DIR\")"
+ sudo kextutil $OPTS -d "$DEP_DIR" "$DIR"
+else
+ echo "${SCRIPT_NAME}.sh: loading $DIR... (kextload $OPTS -d \"$DEP_DIR\" \"$DIR\")"
+sudo kextload $OPTS -d "$DEP_DIR" "$DIR"
+fi
+sync
+sudo chown -R `whoami` "$DIR" "$DEP_DIR"
+kextstat | grep org.virtualbox.kext
+
diff --git a/src/VBox/HostDrivers/VBoxNetFlt/freebsd/Makefile b/src/VBox/HostDrivers/VBoxNetFlt/freebsd/Makefile
new file mode 100644
index 00000000..596f80a0
--- /dev/null
+++ b/src/VBox/HostDrivers/VBoxNetFlt/freebsd/Makefile
@@ -0,0 +1,57 @@
+# $Id: Makefile $
+## @file
+# Makefile for the VirtualBox FreeBSD Host Driver.
+#
+
+#
+# Copyright (C) 2006-2023 Oracle and/or its affiliates.
+#
+# This file is part of VirtualBox base platform packages, as
+# available from https://www.virtualbox.org.
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation, in version 3 of the
+# License.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, see <https://www.gnu.org/licenses>.
+#
+# The contents of this file may alternatively be used under the terms
+# of the Common Development and Distribution License Version 1.0
+# (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+# in the VirtualBox distribution, in which case the provisions of the
+# CDDL are applicable instead of those of the GPL.
+#
+# You may elect to license modified versions of this file under the
+# terms and conditions of either the GPL or the CDDL or both.
+#
+# SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+#
+
+KMOD = vboxnetflt
+
+CFLAGS += -DRT_OS_FREEBSD -DIN_RING0 -DIN_RT_R0 -DIN_SUP_R0 -DVBOX -DRT_WITH_VBOX -Iinclude -I. -Ir0drv -w -DVBOX_WITH_HARDENING -DVIMAGE
+
+.if (${MACHINE_ARCH} == "i386")
+ CFLAGS += -DRT_ARCH_X86
+.elif (${MACHINE_ARCH} == "amd64")
+ CFLAGS += -DRT_ARCH_AMD64
+.endif
+
+SRCS = \
+ VBoxNetFlt-freebsd.c \
+ VBoxNetFlt.c \
+ SUPR0IdcClient-freebsd.c \
+ SUPR0IdcClient.c \
+ SUPR0IdcClientComponent.c
+
+SRCS += device_if.h bus_if.h opt_netgraph.h
+
+.include <bsd.kmod.mk>
+
diff --git a/src/VBox/HostDrivers/VBoxNetFlt/freebsd/VBoxNetFlt-freebsd.c b/src/VBox/HostDrivers/VBoxNetFlt/freebsd/VBoxNetFlt-freebsd.c
new file mode 100644
index 00000000..b8b4212e
--- /dev/null
+++ b/src/VBox/HostDrivers/VBoxNetFlt/freebsd/VBoxNetFlt-freebsd.c
@@ -0,0 +1,817 @@
+/* $Id: VBoxNetFlt-freebsd.c $ */
+/** @file
+ * VBoxNetFlt - Network Filter Driver (Host), FreeBSD Specific Code.
+ */
+
+/*
+ * Copyright (c) 2009 Fredrik Lindberg <fli@shapeshifter.se>
+ *
+ * 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>
+#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/socket.h>
+#include <sys/sockio.h>
+#include <sys/syscallsubr.h>
+#include <sys/queue.h>
+#include <sys/taskqueue.h>
+
+#include <net/if.h>
+#include <net/if_var.h>
+#include <net/if_dl.h>
+#include <net/if_types.h>
+#include <net/ethernet.h>
+
+#include <netgraph/ng_message.h>
+#include <netgraph/netgraph.h>
+#include <netgraph/ng_parse.h>
+
+#define LOG_GROUP LOG_GROUP_NET_FLT_DRV
+#include <VBox/version.h>
+#include <VBox/err.h>
+#include <VBox/log.h>
+#include <VBox/intnetinline.h>
+#include <iprt/initterm.h>
+#include <iprt/string.h>
+#include <iprt/spinlock.h>
+#include <iprt/process.h>
+#include <iprt/assert.h>
+#include <iprt/uuid.h>
+#include <iprt/alloc.h>
+#include <iprt/err.h>
+
+#define VBOXNETFLT_OS_SPECFIC 1
+#include "../VBoxNetFltInternal.h"
+
+static int vboxnetflt_modevent(struct module *, int, void *);
+static ng_constructor_t ng_vboxnetflt_constructor;
+static ng_rcvmsg_t ng_vboxnetflt_rcvmsg;
+static ng_shutdown_t ng_vboxnetflt_shutdown;
+static ng_newhook_t ng_vboxnetflt_newhook;
+static ng_rcvdata_t ng_vboxnetflt_rcvdata;
+static ng_disconnect_t ng_vboxnetflt_disconnect;
+static int ng_vboxnetflt_mod_event(module_t mod, int event, void *data);
+
+/** Netgraph node type */
+#define NG_VBOXNETFLT_NODE_TYPE "vboxnetflt"
+/** Netgraph message cookie */
+#define NGM_VBOXNETFLT_COOKIE 0x56424f58
+
+/** Input netgraph hook name */
+#define NG_VBOXNETFLT_HOOK_IN "input"
+/** Output netgraph hook name */
+#define NG_VBOXNETFLT_HOOK_OUT "output"
+
+/** mbuf tag identifier */
+#define MTAG_VBOX 0x56424f58
+/** mbuf packet tag */
+#define PACKET_TAG_VBOX 128
+
+#if defined(__FreeBSD_version) && __FreeBSD_version >= 800500
+# include <sys/jail.h>
+# include <net/vnet.h>
+
+# define VBOXCURVNET_SET(arg) CURVNET_SET_QUIET(arg)
+# define VBOXCURVNET_SET_FROM_UCRED() VBOXCURVNET_SET(CRED_TO_VNET(curthread->td_ucred))
+# define VBOXCURVNET_RESTORE() CURVNET_RESTORE()
+
+#else /* !defined(__FreeBSD_version) || __FreeBSD_version < 800500 */
+
+# define VBOXCURVNET_SET(arg)
+# define VBOXCURVNET_SET_FROM_UCRED()
+# define VBOXCURVNET_RESTORE()
+
+#endif /* !defined(__FreeBSD_version) || __FreeBSD_version < 800500 */
+
+/*
+ * Netgraph command list, we don't support any
+ * additional commands.
+ */
+static const struct ng_cmdlist ng_vboxnetflt_cmdlist[] =
+{
+ { 0 }
+};
+
+/*
+ * Netgraph type definition
+ */
+static struct ng_type ng_vboxnetflt_typestruct =
+{
+ .version = NG_ABI_VERSION,
+ .name = NG_VBOXNETFLT_NODE_TYPE,
+ .mod_event = vboxnetflt_modevent,
+ .constructor= ng_vboxnetflt_constructor,
+ .rcvmsg = ng_vboxnetflt_rcvmsg,
+ .shutdown = ng_vboxnetflt_shutdown,
+ .newhook = ng_vboxnetflt_newhook,
+ .rcvdata = ng_vboxnetflt_rcvdata,
+ .disconnect = ng_vboxnetflt_disconnect,
+ .cmdlist = ng_vboxnetflt_cmdlist,
+};
+NETGRAPH_INIT(vboxnetflt, &ng_vboxnetflt_typestruct);
+
+/*
+ * Use vboxnetflt because the kernel module is named vboxnetflt and vboxnetadp
+ * depends on this when loading dependencies.
+ * NETGRAP_INIT will prefix the given name with ng_ so MODULE_DEPEND needs the
+ * prefixed name.
+ */
+MODULE_VERSION(vboxnetflt, 1);
+MODULE_DEPEND(ng_vboxnetflt, vboxdrv, 1, 1, 1);
+
+/**
+ * The (common) global data.
+ */
+static VBOXNETFLTGLOBALS g_VBoxNetFltGlobals;
+
+/**
+ * Module event handler, called from netgraph subsystem.
+ */
+static int vboxnetflt_modevent(struct module *pMod, int enmEventType, void *pvArg)
+{
+ int rc;
+
+ Log(("VBoxNetFltFreeBSDModuleEvent\n"));
+
+ switch (enmEventType)
+ {
+ case MOD_LOAD:
+ rc = RTR0Init(0);
+ if (RT_FAILURE(rc))
+ {
+ printf("RTR0Init failed %d\n", rc);
+ return RTErrConvertToErrno(rc);
+ }
+
+ memset(&g_VBoxNetFltGlobals, 0, sizeof(VBOXNETFLTGLOBALS));
+ rc = vboxNetFltInitGlobalsAndIdc(&g_VBoxNetFltGlobals);
+ if (RT_FAILURE(rc))
+ {
+ printf("vboxNetFltInitGlobalsAndIdc failed %d\n", rc);
+ return RTErrConvertToErrno(rc);
+ }
+ /* No MODULE_VERSION in ng_ether so we can't MODULE_DEPEND it */
+ kern_kldload(curthread, "ng_ether", NULL);
+ break;
+
+ case MOD_UNLOAD:
+ rc = vboxNetFltTryDeleteIdcAndGlobals(&g_VBoxNetFltGlobals);
+ memset(&g_VBoxNetFltGlobals, 0, sizeof(VBOXNETFLTGLOBALS));
+ RTR0Term();
+ break;
+
+ case MOD_SHUTDOWN:
+ case MOD_QUIESCE:
+ default:
+ return EOPNOTSUPP;
+ }
+
+ if (RT_SUCCESS(rc))
+ return 0;
+ return RTErrConvertToErrno(rc);
+}
+
+/*
+ * Convert from mbufs to vbox scatter-gather data structure
+ */
+static void vboxNetFltFreeBSDMBufToSG(PVBOXNETFLTINS pThis, struct mbuf *m, PINTNETSG pSG,
+ unsigned int cSegs, unsigned int segOffset)
+{
+ static uint8_t const s_abZero[128] = {0};
+ unsigned int i;
+ struct mbuf *m0;
+
+ IntNetSgInitTempSegs(pSG, m_length(m, NULL), cSegs, 0 /*cSegsUsed*/);
+
+ for (m0 = m, i = segOffset; m0; m0 = m0->m_next)
+ {
+ if (m0->m_len == 0)
+ continue;
+
+ pSG->aSegs[i].cb = m0->m_len;
+ pSG->aSegs[i].pv = mtod(m0, uint8_t *);
+ pSG->aSegs[i].Phys = NIL_RTHCPHYS;
+ i++;
+ }
+
+#ifdef PADD_RUNT_FRAMES_FROM_HOST
+ if (pSG->cbTotal < 60)
+ {
+ pSG->aSegs[i].Phys = NIL_RTHCPHYS;
+ pSG->aSegs[i].pv = (void *)&s_abZero[0];
+ pSG->aSegs[i].cb = 60 - pSG->cbTotal;
+ pSG->cbTotal = 60;
+ i++;
+ }
+#endif
+
+ pSG->cSegsUsed = i;
+}
+
+/*
+ * Convert to mbufs from vbox scatter-gather data structure
+ */
+static struct mbuf * vboxNetFltFreeBSDSGMBufFromSG(PVBOXNETFLTINS pThis, PINTNETSG pSG)
+{
+ struct mbuf *m;
+ int error;
+ unsigned int i;
+
+ if (pSG->cbTotal == 0)
+ return (NULL);
+
+ m = m_getcl(M_WAITOK, MT_DATA, M_PKTHDR);
+ if (m == NULL)
+ return (NULL);
+
+ m->m_pkthdr.len = m->m_len = 0;
+ m->m_pkthdr.rcvif = NULL;
+
+ for (i = 0; i < pSG->cSegsUsed; i++)
+ {
+ error = m_append(m, pSG->aSegs[i].cb, pSG->aSegs[i].pv);
+ if (error == 0)
+ {
+ m_freem(m);
+ return (NULL);
+ }
+ }
+ return (m);
+}
+
+
+static int ng_vboxnetflt_constructor(node_p node)
+{
+ /* Nothing to do */
+ return (EINVAL);
+}
+
+/*
+ * Setup netgraph hooks
+ */
+static int ng_vboxnetflt_newhook(node_p node, hook_p hook, const char *name)
+{
+ PVBOXNETFLTINS pThis = NG_NODE_PRIVATE(node);
+
+ if (strcmp(name, NG_VBOXNETFLT_HOOK_IN) == 0)
+ {
+#if __FreeBSD_version >= 800000
+ NG_HOOK_SET_TO_INBOUND(hook);
+#endif
+ pThis->u.s.input = hook;
+ }
+ else if (strcmp(name, NG_VBOXNETFLT_HOOK_OUT) == 0)
+ {
+ pThis->u.s.output = hook;
+ }
+ else
+ return (EINVAL);
+
+ NG_HOOK_HI_STACK(hook);
+ return (0);
+}
+
+/**
+ * Netgraph message processing for node specific messages.
+ * We don't accept any special messages so this is not used.
+ */
+static int ng_vboxnetflt_rcvmsg(node_p node, item_p item, hook_p lasthook)
+{
+ PVBOXNETFLTINS pThis = NG_NODE_PRIVATE(node);
+ struct ng_mesg *msg;
+ int error = 0;
+
+ NGI_GET_MSG(item, msg);
+ if (msg->header.typecookie != NGM_VBOXNETFLT_COOKIE)
+ return (EINVAL);
+
+ switch (msg->header.cmd)
+ {
+ default:
+ error = EINVAL;
+ }
+ return (error);
+}
+
+/**
+ * Handle data on netgraph hooks.
+ * Frames processing is deferred to a taskqueue because this might
+ * be called with non-sleepable locks held and code paths inside
+ * the virtual switch might sleep.
+ */
+static int ng_vboxnetflt_rcvdata(hook_p hook, item_p item)
+{
+ const node_p node = NG_HOOK_NODE(hook);
+ PVBOXNETFLTINS pThis = NG_NODE_PRIVATE(node);
+ struct ifnet *ifp = pThis->u.s.ifp;
+ struct mbuf *m;
+ struct m_tag *mtag;
+ bool fActive;
+
+ VBOXCURVNET_SET(ifp->if_vnet);
+ fActive = vboxNetFltTryRetainBusyActive(pThis);
+
+ NGI_GET_M(item, m);
+ NG_FREE_ITEM(item);
+
+ /* Locate tag to see if processing should be skipped for this frame */
+ mtag = m_tag_locate(m, MTAG_VBOX, PACKET_TAG_VBOX, NULL);
+ if (mtag != NULL)
+ {
+ m_tag_unlink(m, mtag);
+ m_tag_free(mtag);
+ }
+
+ /*
+ * Handle incoming hook. This is connected to the
+ * input path of the interface, thus handling incoming frames.
+ */
+ if (pThis->u.s.input == hook)
+ {
+ if (mtag != NULL || !fActive)
+ {
+ ether_demux(ifp, m);
+ if (fActive)
+ vboxNetFltRelease(pThis, true /*fBusy*/);
+ VBOXCURVNET_RESTORE();
+ return (0);
+ }
+ mtx_lock_spin(&pThis->u.s.inq.ifq_mtx);
+ _IF_ENQUEUE(&pThis->u.s.inq, m);
+ mtx_unlock_spin(&pThis->u.s.inq.ifq_mtx);
+#if __FreeBSD_version > 1100100
+ taskqueue_enqueue(taskqueue_fast, &pThis->u.s.tskin);
+#else
+ taskqueue_enqueue_fast(taskqueue_fast, &pThis->u.s.tskin);
+#endif
+ }
+ /*
+ * Handle mbufs on the outgoing hook, frames going to the interface
+ */
+ else if (pThis->u.s.output == hook)
+ {
+ if (mtag != NULL || !fActive)
+ {
+ int rc = ether_output_frame(ifp, m);
+ if (fActive)
+ vboxNetFltRelease(pThis, true /*fBusy*/);
+ VBOXCURVNET_RESTORE();
+ return rc;
+ }
+ mtx_lock_spin(&pThis->u.s.outq.ifq_mtx);
+ _IF_ENQUEUE(&pThis->u.s.outq, m);
+ mtx_unlock_spin(&pThis->u.s.outq.ifq_mtx);
+#if __FreeBSD_version > 1100100
+ taskqueue_enqueue(taskqueue_fast, &pThis->u.s.tskout);
+#else
+ taskqueue_enqueue_fast(taskqueue_fast, &pThis->u.s.tskout);
+#endif
+ }
+ else
+ {
+ m_freem(m);
+ }
+
+ if (fActive)
+ vboxNetFltRelease(pThis, true /*fBusy*/);
+ VBOXCURVNET_RESTORE();
+ return (0);
+}
+
+static int ng_vboxnetflt_shutdown(node_p node)
+{
+ PVBOXNETFLTINS pThis = NG_NODE_PRIVATE(node);
+ bool fActive;
+
+ /* Prevent node shutdown if we're active */
+ if (pThis->enmTrunkState == INTNETTRUNKIFSTATE_ACTIVE)
+ return (EBUSY);
+ NG_NODE_UNREF(node);
+ return (0);
+}
+
+static int ng_vboxnetflt_disconnect(hook_p hook)
+{
+ return (0);
+}
+
+/**
+ * Input processing task, handles incoming frames
+ */
+static void vboxNetFltFreeBSDinput(void *arg, int pending)
+{
+ PVBOXNETFLTINS pThis = (PVBOXNETFLTINS)arg;
+ struct mbuf *m, *m0;
+ struct ifnet *ifp = pThis->u.s.ifp;
+ unsigned int cSegs = 0;
+ bool fDropIt = false, fActive;
+ PINTNETSG pSG;
+
+ VBOXCURVNET_SET(ifp->if_vnet);
+ vboxNetFltRetain(pThis, true /* fBusy */);
+ for (;;)
+ {
+ mtx_lock_spin(&pThis->u.s.inq.ifq_mtx);
+ _IF_DEQUEUE(&pThis->u.s.inq, m);
+ mtx_unlock_spin(&pThis->u.s.inq.ifq_mtx);
+ if (m == NULL)
+ break;
+
+ for (m0 = m; m0 != NULL; m0 = m0->m_next)
+ if (m0->m_len > 0)
+ cSegs++;
+
+#ifdef PADD_RUNT_FRAMES_FROM_HOST
+ if (m_length(m, NULL) < 60)
+ cSegs++;
+#endif
+
+ /* Create a copy and deliver to the virtual switch */
+ pSG = RTMemTmpAlloc(RT_UOFFSETOF_DYN(INTNETSG, aSegs[cSegs]));
+ vboxNetFltFreeBSDMBufToSG(pThis, m, pSG, cSegs, 0);
+ fDropIt = pThis->pSwitchPort->pfnRecv(pThis->pSwitchPort, NULL /* pvIf */, pSG, INTNETTRUNKDIR_WIRE);
+ RTMemTmpFree(pSG);
+ if (fDropIt)
+ m_freem(m);
+ else
+ ether_demux(ifp, m);
+ }
+ vboxNetFltRelease(pThis, true /* fBusy */);
+ VBOXCURVNET_RESTORE();
+}
+
+/**
+ * Output processing task, handles outgoing frames
+ */
+static void vboxNetFltFreeBSDoutput(void *arg, int pending)
+{
+ PVBOXNETFLTINS pThis = (PVBOXNETFLTINS)arg;
+ struct mbuf *m, *m0;
+ struct ifnet *ifp = pThis->u.s.ifp;
+ unsigned int cSegs = 0;
+ bool fDropIt = false, fActive;
+ PINTNETSG pSG;
+
+ VBOXCURVNET_SET(ifp->if_vnet);
+ vboxNetFltRetain(pThis, true /* fBusy */);
+ for (;;)
+ {
+ mtx_lock_spin(&pThis->u.s.outq.ifq_mtx);
+ _IF_DEQUEUE(&pThis->u.s.outq, m);
+ mtx_unlock_spin(&pThis->u.s.outq.ifq_mtx);
+ if (m == NULL)
+ break;
+
+ for (m0 = m; m0 != NULL; m0 = m0->m_next)
+ if (m0->m_len > 0)
+ cSegs++;
+
+#ifdef PADD_RUNT_FRAMES_FROM_HOST
+ if (m_length(m, NULL) < 60)
+ cSegs++;
+#endif
+ /* Create a copy and deliver to the virtual switch */
+ pSG = RTMemTmpAlloc(RT_UOFFSETOF_DYN(INTNETSG, aSegs[cSegs]));
+ vboxNetFltFreeBSDMBufToSG(pThis, m, pSG, cSegs, 0);
+ fDropIt = pThis->pSwitchPort->pfnRecv(pThis->pSwitchPort, NULL /* pvIf */, pSG, INTNETTRUNKDIR_HOST);
+ RTMemTmpFree(pSG);
+
+ if (fDropIt)
+ m_freem(m);
+ else
+ ether_output_frame(ifp, m);
+ }
+ vboxNetFltRelease(pThis, true /* fBusy */);
+ VBOXCURVNET_RESTORE();
+}
+
+/**
+ * Called to deliver a frame to either the host, the wire or both.
+ */
+int vboxNetFltPortOsXmit(PVBOXNETFLTINS pThis, void *pvIfData, PINTNETSG pSG, uint32_t fDst)
+{
+ NOREF(pvIfData);
+
+ void (*input_f)(struct ifnet *, struct mbuf *);
+ struct ifnet *ifp;
+ struct mbuf *m;
+ struct m_tag *mtag;
+ bool fActive;
+ int error;
+
+ ifp = ASMAtomicUoReadPtrT(&pThis->u.s.ifp, struct ifnet *);
+ VBOXCURVNET_SET(ifp->if_vnet);
+
+ if (fDst & INTNETTRUNKDIR_WIRE)
+ {
+ m = vboxNetFltFreeBSDSGMBufFromSG(pThis, pSG);
+ if (m == NULL)
+ return VERR_NO_MEMORY;
+ m = m_pullup(m, ETHER_HDR_LEN);
+ if (m == NULL)
+ return VERR_NO_MEMORY;
+
+ m->m_flags |= M_PKTHDR;
+ ether_output_frame(ifp, m);
+ }
+
+ if (fDst & INTNETTRUNKDIR_HOST)
+ {
+ m = vboxNetFltFreeBSDSGMBufFromSG(pThis, pSG);
+ if (m == NULL)
+ return VERR_NO_MEMORY;
+ m = m_pullup(m, ETHER_HDR_LEN);
+ if (m == NULL)
+ return VERR_NO_MEMORY;
+ /*
+ * Delivering packets to the host will be captured by the
+ * input hook. Tag the packet with a mbuf tag so that we
+ * can skip re-delivery of the packet to the guest during
+ * input hook processing.
+ */
+ mtag = m_tag_alloc(MTAG_VBOX, PACKET_TAG_VBOX, 0, M_NOWAIT);
+ if (mtag == NULL)
+ {
+ m_freem(m);
+ return VERR_NO_MEMORY;
+ }
+
+ m_tag_init(m);
+ m_tag_prepend(m, mtag);
+ m->m_flags |= M_PKTHDR;
+ m->m_pkthdr.rcvif = ifp;
+ ifp->if_input(ifp, m);
+ }
+ VBOXCURVNET_RESTORE();
+ return VINF_SUCCESS;
+}
+
+static bool vboxNetFltFreeBsdIsPromiscuous(PVBOXNETFLTINS pThis)
+{
+ /** @todo This isn't taking into account that we put the interface in
+ * promiscuous mode. */
+ return (pThis->u.s.flags & IFF_PROMISC) ? true : false;
+}
+
+int vboxNetFltOsInitInstance(PVBOXNETFLTINS pThis, void *pvContext)
+{
+ char nam[NG_NODESIZ];
+ struct ifnet *ifp;
+ node_p node;
+
+ VBOXCURVNET_SET_FROM_UCRED();
+ NOREF(pvContext);
+ ifp = ifunit(pThis->szName);
+ if (ifp == NULL)
+ return VERR_INTNET_FLT_IF_NOT_FOUND;
+
+ /* Create a new netgraph node for this instance */
+ if (ng_make_node_common(&ng_vboxnetflt_typestruct, &node) != 0)
+ return VERR_INTERNAL_ERROR;
+
+ RTSpinlockAcquire(pThis->hSpinlock);
+
+ ASMAtomicUoWritePtr(&pThis->u.s.ifp, ifp);
+ pThis->u.s.node = node;
+ bcopy(IF_LLADDR(ifp), &pThis->u.s.MacAddr, ETHER_ADDR_LEN);
+ ASMAtomicUoWriteBool(&pThis->fDisconnectedFromHost, false);
+
+ /* Initialize deferred input queue */
+ bzero(&pThis->u.s.inq, sizeof(struct ifqueue));
+ mtx_init(&pThis->u.s.inq.ifq_mtx, "vboxnetflt inq", NULL, MTX_SPIN);
+ TASK_INIT(&pThis->u.s.tskin, 0, vboxNetFltFreeBSDinput, pThis);
+
+ /* Initialize deferred output queue */
+ bzero(&pThis->u.s.outq, sizeof(struct ifqueue));
+ mtx_init(&pThis->u.s.outq.ifq_mtx, "vboxnetflt outq", NULL, MTX_SPIN);
+ TASK_INIT(&pThis->u.s.tskout, 0, vboxNetFltFreeBSDoutput, pThis);
+
+ RTSpinlockRelease(pThis->hSpinlock);
+
+ NG_NODE_SET_PRIVATE(node, pThis);
+
+ /* Attempt to name it vboxnetflt_<ifname> */
+ snprintf(nam, NG_NODESIZ, "vboxnetflt_%s", pThis->szName);
+ ng_name_node(node, nam);
+
+ /* Report MAC address, promiscuous mode and GSO capabilities. */
+ /** @todo keep these reports up to date, either by polling for changes or
+ * intercept some control flow if possible. */
+ if (vboxNetFltTryRetainBusyNotDisconnected(pThis))
+ {
+ Assert(pThis->pSwitchPort);
+ pThis->pSwitchPort->pfnReportMacAddress(pThis->pSwitchPort, &pThis->u.s.MacAddr);
+ pThis->pSwitchPort->pfnReportPromiscuousMode(pThis->pSwitchPort, vboxNetFltFreeBsdIsPromiscuous(pThis));
+ pThis->pSwitchPort->pfnReportGsoCapabilities(pThis->pSwitchPort, 0, INTNETTRUNKDIR_WIRE | INTNETTRUNKDIR_HOST);
+ pThis->pSwitchPort->pfnReportNoPreemptDsts(pThis->pSwitchPort, 0 /* none */);
+ vboxNetFltRelease(pThis, true /*fBusy*/);
+ }
+ VBOXCURVNET_RESTORE();
+
+ return VINF_SUCCESS;
+}
+
+bool vboxNetFltOsMaybeRediscovered(PVBOXNETFLTINS pThis)
+{
+ struct ifnet *ifp, *ifp0;
+
+ ifp = ASMAtomicUoReadPtrT(&pThis->u.s.ifp, struct ifnet *);
+ VBOXCURVNET_SET(ifp->if_vnet);
+ /*
+ * Attempt to check if the interface is still there and re-initialize if
+ * something has changed.
+ */
+ ifp0 = ifunit(pThis->szName);
+ if (ifp != ifp0)
+ {
+ ASMAtomicUoWriteBool(&pThis->fDisconnectedFromHost, true);
+ ng_rmnode_self(pThis->u.s.node);
+ pThis->u.s.node = NULL;
+ }
+ VBOXCURVNET_RESTORE();
+
+ if (ifp0 != NULL)
+ {
+ vboxNetFltOsDeleteInstance(pThis);
+ vboxNetFltOsInitInstance(pThis, NULL);
+ }
+
+ return !ASMAtomicUoReadBool(&pThis->fDisconnectedFromHost);
+}
+
+void vboxNetFltOsDeleteInstance(PVBOXNETFLTINS pThis)
+{
+
+ taskqueue_drain(taskqueue_fast, &pThis->u.s.tskin);
+ taskqueue_drain(taskqueue_fast, &pThis->u.s.tskout);
+
+ mtx_destroy(&pThis->u.s.inq.ifq_mtx);
+ mtx_destroy(&pThis->u.s.outq.ifq_mtx);
+
+ VBOXCURVNET_SET_FROM_UCRED();
+ if (pThis->u.s.node != NULL)
+ ng_rmnode_self(pThis->u.s.node);
+ VBOXCURVNET_RESTORE();
+ pThis->u.s.node = NULL;
+}
+
+int vboxNetFltOsPreInitInstance(PVBOXNETFLTINS pThis)
+{
+
+ pThis->u.s.ifp = NULL;
+ pThis->u.s.flags = 0;
+ pThis->u.s.node = NULL;
+ return VINF_SUCCESS;
+}
+
+void vboxNetFltPortOsSetActive(PVBOXNETFLTINS pThis, bool fActive)
+{
+ struct ifnet *ifp;
+ struct ifreq ifreq;
+ int error;
+ node_p node;
+ struct ng_mesg *msg;
+ struct ngm_connect *con;
+ struct ngm_rmhook *rm;
+ char path[NG_PATHSIZ];
+
+ Log(("%s: fActive:%d\n", __func__, fActive));
+
+ ifp = ASMAtomicUoReadPtrT(&pThis->u.s.ifp, struct ifnet *);
+ VBOXCURVNET_SET(ifp->if_vnet);
+ node = ASMAtomicUoReadPtrT(&pThis->u.s.node, node_p);
+
+ memset(&ifreq, 0, sizeof(struct ifreq));
+ /* Activate interface */
+ if (fActive)
+ {
+ pThis->u.s.flags = ifp->if_flags;
+ ifpromisc(ifp, 1);
+
+ /* ng_ether nodes are named after the interface name */
+ snprintf(path, sizeof(path), "%s:", ifp->if_xname);
+
+ /*
+ * Send a netgraph connect message to the ng_ether node
+ * assigned to the bridged interface. Connecting
+ * the hooks 'lower' (ng_ether) to out 'input'.
+ */
+ NG_MKMESSAGE(msg, NGM_GENERIC_COOKIE, NGM_CONNECT,
+ sizeof(struct ngm_connect), M_NOWAIT);
+ if (msg == NULL)
+ return;
+ con = (struct ngm_connect *)msg->data;
+ snprintf(con->path, NG_PATHSIZ, "vboxnetflt_%s:", ifp->if_xname);
+ strlcpy(con->ourhook, "lower", NG_HOOKSIZ);
+ strlcpy(con->peerhook, "input", NG_HOOKSIZ);
+ NG_SEND_MSG_PATH(error, node, msg, path, 0);
+
+ /*
+ * Do the same for the hooks 'upper' (ng_ether) and our
+ * 'output' hook.
+ */
+ NG_MKMESSAGE(msg, NGM_GENERIC_COOKIE, NGM_CONNECT,
+ sizeof(struct ngm_connect), M_NOWAIT);
+ if (msg == NULL)
+ return;
+ con = (struct ngm_connect *)msg->data;
+ snprintf(con->path, NG_PATHSIZ, "vboxnetflt_%s:",
+ ifp->if_xname);
+ strlcpy(con->ourhook, "upper", sizeof(con->ourhook));
+ strlcpy(con->peerhook, "output", sizeof(con->peerhook));
+ NG_SEND_MSG_PATH(error, node, msg, path, 0);
+ }
+ else
+ {
+ /* De-activate interface */
+ pThis->u.s.flags = 0;
+ ifpromisc(ifp, 0);
+
+ /* Disconnect msgs are addressed to ourself */
+ snprintf(path, sizeof(path), "vboxnetflt_%s:", ifp->if_xname);
+
+ /*
+ * Send a netgraph message to disconnect our 'input' hook
+ */
+ NG_MKMESSAGE(msg, NGM_GENERIC_COOKIE, NGM_RMHOOK,
+ sizeof(struct ngm_rmhook), M_NOWAIT);
+ if (msg == NULL)
+ return;
+ rm = (struct ngm_rmhook *)msg->data;
+ strlcpy(rm->ourhook, "input", NG_HOOKSIZ);
+ NG_SEND_MSG_PATH(error, node, msg, path, 0);
+
+ /*
+ * Send a netgraph message to disconnect our 'output' hook
+ */
+ NG_MKMESSAGE(msg, NGM_GENERIC_COOKIE, NGM_RMHOOK,
+ sizeof(struct ngm_rmhook), M_NOWAIT);
+ if (msg == NULL)
+ return;
+ rm = (struct ngm_rmhook *)msg->data;
+ strlcpy(rm->ourhook, "output", NG_HOOKSIZ);
+ NG_SEND_MSG_PATH(error, node, msg, path, 0);
+ }
+ VBOXCURVNET_RESTORE();
+}
+
+int vboxNetFltOsDisconnectIt(PVBOXNETFLTINS pThis)
+{
+ return VINF_SUCCESS;
+}
+
+int vboxNetFltOsConnectIt(PVBOXNETFLTINS pThis)
+{
+ return VINF_SUCCESS;
+}
+
+void vboxNetFltPortOsNotifyMacAddress(PVBOXNETFLTINS pThis, void *pvIfData, PCRTMAC pMac)
+{
+ NOREF(pThis); NOREF(pvIfData); NOREF(pMac);
+}
+
+int vboxNetFltPortOsConnectInterface(PVBOXNETFLTINS pThis, void *pvIf, void **ppvIfData)
+{
+ /* Nothing to do */
+ NOREF(pThis); NOREF(pvIf); NOREF(ppvIfData);
+ return VINF_SUCCESS;
+}
+
+int vboxNetFltPortOsDisconnectInterface(PVBOXNETFLTINS pThis, void *pvIfData)
+{
+ /* Nothing to do */
+ NOREF(pThis); NOREF(pvIfData);
+ return VINF_SUCCESS;
+}
+
diff --git a/src/VBox/HostDrivers/VBoxNetFlt/freebsd/files_vboxnetflt b/src/VBox/HostDrivers/VBoxNetFlt/freebsd/files_vboxnetflt
new file mode 100755
index 00000000..602b72e4
--- /dev/null
+++ b/src/VBox/HostDrivers/VBoxNetFlt/freebsd/files_vboxnetflt
@@ -0,0 +1,99 @@
+#!/bin/sh
+# $Id: files_vboxnetflt $
+## @file
+# Shared file between Makefile.kmk and export_modules.sh.
+#
+
+#
+# Copyright (C) 2007-2023 Oracle and/or its affiliates.
+#
+# This file is part of VirtualBox base platform packages, as
+# available from https://www.virtualbox.org.
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation, in version 3 of the
+# License.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, see <https://www.gnu.org/licenses>.
+#
+# The contents of this file may alternatively be used under the terms
+# of the Common Development and Distribution License Version 1.0
+# (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+# in the VirtualBox distribution, in which case the provisions of the
+# CDDL are applicable instead of those of the GPL.
+#
+# You may elect to license modified versions of this file under the
+# terms and conditions of either the GPL or the CDDL or both.
+#
+# SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+#
+
+VBOX_VBOXNETFLT_SOURCES=" \
+ ${PATH_ROOT}/include/iprt/alloc.h=>include/iprt/alloc.h \
+ ${PATH_ROOT}/include/iprt/alloca.h=>include/iprt/alloca.h \
+ ${PATH_ROOT}/include/iprt/asm.h=>include/iprt/asm.h \
+ ${PATH_ROOT}/include/iprt/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/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/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/uni.h=>include/iprt/uni.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/x86-helpers.h=>include/iprt/x86-helpers.h \
+ ${PATH_ROOT}/include/iprt/nocrt/limits.h=>include/iprt/nocrt/limits.h \
+ ${PATH_ROOT}/include/VBox/cdefs.h=>include/VBox/cdefs.h \
+ ${PATH_ROOT}/include/VBox/err.h=>include/VBox/err.h \
+ ${PATH_ROOT}/include/VBox/log.h=>include/VBox/log.h \
+ ${PATH_ROOT}/include/VBox/intnet.h=>include/VBox/intnet.h \
+ ${PATH_ROOT}/include/VBox/intnetinline.h=>include/VBox/intnetinline.h \
+ ${PATH_ROOT}/include/VBox/vmm/stam.h=>include/VBox/vmm/stam.h \
+ ${PATH_ROOT}/include/VBox/sup.h=>include/VBox/sup.h \
+ ${PATH_ROOT}/include/VBox/types.h=>include/VBox/types.h \
+ ${PATH_ROOT}/include/VBox/version.h=>include/VBox/version.h \
+ ${PATH_ROOT}/include/VBox/SUPDrvMangling.h=>include/VBox/SUPDrvMangling.h \
+ ${PATH_ROOT}/src/VBox/HostDrivers/VBoxNetFlt/freebsd/VBoxNetFlt-freebsd.c=>VBoxNetFlt-freebsd.c \
+ ${PATH_ROOT}/src/VBox/HostDrivers/VBoxNetFlt/VBoxNetFlt.c=>VBoxNetFlt.c \
+ ${PATH_ROOT}/src/VBox/HostDrivers/VBoxNetFlt/VBoxNetFltInternal.h=>VBoxNetFltInternal.h \
+ ${PATH_ROOT}/src/VBox/HostDrivers/Support/SUPDrvIDC.h=>SUPDrvIDC.h \
+ ${PATH_ROOT}/src/VBox/HostDrivers/Support/SUPR0IdcClient.c=>SUPR0IdcClient.c \
+ ${PATH_ROOT}/src/VBox/HostDrivers/Support/SUPR0IdcClientComponent.c=>SUPR0IdcClientComponent.c \
+ ${PATH_ROOT}/src/VBox/HostDrivers/Support/SUPR0IdcClientInternal.h=>SUPR0IdcClientInternal.h \
+ ${PATH_ROOT}/src/VBox/HostDrivers/Support/freebsd/SUPR0IdcClient-freebsd.c=>SUPR0IdcClient-freebsd.c \
+ ${PATH_ROOT}/src/VBox/Runtime/r0drv/freebsd/the-freebsd-kernel.h=>r0drv/freebsd/the-freebsd-kernel.h \
+ ${PATH_OUT}/version-generated.h=>version-generated.h \
+ ${PATH_OUT}/product-generated.h=>product-generated.h \
+"
+
diff --git a/src/VBox/HostDrivers/VBoxNetFlt/linux/Makefile b/src/VBox/HostDrivers/VBoxNetFlt/linux/Makefile
new file mode 100644
index 00000000..9c5d5b80
--- /dev/null
+++ b/src/VBox/HostDrivers/VBoxNetFlt/linux/Makefile
@@ -0,0 +1,81 @@
+# $Id: Makefile $
+## @file
+# Makefile for the VirtualBox Linux Host Network Filter Driver.
+#
+
+#
+# Copyright (C) 2006-2023 Oracle and/or its affiliates.
+#
+# This file is part of VirtualBox base platform packages, as
+# available from https://www.virtualbox.org.
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation, in version 3 of the
+# License.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, see <https://www.gnu.org/licenses>.
+#
+# The contents of this file may alternatively be used under the terms
+# of the Common Development and Distribution License Version 1.0
+# (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+# in the VirtualBox distribution, in which case the provisions of the
+# CDDL are applicable instead of those of the GPL.
+#
+# You may elect to license modified versions of this file under the
+# terms and conditions of either the GPL or the CDDL or both.
+#
+# SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+#
+
+# Linux kbuild sets this to our source directory if we are called from there
+obj ?= $(CURDIR)
+include $(obj)/Makefile-header.gmk
+VBOXNETFLT_DIR := $(VBOX_MODULE_SRC_DIR)
+
+# Allow building directly from the subdirectory without assuming the toplevel
+# makefile has done the copying. Not the default use case, but can be handy.
+ifndef KBUILD_EXTRA_SYMBOLS
+KBUILD_EXTRA_SYMBOLS=$(abspath $(VBOXNETFLT_DIR)/../vboxdrv/Module.symvers)
+endif
+
+VBOXMOD_NAME = vboxnetflt
+VBOXMOD_OBJS = \
+ linux/VBoxNetFlt-linux.o \
+ VBoxNetFlt.o \
+ SUPR0IdcClient.o \
+ SUPR0IdcClientComponent.o \
+ linux/SUPR0IdcClient-linux.o
+ifeq ($(VBOX_KBUILD_TARGET_ARCH),x86)
+VBOXMOD_OBJS += \
+ math/gcc/divdi3.o \
+ math/gcc/moddi3.o \
+ math/gcc/qdivrem.o \
+ math/gcc/udivdi3.o \
+ math/gcc/udivmoddi4.o \
+ math/gcc/divdi3.o \
+ math/gcc/umoddi3.o
+endif
+VBOXMOD_INCL = \
+ $(VBOXNETFLT_DIR) \
+ $(VBOXNETFLT_DIR)include \
+ $(VBOXNETFLT_DIR)r0drv/linux
+VBOXMOD_DEFS = \
+ RT_OS_LINUX \
+ IN_RING0 \
+ IN_RT_R0 \
+ IN_SUP_R0 \
+ VBOX \
+ RT_WITH_VBOX \
+ VBOX_WITH_HARDENING \
+ VBOX_WITH_64_BITS_GUESTS # <-- must be consistent with Config.kmk!
+VBOXMOD_CFLAGS = -include $(VBOXNETFLT_DIR)include/VBox/SUPDrvMangling.h -fno-pie -Wno-declaration-after-statement
+
+include $(obj)/Makefile-footer.gmk
+
diff --git a/src/VBox/HostDrivers/VBoxNetFlt/linux/Makefile.kup b/src/VBox/HostDrivers/VBoxNetFlt/linux/Makefile.kup
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/src/VBox/HostDrivers/VBoxNetFlt/linux/Makefile.kup
diff --git a/src/VBox/HostDrivers/VBoxNetFlt/linux/VBoxNetFlt-linux.c b/src/VBox/HostDrivers/VBoxNetFlt/linux/VBoxNetFlt-linux.c
new file mode 100644
index 00000000..eaf07e8c
--- /dev/null
+++ b/src/VBox/HostDrivers/VBoxNetFlt/linux/VBoxNetFlt-linux.c
@@ -0,0 +1,2609 @@
+/* $Id: VBoxNetFlt-linux.c $ */
+/** @file
+ * VBoxNetFlt - Network Filter Driver (Host), Linux Specific Code.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#define LOG_GROUP LOG_GROUP_NET_FLT_DRV
+#define VBOXNETFLT_LINUX_NO_XMIT_QUEUE
+#include "the-linux-kernel.h"
+#include "version-generated.h"
+#include "revision-generated.h"
+#include "product-generated.h"
+#if RTLNX_VER_MIN(2,6,24)
+# include <linux/nsproxy.h>
+#endif
+#if RTLNX_VER_MIN(6,4,10) || RTLNX_RHEL_RANGE(9,4, 9,99)
+# include <net/gso.h>
+#endif
+#include <linux/netdevice.h>
+#if RTLNX_VER_MAX(2,6,29) || RTLNX_VER_MIN(5,11,0)
+# include <linux/ethtool.h>
+#endif
+#include <linux/etherdevice.h>
+#include <linux/rtnetlink.h>
+#include <linux/miscdevice.h>
+#include <linux/inetdevice.h>
+#include <linux/in.h>
+#include <linux/ip.h>
+#include <linux/if_vlan.h>
+#if RTLNX_VER_MIN(4,5,0)
+# include <uapi/linux/pkt_cls.h>
+#endif
+#include <net/ipv6.h>
+#include <net/if_inet6.h>
+#include <net/addrconf.h>
+
+#include <VBox/log.h>
+#include <VBox/err.h>
+#include <VBox/intnetinline.h>
+#include <VBox/vmm/pdmnetinline.h>
+#include <VBox/param.h>
+#include <iprt/alloca.h>
+#include <iprt/assert.h>
+#include <iprt/spinlock.h>
+#include <iprt/semaphore.h>
+#include <iprt/initterm.h>
+#include <iprt/process.h>
+#include <iprt/mem.h>
+#include <iprt/net.h>
+#include <iprt/log.h>
+#include <iprt/mp.h>
+#include <iprt/mem.h>
+#include <iprt/time.h>
+
+#define VBOXNETFLT_OS_SPECFIC 1
+#include "../VBoxNetFltInternal.h"
+
+typedef struct VBOXNETFLTNOTIFIER {
+ struct notifier_block Notifier;
+ PVBOXNETFLTINS pThis;
+} VBOXNETFLTNOTIFIER;
+typedef struct VBOXNETFLTNOTIFIER *PVBOXNETFLTNOTIFIER;
+
+
+/*********************************************************************************************************************************
+* Defined Constants And Macros *
+*********************************************************************************************************************************/
+#define VBOX_FLT_NB_TO_INST(pNB) RT_FROM_MEMBER(pNB, VBOXNETFLTINS, u.s.Notifier)
+#define VBOX_FLT_PT_TO_INST(pPT) RT_FROM_MEMBER(pPT, VBOXNETFLTINS, u.s.PacketType)
+#ifndef VBOXNETFLT_LINUX_NO_XMIT_QUEUE
+# define VBOX_FLT_XT_TO_INST(pXT) RT_FROM_MEMBER(pXT, VBOXNETFLTINS, u.s.XmitTask)
+#endif
+
+#if RTLNX_VER_MIN(3,11,0)
+# define VBOX_NETDEV_NOTIFIER_INFO_TO_DEV(ptr) netdev_notifier_info_to_dev(ptr)
+#else
+# define VBOX_NETDEV_NOTIFIER_INFO_TO_DEV(ptr) ((struct net_device *)ptr)
+#endif
+
+#if RTLNX_VER_MIN(3,5,0)
+# define VBOX_SKB_KMAP_FRAG(frag) kmap_atomic(skb_frag_page(frag))
+# define VBOX_SKB_KUNMAP_FRAG(vaddr) kunmap_atomic(vaddr)
+#else
+# if RTLNX_VER_MIN(3,2,0)
+# define VBOX_SKB_KMAP_FRAG(frag) kmap_atomic(skb_frag_page(frag), KM_SKB_DATA_SOFTIRQ)
+# define VBOX_SKB_KUNMAP_FRAG(vaddr) kunmap_atomic(vaddr, KM_SKB_DATA_SOFTIRQ)
+# else
+# define VBOX_SKB_KMAP_FRAG(frag) kmap_atomic(frag->page, KM_SKB_DATA_SOFTIRQ)
+# define VBOX_SKB_KUNMAP_FRAG(vaddr) kunmap_atomic(vaddr, KM_SKB_DATA_SOFTIRQ)
+# endif
+#endif
+
+#if RTLNX_VER_MIN(2,6,34)
+# define VBOX_NETDEV_NAME(dev) netdev_name(dev)
+#else
+# define VBOX_NETDEV_NAME(dev) ((dev)->reg_state != NETREG_REGISTERED ? "(unregistered net_device)" : (dev)->name)
+#endif
+
+#if RTLNX_VER_MIN(2,6,25)
+# define VBOX_IPV4_IS_LOOPBACK(addr) ipv4_is_loopback(addr)
+# define VBOX_IPV4_IS_LINKLOCAL_169(addr) ipv4_is_linklocal_169(addr)
+#else
+# define VBOX_IPV4_IS_LOOPBACK(addr) ((addr & htonl(IN_CLASSA_NET)) == htonl(0x7f000000))
+# define VBOX_IPV4_IS_LINKLOCAL_169(addr) ((addr & htonl(IN_CLASSB_NET)) == htonl(0xa9fe0000))
+#endif
+
+#if RTLNX_VER_MIN(2,6,22)
+# define VBOX_SKB_RESET_NETWORK_HDR(skb) skb_reset_network_header(skb)
+# define VBOX_SKB_RESET_MAC_HDR(skb) skb_reset_mac_header(skb)
+# define VBOX_SKB_CSUM_OFFSET(skb) skb->csum_offset
+#else
+# define VBOX_SKB_RESET_NETWORK_HDR(skb) skb->nh.raw = skb->data
+# define VBOX_SKB_RESET_MAC_HDR(skb) skb->mac.raw = skb->data
+# define VBOX_SKB_CSUM_OFFSET(skb) skb->csum
+#endif
+
+#if RTLNX_VER_MIN(2,6,19)
+# define VBOX_SKB_CHECKSUM_HELP(skb) skb_checksum_help(skb)
+#else
+# define CHECKSUM_PARTIAL CHECKSUM_HW
+# if RTLNX_VER_MIN(2,6,10)
+# define VBOX_SKB_CHECKSUM_HELP(skb) skb_checksum_help(skb, 0)
+# else
+# if RTLNX_VER_MIN(2,6,7)
+# define VBOX_SKB_CHECKSUM_HELP(skb) skb_checksum_help(&skb, 0)
+# else
+# define VBOX_SKB_CHECKSUM_HELP(skb) (!skb_checksum_help(skb))
+# endif
+/* Versions prior 2.6.10 use stats for both bstats and qstats */
+# define bstats stats
+# define qstats stats
+# endif
+#endif
+
+#if RTLNX_VER_MIN(3,20,0) || RTLNX_RHEL_RANGE(7,2, 8,0) || RTLNX_RHEL_RANGE(6,8, 7,0)
+# define VBOX_HAVE_SKB_VLAN
+#endif
+
+#ifdef VBOX_HAVE_SKB_VLAN
+# define vlan_tx_tag_get(skb) skb_vlan_tag_get(skb)
+# define vlan_tx_tag_present(skb) skb_vlan_tag_present(skb)
+#endif
+
+#ifndef NET_IP_ALIGN
+# define NET_IP_ALIGN 2
+#endif
+
+#if 1
+/** Create scatter / gather segments for fragments. When not used, we will
+ * linearize the socket buffer before creating the internal networking SG. */
+# define VBOXNETFLT_SG_SUPPORT 1
+#endif
+
+#if RTLNX_VER_MIN(2,6,18)
+
+/** Indicates that the linux kernel may send us GSO frames. */
+# define VBOXNETFLT_WITH_GSO 1
+
+/** This enables or disables the transmitting of GSO frame from the internal
+ * network and to the host. */
+# define VBOXNETFLT_WITH_GSO_XMIT_HOST 1
+
+# if 0 /** @todo This is currently disable because it causes performance loss of 5-10%. */
+/** This enables or disables the transmitting of GSO frame from the internal
+ * network and to the wire. */
+# define VBOXNETFLT_WITH_GSO_XMIT_WIRE 1
+# endif
+
+/** This enables or disables the forwarding/flooding of GSO frame from the host
+ * to the internal network. */
+# define VBOXNETFLT_WITH_GSO_RECV 1
+
+#endif /* RTLNX_VER_MIN(2,6,18) */
+
+#if RTLNX_VER_MIN(2,6,29)
+/** This enables or disables handling of GSO frames coming from the wire (GRO). */
+# define VBOXNETFLT_WITH_GRO 1
+#endif
+
+/*
+ * GRO support was backported to RHEL 5.4
+ */
+#if RTLNX_RHEL_MAJ_PREREQ(5, 4)
+# define VBOXNETFLT_WITH_GRO 1
+#endif
+
+
+/*********************************************************************************************************************************
+* Internal Functions *
+*********************************************************************************************************************************/
+static int __init VBoxNetFltLinuxInit(void);
+static void __exit VBoxNetFltLinuxUnload(void);
+static void vboxNetFltLinuxForwardToIntNet(PVBOXNETFLTINS pThis, struct sk_buff *pBuf);
+
+
+/*********************************************************************************************************************************
+* Global Variables *
+*********************************************************************************************************************************/
+/**
+ * The (common) global data.
+ */
+static VBOXNETFLTGLOBALS g_VBoxNetFltGlobals;
+
+module_init(VBoxNetFltLinuxInit);
+module_exit(VBoxNetFltLinuxUnload);
+
+MODULE_AUTHOR(VBOX_VENDOR);
+MODULE_DESCRIPTION(VBOX_PRODUCT " Network Filter Driver");
+MODULE_LICENSE("GPL");
+#ifdef MODULE_VERSION
+MODULE_VERSION(VBOX_VERSION_STRING " r" RT_XSTR(VBOX_SVN_REV) " (" RT_XSTR(INTNETTRUNKIFPORT_VERSION) ")");
+#endif
+
+
+#if RTLNX_VER_MAX(2,6,12) && defined(LOG_ENABLED)
+unsigned dev_get_flags(const struct net_device *dev)
+{
+ unsigned flags;
+
+ flags = (dev->flags & ~(IFF_PROMISC |
+ IFF_ALLMULTI |
+ IFF_RUNNING)) |
+ (dev->gflags & (IFF_PROMISC |
+ IFF_ALLMULTI));
+
+ if (netif_running(dev) && netif_carrier_ok(dev))
+ flags |= IFF_RUNNING;
+
+ return flags;
+}
+#endif /* RTLNX_VER_MAX(2,6,12) */
+
+
+/**
+ * Initialize module.
+ *
+ * @returns appropriate status code.
+ */
+static int __init VBoxNetFltLinuxInit(void)
+{
+ int rc;
+ /*
+ * Initialize IPRT.
+ */
+ rc = RTR0Init(0);
+ if (RT_SUCCESS(rc))
+ {
+ Log(("VBoxNetFltLinuxInit\n"));
+
+ /*
+ * Initialize the globals and connect to the support driver.
+ *
+ * This will call back vboxNetFltOsOpenSupDrv (and maybe vboxNetFltOsCloseSupDrv)
+ * for establishing the connect to the support driver.
+ */
+ memset(&g_VBoxNetFltGlobals, 0, sizeof(g_VBoxNetFltGlobals));
+ rc = vboxNetFltInitGlobalsAndIdc(&g_VBoxNetFltGlobals);
+ if (RT_SUCCESS(rc))
+ {
+ LogRel(("VBoxNetFlt: Successfully started.\n"));
+ return 0;
+ }
+
+ LogRel(("VBoxNetFlt: failed to initialize device extension (rc=%d)\n", rc));
+ RTR0Term();
+ }
+ else
+ LogRel(("VBoxNetFlt: failed to initialize IPRT (rc=%d)\n", rc));
+
+ memset(&g_VBoxNetFltGlobals, 0, sizeof(g_VBoxNetFltGlobals));
+ return -RTErrConvertToErrno(rc);
+}
+
+
+/**
+ * Unload the module.
+ *
+ * @todo We have to prevent this if we're busy!
+ */
+static void __exit VBoxNetFltLinuxUnload(void)
+{
+ int rc;
+ Log(("VBoxNetFltLinuxUnload\n"));
+ Assert(vboxNetFltCanUnload(&g_VBoxNetFltGlobals));
+
+ /*
+ * Undo the work done during start (in reverse order).
+ */
+ rc = vboxNetFltTryDeleteIdcAndGlobals(&g_VBoxNetFltGlobals);
+ AssertRC(rc); NOREF(rc);
+
+ RTR0Term();
+
+ memset(&g_VBoxNetFltGlobals, 0, sizeof(g_VBoxNetFltGlobals));
+
+ Log(("VBoxNetFltLinuxUnload - done\n"));
+}
+
+
+/**
+ * We filter traffic from the host to the internal network
+ * before it reaches the NIC driver.
+ *
+ * The current code uses a very ugly hack overriding hard_start_xmit
+ * callback in the device structure, but it has been shown to give us a
+ * performance boost of 60-100% though. Eventually we have to find some
+ * less hacky way of getting this job done.
+ */
+#define VBOXNETFLT_WITH_HOST2WIRE_FILTER
+
+#ifdef VBOXNETFLT_WITH_HOST2WIRE_FILTER
+
+# if RTLNX_VER_MAX(2,6,29)
+
+typedef struct ethtool_ops OVR_OPSTYPE;
+# define OVR_OPS ethtool_ops
+# define OVR_XMIT pfnStartXmit
+
+# else /* RTLNX_VER_MIN(2,6,29) */
+
+typedef struct net_device_ops OVR_OPSTYPE;
+# define OVR_OPS netdev_ops
+# define OVR_XMIT pOrgOps->ndo_start_xmit
+
+# endif /* RTLNX_VER_MIN(2,6,29) */
+
+/**
+ * The overridden net_device_ops of the device we're attached to.
+ *
+ * As there is no net_device_ops structure in pre-2.6.29 kernels we override
+ * ethtool_ops instead along with hard_start_xmit callback in net_device
+ * structure.
+ *
+ * This is a very dirty hack that was created to explore how much we can improve
+ * the host to guest transfers by not CC'ing the NIC. It turns out to be
+ * the only way to filter outgoing packets for devices without TX queue.
+ */
+typedef struct VBoxNetDeviceOpsOverride
+{
+ /** Our overridden ops. */
+ OVR_OPSTYPE Ops;
+ /** Magic word. */
+ uint32_t u32Magic;
+ /** Pointer to the original ops. */
+ OVR_OPSTYPE const *pOrgOps;
+# if RTLNX_VER_MAX(2,6,29)
+ /** Pointer to the original hard_start_xmit function. */
+ int (*pfnStartXmit)(struct sk_buff *pSkb, struct net_device *pDev);
+# endif /* RTLNX_VER_MAX(2,6,29) */
+ /** Pointer to the net filter instance. */
+ PVBOXNETFLTINS pVBoxNetFlt;
+ /** The number of filtered packages. */
+ uint64_t cFiltered;
+ /** The total number of packets */
+ uint64_t cTotal;
+} VBOXNETDEVICEOPSOVERRIDE, *PVBOXNETDEVICEOPSOVERRIDE;
+/** VBOXNETDEVICEOPSOVERRIDE::u32Magic value. */
+#define VBOXNETDEVICEOPSOVERRIDE_MAGIC UINT32_C(0x00c0ffee)
+
+/**
+ * ndo_start_xmit wrapper that drops packets that shouldn't go to the wire
+ * because they belong on the internal network.
+ *
+ * @returns NETDEV_TX_XXX.
+ * @param pSkb The socket buffer to transmit.
+ * @param pDev The net device.
+ */
+static int vboxNetFltLinuxStartXmitFilter(struct sk_buff *pSkb, struct net_device *pDev)
+{
+ PVBOXNETDEVICEOPSOVERRIDE pOverride = (PVBOXNETDEVICEOPSOVERRIDE)pDev->OVR_OPS;
+ uint8_t abHdrBuf[sizeof(RTNETETHERHDR) + sizeof(uint32_t) + RTNETIPV4_MIN_LEN];
+ PCRTNETETHERHDR pEtherHdr;
+ PINTNETTRUNKSWPORT pSwitchPort;
+ uint32_t cbHdrs;
+
+
+ /*
+ * Validate the override structure.
+ *
+ * Note! We're racing vboxNetFltLinuxUnhookDev here. If this was supposed
+ * to be production quality code, we would have to be much more
+ * careful here and avoid the race.
+ */
+ if ( !RT_VALID_PTR(pOverride)
+ || pOverride->u32Magic != VBOXNETDEVICEOPSOVERRIDE_MAGIC
+# if RTLNX_VER_MIN(2,6,29)
+ || !RT_VALID_PTR(pOverride->pOrgOps)
+# endif
+ )
+ {
+ printk("vboxNetFltLinuxStartXmitFilter: bad override %p\n", pOverride);
+ dev_kfree_skb(pSkb);
+ return NETDEV_TX_OK;
+ }
+ pOverride->cTotal++;
+
+ /*
+ * Do the filtering base on the default OUI of our virtual NICs
+ *
+ * Note! In a real solution, we would ask the switch whether the
+ * destination MAC is 100% to be on the internal network and then
+ * drop it.
+ */
+ cbHdrs = skb_headlen(pSkb);
+ cbHdrs = RT_MIN(cbHdrs, sizeof(abHdrBuf));
+ pEtherHdr = (PCRTNETETHERHDR)skb_header_pointer(pSkb, 0, cbHdrs, &abHdrBuf[0]);
+ if ( pEtherHdr
+ && RT_VALID_PTR(pOverride->pVBoxNetFlt)
+ && (pSwitchPort = pOverride->pVBoxNetFlt->pSwitchPort) != NULL
+ && RT_VALID_PTR(pSwitchPort)
+ && cbHdrs >= 6)
+ {
+ INTNETSWDECISION enmDecision;
+
+ /** @todo consider reference counting, etc. */
+ enmDecision = pSwitchPort->pfnPreRecv(pSwitchPort, pEtherHdr, cbHdrs, INTNETTRUNKDIR_HOST);
+ if (enmDecision == INTNETSWDECISION_INTNET)
+ {
+ dev_kfree_skb(pSkb);
+ pOverride->cFiltered++;
+ return NETDEV_TX_OK;
+ }
+ }
+
+ return pOverride->OVR_XMIT(pSkb, pDev);
+}
+
+/**
+ * Hooks the device ndo_start_xmit operation of the device.
+ *
+ * @param pThis The net filter instance.
+ * @param pDev The net device.
+ */
+static void vboxNetFltLinuxHookDev(PVBOXNETFLTINS pThis, struct net_device *pDev)
+{
+ PVBOXNETDEVICEOPSOVERRIDE pOverride;
+
+ /* Cancel override if ethtool_ops is missing (host-only case, @bugref{5712}) */
+ if (!RT_VALID_PTR(pDev->OVR_OPS))
+ return;
+ pOverride = RTMemAlloc(sizeof(*pOverride));
+ if (!pOverride)
+ return;
+ pOverride->pOrgOps = pDev->OVR_OPS;
+ pOverride->Ops = *pDev->OVR_OPS;
+# if RTLNX_VER_MAX(2,6,29)
+ pOverride->pfnStartXmit = pDev->hard_start_xmit;
+# else /* RTLNX_VER_MIN(2,6,29) */
+ pOverride->Ops.ndo_start_xmit = vboxNetFltLinuxStartXmitFilter;
+# endif /* RTLNX_VER_MIN(2,6,29) */
+ pOverride->u32Magic = VBOXNETDEVICEOPSOVERRIDE_MAGIC;
+ pOverride->cTotal = 0;
+ pOverride->cFiltered = 0;
+ pOverride->pVBoxNetFlt = pThis;
+
+ RTSpinlockAcquire(pThis->hSpinlock); /* (this isn't necessary, but so what) */
+ ASMAtomicWritePtr((void * volatile *)&pDev->OVR_OPS, pOverride);
+# if RTLNX_VER_MAX(2,6,29)
+ ASMAtomicXchgPtr((void * volatile *)&pDev->hard_start_xmit, vboxNetFltLinuxStartXmitFilter);
+# endif /* RTLNX_VER_MAX(2,6,29) */
+ RTSpinlockRelease(pThis->hSpinlock);
+}
+
+/**
+ * Undos what vboxNetFltLinuxHookDev did.
+ *
+ * @param pThis The net filter instance.
+ * @param pDev The net device. Can be NULL, in which case
+ * we'll try retrieve it from @a pThis.
+ */
+static void vboxNetFltLinuxUnhookDev(PVBOXNETFLTINS pThis, struct net_device *pDev)
+{
+ PVBOXNETDEVICEOPSOVERRIDE pOverride;
+
+ RTSpinlockAcquire(pThis->hSpinlock);
+ if (!pDev)
+ pDev = ASMAtomicUoReadPtrT(&pThis->u.s.pDev, struct net_device *);
+ if (RT_VALID_PTR(pDev))
+ {
+ pOverride = (PVBOXNETDEVICEOPSOVERRIDE)pDev->OVR_OPS;
+ if ( RT_VALID_PTR(pOverride)
+ && pOverride->u32Magic == VBOXNETDEVICEOPSOVERRIDE_MAGIC
+ && RT_VALID_PTR(pOverride->pOrgOps)
+ )
+ {
+# if RTLNX_VER_MAX(2,6,29)
+ ASMAtomicWritePtr((void * volatile *)&pDev->hard_start_xmit, pOverride->pfnStartXmit);
+# endif /* RTLNX_VER_MAX(2,6,29) */
+ ASMAtomicWritePtr((void const * volatile *)&pDev->OVR_OPS, pOverride->pOrgOps);
+ ASMAtomicWriteU32(&pOverride->u32Magic, 0);
+ }
+ else
+ pOverride = NULL;
+ }
+ else
+ pOverride = NULL;
+ RTSpinlockRelease(pThis->hSpinlock);
+
+ if (pOverride)
+ {
+ printk("vboxnetflt: %llu out of %llu packets were not sent (directed to host)\n", pOverride->cFiltered, pOverride->cTotal);
+ RTMemFree(pOverride);
+ }
+}
+
+#endif /* VBOXNETFLT_WITH_HOST2WIRE_FILTER */
+
+
+/**
+ * Reads and retains the host interface handle.
+ *
+ * @returns The handle, NULL if detached.
+ * @param pThis
+ */
+DECLINLINE(struct net_device *) vboxNetFltLinuxRetainNetDev(PVBOXNETFLTINS pThis)
+{
+#if 0
+ struct net_device *pDev = NULL;
+
+ Log(("vboxNetFltLinuxRetainNetDev\n"));
+ /*
+ * Be careful here to avoid problems racing the detached callback.
+ */
+ RTSpinlockAcquire(pThis->hSpinlock);
+ if (!ASMAtomicUoReadBool(&pThis->fDisconnectedFromHost))
+ {
+ pDev = (struct net_device *)ASMAtomicUoReadPtr((void * volatile *)&pThis->u.s.pDev);
+ if (pDev)
+ {
+ dev_hold(pDev);
+ Log(("vboxNetFltLinuxRetainNetDev: Device %p(%s) retained. ref=%d\n",
+ pDev, pDev->name,
+#if RTLNX_VER_MIN(2,6,37)
+ netdev_refcnt_read(pDev)
+#else
+ atomic_read(&pDev->refcnt)
+#endif
+ ));
+ }
+ }
+ RTSpinlockRelease(pThis->hSpinlock);
+
+ Log(("vboxNetFltLinuxRetainNetDev - done\n"));
+ return pDev;
+#else
+ return ASMAtomicUoReadPtrT(&pThis->u.s.pDev, struct net_device *);
+#endif
+}
+
+
+/**
+ * Release the host interface handle previously retained
+ * by vboxNetFltLinuxRetainNetDev.
+ *
+ * @param pThis The instance.
+ * @param pDev The vboxNetFltLinuxRetainNetDev
+ * return value, NULL is fine.
+ */
+DECLINLINE(void) vboxNetFltLinuxReleaseNetDev(PVBOXNETFLTINS pThis, struct net_device *pDev)
+{
+#if 0
+ Log(("vboxNetFltLinuxReleaseNetDev\n"));
+ NOREF(pThis);
+ if (pDev)
+ {
+ dev_put(pDev);
+ Log(("vboxNetFltLinuxReleaseNetDev: Device %p(%s) released. ref=%d\n",
+ pDev, pDev->name,
+#if RTLNX_VER_MIN(2,6,37)
+ netdev_refcnt_read(pDev)
+#else
+ atomic_read(&pDev->refcnt)
+#endif
+ ));
+ }
+ Log(("vboxNetFltLinuxReleaseNetDev - done\n"));
+#endif
+}
+
+#define VBOXNETFLT_CB_TAG(skb) (0xA1C90000 | (skb->dev->ifindex & 0xFFFF))
+#define VBOXNETFLT_SKB_TAG(skb) (*(uint32_t*)&((skb)->cb[sizeof((skb)->cb)-sizeof(uint32_t)]))
+
+/**
+ * Checks whether this is an mbuf created by vboxNetFltLinuxMBufFromSG,
+ * i.e. a buffer which we're pushing and should be ignored by the filter callbacks.
+ *
+ * @returns true / false accordingly.
+ * @param pBuf The sk_buff.
+ */
+DECLINLINE(bool) vboxNetFltLinuxSkBufIsOur(struct sk_buff *pBuf)
+{
+ return VBOXNETFLT_SKB_TAG(pBuf) == VBOXNETFLT_CB_TAG(pBuf);
+}
+
+
+/**
+ * Checks whether this SG list contains a GSO packet.
+ *
+ * @returns true / false accordingly.
+ * @param pSG The (scatter/)gather list.
+ */
+DECLINLINE(bool) vboxNetFltLinuxIsGso(PINTNETSG pSG)
+{
+#if defined(VBOXNETFLT_WITH_GSO_XMIT_WIRE) || defined(VBOXNETFLT_WITH_GSO_XMIT_HOST)
+ return !((PDMNETWORKGSOTYPE)pSG->GsoCtx.u8Type == PDMNETWORKGSOTYPE_INVALID);
+#else /* !VBOXNETFLT_WITH_GSO_XMIT_WIRE && !VBOXNETFLT_WITH_GSO_XMIT_HOST */
+ return false;
+#endif /* !VBOXNETFLT_WITH_GSO_XMIT_WIRE && !VBOXNETFLT_WITH_GSO_XMIT_HOST */
+}
+
+
+/**
+ * Find out the frame size (of a single segment in case of GSO frames).
+ *
+ * @returns the frame size.
+ * @param pSG The (scatter/)gather list.
+ */
+DECLINLINE(uint32_t) vboxNetFltLinuxFrameSize(PINTNETSG pSG)
+{
+ uint16_t u16Type = 0;
+ uint32_t cbVlanTag = 0;
+ if (pSG->aSegs[0].cb >= sizeof(RTNETETHERHDR))
+ u16Type = RT_BE2H_U16(((PCRTNETETHERHDR)pSG->aSegs[0].pv)->EtherType);
+ else if (pSG->cbTotal >= sizeof(RTNETETHERHDR))
+ {
+ uint32_t off = RT_UOFFSETOF(RTNETETHERHDR, EtherType);
+ uint32_t i;
+ for (i = 0; i < pSG->cSegsUsed; ++i)
+ {
+ if (off <= pSG->aSegs[i].cb)
+ {
+ if (off + sizeof(uint16_t) <= pSG->aSegs[i].cb)
+ u16Type = RT_BE2H_U16(*(uint16_t *)((uintptr_t)pSG->aSegs[i].pv + off));
+ else if (i + 1 < pSG->cSegsUsed)
+ u16Type = RT_BE2H_U16( ((uint16_t)( ((uint8_t *)pSG->aSegs[i].pv)[off] ) << 8)
+ + *(uint8_t *)pSG->aSegs[i + 1].pv); /* ASSUMES no empty segments! */
+ /* else: frame is too short. */
+ break;
+ }
+ off -= pSG->aSegs[i].cb;
+ }
+ }
+ if (u16Type == RTNET_ETHERTYPE_VLAN)
+ cbVlanTag = 4;
+ return (vboxNetFltLinuxIsGso(pSG) ? (uint32_t)pSG->GsoCtx.cbMaxSeg + pSG->GsoCtx.cbHdrsTotal : pSG->cbTotal) - cbVlanTag;
+}
+
+
+/**
+ * Internal worker that create a linux sk_buff for a
+ * (scatter/)gather list.
+ *
+ * @returns Pointer to the sk_buff.
+ * @param pThis The instance.
+ * @param pSG The (scatter/)gather list.
+ * @param fDstWire Set if the destination is the wire.
+ */
+static struct sk_buff *vboxNetFltLinuxSkBufFromSG(PVBOXNETFLTINS pThis, PINTNETSG pSG, bool fDstWire)
+{
+ struct sk_buff *pPkt;
+ struct net_device *pDev;
+#if defined(VBOXNETFLT_WITH_GSO_XMIT_WIRE) || defined(VBOXNETFLT_WITH_GSO_XMIT_HOST)
+ unsigned fGsoType = 0;
+#endif
+
+ if (pSG->cbTotal == 0)
+ {
+ LogRel(("VBoxNetFlt: Dropped empty packet coming from internal network.\n"));
+ return NULL;
+ }
+ Log5(("VBoxNetFlt: Packet to %s of %d bytes (frame=%d).\n", fDstWire?"wire":"host", pSG->cbTotal, vboxNetFltLinuxFrameSize(pSG)));
+ if (fDstWire && (vboxNetFltLinuxFrameSize(pSG) > ASMAtomicReadU32(&pThis->u.s.cbMtu) + 14))
+ {
+ static bool s_fOnce = true;
+ if (s_fOnce)
+ {
+ s_fOnce = false;
+ printk("VBoxNetFlt: Dropped over-sized packet (%d bytes) coming from internal network.\n", vboxNetFltLinuxFrameSize(pSG));
+ }
+ return NULL;
+ }
+
+ /** @todo We should use fragments mapping the SG buffers with large packets.
+ * 256 bytes seems to be the a threshold used a lot for this. It
+ * requires some nasty work on the intnet side though... */
+ /*
+ * Allocate a packet and copy over the data.
+ */
+ pDev = ASMAtomicUoReadPtrT(&pThis->u.s.pDev, struct net_device *);
+ pPkt = dev_alloc_skb(pSG->cbTotal + NET_IP_ALIGN);
+ if (RT_UNLIKELY(!pPkt))
+ {
+ Log(("vboxNetFltLinuxSkBufFromSG: Failed to allocate sk_buff(%u).\n", pSG->cbTotal));
+ pSG->pvUserData = NULL;
+ return NULL;
+ }
+ pPkt->dev = pDev;
+ pPkt->ip_summed = CHECKSUM_NONE;
+
+ /* Align IP header on 16-byte boundary: 2 + 14 (ethernet hdr size). */
+ skb_reserve(pPkt, NET_IP_ALIGN);
+
+ /* Copy the segments. */
+ skb_put(pPkt, pSG->cbTotal);
+ IntNetSgRead(pSG, pPkt->data);
+
+#if defined(VBOXNETFLT_WITH_GSO_XMIT_WIRE) || defined(VBOXNETFLT_WITH_GSO_XMIT_HOST)
+ /*
+ * Setup GSO if used by this packet.
+ */
+ switch ((PDMNETWORKGSOTYPE)pSG->GsoCtx.u8Type)
+ {
+ default:
+ AssertMsgFailed(("%u (%s)\n", pSG->GsoCtx.u8Type, PDMNetGsoTypeName((PDMNETWORKGSOTYPE)pSG->GsoCtx.u8Type) ));
+ RT_FALL_THRU();
+ case PDMNETWORKGSOTYPE_INVALID:
+ fGsoType = 0;
+ break;
+ case PDMNETWORKGSOTYPE_IPV4_TCP:
+ fGsoType = SKB_GSO_TCPV4;
+ break;
+ case PDMNETWORKGSOTYPE_IPV6_TCP:
+ fGsoType = SKB_GSO_TCPV6;
+ break;
+ }
+ if (fGsoType)
+ {
+ struct skb_shared_info *pShInfo = skb_shinfo(pPkt);
+
+ pShInfo->gso_type = fGsoType | SKB_GSO_DODGY;
+ pShInfo->gso_size = pSG->GsoCtx.cbMaxSeg;
+ pShInfo->gso_segs = PDMNetGsoCalcSegmentCount(&pSG->GsoCtx, pSG->cbTotal);
+
+ /*
+ * We need to set checksum fields even if the packet goes to the host
+ * directly as it may be immediately forwarded by IP layer @bugref{5020}.
+ */
+ Assert(skb_headlen(pPkt) >= pSG->GsoCtx.cbHdrsTotal);
+ pPkt->ip_summed = CHECKSUM_PARTIAL;
+# if RTLNX_VER_MIN(2,6,22)
+ pPkt->csum_start = skb_headroom(pPkt) + pSG->GsoCtx.offHdr2;
+ if (fGsoType & (SKB_GSO_TCPV4 | SKB_GSO_TCPV6))
+ pPkt->csum_offset = RT_UOFFSETOF(RTNETTCP, th_sum);
+ else
+ pPkt->csum_offset = RT_UOFFSETOF(RTNETUDP, uh_sum);
+# else
+ pPkt->h.raw = pPkt->data + pSG->GsoCtx.offHdr2;
+ if (fGsoType & (SKB_GSO_TCPV4 | SKB_GSO_TCPV6))
+ pPkt->csum = RT_UOFFSETOF(RTNETTCP, th_sum);
+ else
+ pPkt->csum = RT_UOFFSETOF(RTNETUDP, uh_sum);
+# endif
+ if (!fDstWire)
+ PDMNetGsoPrepForDirectUse(&pSG->GsoCtx, pPkt->data, pSG->cbTotal, PDMNETCSUMTYPE_PSEUDO);
+ }
+#endif /* VBOXNETFLT_WITH_GSO_XMIT_WIRE || VBOXNETFLT_WITH_GSO_XMIT_HOST */
+
+ /*
+ * Finish up the socket buffer.
+ */
+ pPkt->protocol = eth_type_trans(pPkt, pDev);
+ if (fDstWire)
+ {
+ VBOX_SKB_RESET_NETWORK_HDR(pPkt);
+
+ /* Restore ethernet header back. */
+ skb_push(pPkt, ETH_HLEN); /** @todo VLAN: +4 if VLAN? */
+ VBOX_SKB_RESET_MAC_HDR(pPkt);
+ }
+ VBOXNETFLT_SKB_TAG(pPkt) = VBOXNETFLT_CB_TAG(pPkt);
+
+ return pPkt;
+}
+
+
+/**
+ * Return the offset where to start checksum computation from.
+ *
+ * @returns the offset relative to pBuf->data.
+ * @param pBuf The socket buffer.
+ */
+DECLINLINE(unsigned) vboxNetFltLinuxGetChecksumStartOffset(struct sk_buff *pBuf)
+{
+#if RTLNX_VER_MIN(2,6,38)
+ return skb_checksum_start_offset(pBuf);
+#elif RTLNX_VER_MIN(2,6,22)
+ return pBuf->csum_start - skb_headroom(pBuf);
+#else
+ unsigned char *pTransportHdr = pBuf->h.raw;
+# if RTLNX_VER_MAX(2,6,19)
+ /*
+ * Try to work around the problem with CentOS 4.7 and 5.2 (2.6.9
+ * and 2.6.18 kernels), they pass wrong 'h' pointer down. We take IP
+ * header length from the header itself and reconstruct 'h' pointer
+ * to TCP (or whatever) header.
+ */
+ if (pBuf->h.raw == pBuf->nh.raw && pBuf->protocol == htons(ETH_P_IP))
+ pTransportHdr = pBuf->nh.raw + pBuf->nh.iph->ihl * 4;
+# endif
+ return pTransportHdr - pBuf->data;
+#endif
+}
+
+
+/**
+ * Initializes a SG list from an sk_buff.
+ *
+ * @param pThis The instance.
+ * @param pBuf The sk_buff.
+ * @param pSG The SG.
+ * @param cbExtra The number of bytes of extra space allocated immediately after the SG.
+ * @param cSegs The number of segments allocated for the SG.
+ * This should match the number in the mbuf exactly!
+ * @param fSrc The source of the frame.
+ * @param pGsoCtx Pointer to the GSO context if it's a GSO
+ * internal network frame. NULL if regular frame.
+ */
+static void vboxNetFltLinuxSkBufToSG(PVBOXNETFLTINS pThis, struct sk_buff *pBuf, PINTNETSG pSG,
+ unsigned cbExtra, unsigned cSegs, uint32_t fSrc, PCPDMNETWORKGSO pGsoCtx)
+{
+ int i;
+ NOREF(pThis);
+
+#ifndef VBOXNETFLT_SG_SUPPORT
+ Assert(!skb_shinfo(pBuf)->frag_list);
+#else /* VBOXNETFLT_SG_SUPPORT */
+ uint8_t *pExtra = (uint8_t *)&pSG->aSegs[cSegs];
+ unsigned cbConsumed = 0;
+ unsigned cbProduced = 0;
+
+# if RTLNX_VER_MIN(2,6,27)
+ /* Restore VLAN tag stripped by host hardware */
+ if (vlan_tx_tag_present(pBuf))
+ {
+ uint8_t *pMac = pBuf->data;
+ struct vlan_ethhdr *pVHdr = (struct vlan_ethhdr *)pExtra;
+ Assert(ETH_ALEN * 2 + VLAN_HLEN <= cbExtra);
+ memmove(pVHdr, pMac, ETH_ALEN * 2);
+ /* Consume whole Ethernet header: 2 addresses + EtherType (see @bugref{8599}) */
+ cbConsumed += ETH_ALEN * 2 + sizeof(uint16_t);
+ pVHdr->h_vlan_proto = RT_H2N_U16(ETH_P_8021Q);
+ pVHdr->h_vlan_TCI = RT_H2N_U16(vlan_tx_tag_get(pBuf));
+ pVHdr->h_vlan_encapsulated_proto = *(uint16_t*)(pMac + ETH_ALEN * 2);
+ cbProduced += VLAN_ETH_HLEN;
+ }
+# endif /* RTLNX_VER_MIN(2,6,27) */
+
+ if (pBuf->ip_summed == CHECKSUM_PARTIAL && pBuf->pkt_type == PACKET_OUTGOING)
+ {
+ unsigned uCsumStartOffset = vboxNetFltLinuxGetChecksumStartOffset(pBuf);
+ unsigned uCsumStoreOffset = uCsumStartOffset + VBOX_SKB_CSUM_OFFSET(pBuf) - cbConsumed;
+ Log3(("cbConsumed=%u cbProduced=%u uCsumStartOffset=%u uCsumStoreOffset=%u\n",
+ cbConsumed, cbProduced, uCsumStartOffset, uCsumStoreOffset));
+ Assert(cbProduced + uCsumStoreOffset + sizeof(uint16_t) <= cbExtra);
+ /*
+ * We assume that the checksum is stored at the very end of the transport header
+ * so we will have all headers in a single fragment. If our assumption is wrong
+ * we may see suboptimal performance.
+ */
+ memmove(pExtra + cbProduced,
+ pBuf->data + cbConsumed,
+ uCsumStoreOffset);
+ unsigned uChecksum = skb_checksum(pBuf, uCsumStartOffset, pBuf->len - uCsumStartOffset, 0);
+ *(uint16_t*)(pExtra + cbProduced + uCsumStoreOffset) = csum_fold(uChecksum);
+ cbProduced += uCsumStoreOffset + sizeof(uint16_t);
+ cbConsumed += uCsumStoreOffset + sizeof(uint16_t);
+ }
+#endif /* VBOXNETFLT_SG_SUPPORT */
+
+ if (!pGsoCtx)
+ IntNetSgInitTempSegs(pSG, pBuf->len + cbProduced - cbConsumed, cSegs, 0 /*cSegsUsed*/);
+ else
+ IntNetSgInitTempSegsGso(pSG, pBuf->len + cbProduced - cbConsumed, cSegs, 0 /*cSegsUsed*/, pGsoCtx);
+
+ int iSeg = 0;
+#ifdef VBOXNETFLT_SG_SUPPORT
+ if (cbProduced)
+ {
+ pSG->aSegs[iSeg].cb = cbProduced;
+ pSG->aSegs[iSeg].pv = pExtra;
+ pSG->aSegs[iSeg++].Phys = NIL_RTHCPHYS;
+ }
+ pSG->aSegs[iSeg].cb = skb_headlen(pBuf) - cbConsumed;
+ pSG->aSegs[iSeg].pv = pBuf->data + cbConsumed;
+ pSG->aSegs[iSeg++].Phys = NIL_RTHCPHYS;
+ Assert(iSeg <= pSG->cSegsAlloc);
+
+# ifdef LOG_ENABLED
+ if (pBuf->data_len)
+ Log6((" kmap_atomic:"));
+# endif /* LOG_ENABLED */
+ for (i = 0; i < skb_shinfo(pBuf)->nr_frags; i++)
+ {
+ skb_frag_t *pFrag = &skb_shinfo(pBuf)->frags[i];
+# if RTLNX_VER_MIN(5,4,0) || RTLNX_SUSE_MAJ_PREREQ(15, 2)
+ pSG->aSegs[iSeg].cb = pFrag->bv_len;
+ pSG->aSegs[iSeg].pv = VBOX_SKB_KMAP_FRAG(pFrag) + pFrag->bv_offset;
+# else /* < KERNEL_VERSION(5, 4, 0) */
+ pSG->aSegs[iSeg].cb = pFrag->size;
+ pSG->aSegs[iSeg].pv = VBOX_SKB_KMAP_FRAG(pFrag) + pFrag->page_offset;
+# endif /* >= KERNEL_VERSION(5, 4, 0) */
+ Log6((" %p", pSG->aSegs[iSeg].pv));
+ pSG->aSegs[iSeg++].Phys = NIL_RTHCPHYS;
+ Assert(iSeg <= pSG->cSegsAlloc);
+ }
+ struct sk_buff *pFragBuf;
+ for (pFragBuf = skb_shinfo(pBuf)->frag_list; pFragBuf; pFragBuf = pFragBuf->next)
+ {
+ pSG->aSegs[iSeg].cb = skb_headlen(pFragBuf);
+ pSG->aSegs[iSeg].pv = pFragBuf->data;
+ pSG->aSegs[iSeg++].Phys = NIL_RTHCPHYS;
+ Assert(iSeg <= pSG->cSegsAlloc);
+ for (i = 0; i < skb_shinfo(pFragBuf)->nr_frags; i++)
+ {
+ skb_frag_t *pFrag = &skb_shinfo(pFragBuf)->frags[i];
+# if RTLNX_VER_MIN(5,4,0) || RTLNX_SUSE_MAJ_PREREQ(15, 2)
+ pSG->aSegs[iSeg].cb = pFrag->bv_len;
+ pSG->aSegs[iSeg].pv = VBOX_SKB_KMAP_FRAG(pFrag) + pFrag->bv_offset;
+# else /* < KERNEL_VERSION(5, 4, 0) */
+ pSG->aSegs[iSeg].cb = pFrag->size;
+ pSG->aSegs[iSeg].pv = VBOX_SKB_KMAP_FRAG(pFrag) + pFrag->page_offset;
+# endif /* >= KERNEL_VERSION(5, 4, 0) */
+ Log6((" %p", pSG->aSegs[iSeg].pv));
+ pSG->aSegs[iSeg++].Phys = NIL_RTHCPHYS;
+ Assert(iSeg <= pSG->cSegsAlloc);
+ }
+ }
+# ifdef LOG_ENABLED
+ if (pBuf->data_len)
+ Log6(("\n"));
+# endif /* LOG_ENABLED */
+#else
+ pSG->aSegs[iSeg].cb = pBuf->len;
+ pSG->aSegs[iSeg].pv = pBuf->data;
+ pSG->aSegs[iSeg++].Phys = NIL_RTHCPHYS;
+#endif
+
+ pSG->cSegsUsed = iSeg;
+
+#if 0
+ if (cbProduced)
+ {
+ LogRel(("vboxNetFltLinuxSkBufToSG: original packet dump:\n%.*Rhxd\n", pBuf->len-pBuf->data_len, skb_mac_header(pBuf)));
+ LogRel(("vboxNetFltLinuxSkBufToSG: cbConsumed=%u cbProduced=%u cbExtra=%u\n", cbConsumed, cbProduced, cbExtra));
+ uint32_t offset = 0;
+ for (i = 0; i < pSG->cSegsUsed; ++i)
+ {
+ LogRel(("vboxNetFltLinuxSkBufToSG: seg#%d (%d bytes, starting at 0x%x):\n%.*Rhxd\n",
+ i, pSG->aSegs[i].cb, offset, pSG->aSegs[i].cb, pSG->aSegs[i].pv));
+ offset += pSG->aSegs[i].cb;
+ }
+ }
+#endif
+
+#ifdef PADD_RUNT_FRAMES_FROM_HOST
+ /*
+ * Add a trailer if the frame is too small.
+ *
+ * Since we're getting to the packet before it is framed, it has not
+ * yet been padded. The current solution is to add a segment pointing
+ * to a buffer containing all zeros and pray that works for all frames...
+ */
+ if (pSG->cbTotal < 60 && (fSrc & INTNETTRUNKDIR_HOST))
+ {
+ Assert(pBuf->data_len == 0); /* Packets with fragments are never small! */
+ static uint8_t const s_abZero[128] = {0};
+
+ AssertReturnVoid(iSeg < cSegs);
+
+ pSG->aSegs[iSeg].Phys = NIL_RTHCPHYS;
+ pSG->aSegs[iSeg].pv = (void *)&s_abZero[0];
+ pSG->aSegs[iSeg++].cb = 60 - pSG->cbTotal;
+ pSG->cbTotal = 60;
+ pSG->cSegsUsed++;
+ Assert(iSeg <= pSG->cSegsAlloc)
+ }
+#endif
+
+ Log6(("vboxNetFltLinuxSkBufToSG: allocated=%d, segments=%d frags=%d next=%p frag_list=%p pkt_type=%x fSrc=%x\n",
+ pSG->cSegsAlloc, pSG->cSegsUsed, skb_shinfo(pBuf)->nr_frags, pBuf->next, skb_shinfo(pBuf)->frag_list, pBuf->pkt_type, fSrc));
+ for (i = 0; i < pSG->cSegsUsed; i++)
+ Log6(("vboxNetFltLinuxSkBufToSG: #%d: cb=%d pv=%p\n",
+ i, pSG->aSegs[i].cb, pSG->aSegs[i].pv));
+}
+
+/**
+ * Packet handler; not really documented - figure it out yourself.
+ *
+ * @returns 0 or EJUSTRETURN - this is probably copy & pastry and thus wrong.
+ */
+#if RTLNX_VER_MIN(2,6,14)
+static int vboxNetFltLinuxPacketHandler(struct sk_buff *pBuf,
+ struct net_device *pSkbDev,
+ struct packet_type *pPacketType,
+ struct net_device *pOrigDev)
+#else
+static int vboxNetFltLinuxPacketHandler(struct sk_buff *pBuf,
+ struct net_device *pSkbDev,
+ struct packet_type *pPacketType)
+#endif
+{
+ PVBOXNETFLTINS pThis;
+ struct net_device *pDev;
+ LogFlow(("vboxNetFltLinuxPacketHandler: pBuf=%p pSkbDev=%p pPacketType=%p\n",
+ pBuf, pSkbDev, pPacketType));
+#if RTLNX_VER_MIN(2,6,18)
+ Log3(("vboxNetFltLinuxPacketHandler: skb len=%u data_len=%u truesize=%u next=%p nr_frags=%u gso_size=%u gso_seqs=%u gso_type=%x frag_list=%p pkt_type=%x\n",
+ pBuf->len, pBuf->data_len, pBuf->truesize, pBuf->next, skb_shinfo(pBuf)->nr_frags, skb_shinfo(pBuf)->gso_size, skb_shinfo(pBuf)->gso_segs, skb_shinfo(pBuf)->gso_type, skb_shinfo(pBuf)->frag_list, pBuf->pkt_type));
+# if RTLNX_VER_MIN(2,6,22)
+ Log6(("vboxNetFltLinuxPacketHandler: packet dump follows:\n%.*Rhxd\n", pBuf->len-pBuf->data_len, skb_mac_header(pBuf)));
+# endif
+#else
+ Log3(("vboxNetFltLinuxPacketHandler: skb len=%u data_len=%u truesize=%u next=%p nr_frags=%u tso_size=%u tso_seqs=%u frag_list=%p pkt_type=%x\n",
+ pBuf->len, pBuf->data_len, pBuf->truesize, pBuf->next, skb_shinfo(pBuf)->nr_frags, skb_shinfo(pBuf)->tso_size, skb_shinfo(pBuf)->tso_segs, skb_shinfo(pBuf)->frag_list, pBuf->pkt_type));
+#endif
+ /*
+ * Drop it immediately?
+ */
+ if (!pBuf)
+ return 0;
+
+ if (pBuf->pkt_type == PACKET_LOOPBACK)
+ {
+ /*
+ * We are not interested in loopbacked packets as they will always have
+ * another copy going to the wire.
+ */
+ Log2(("vboxNetFltLinuxPacketHandler: dropped loopback packet (cb=%u)\n", pBuf->len));
+ dev_kfree_skb(pBuf); /* We must 'consume' all packets we get (@bugref{6539})! */
+ return 0;
+ }
+
+ pThis = VBOX_FLT_PT_TO_INST(pPacketType);
+ pDev = ASMAtomicUoReadPtrT(&pThis->u.s.pDev, struct net_device *);
+ if (pDev != pSkbDev)
+ {
+ Log(("vboxNetFltLinuxPacketHandler: Devices do not match, pThis may be wrong! pThis=%p\n", pThis));
+ kfree_skb(pBuf); /* This is a failure, so we use kfree_skb instead of dev_kfree_skb. */
+ return 0;
+ }
+
+ Log6(("vboxNetFltLinuxPacketHandler: pBuf->cb dump:\n%.*Rhxd\n", sizeof(pBuf->cb), pBuf->cb));
+ if (vboxNetFltLinuxSkBufIsOur(pBuf))
+ {
+ Log2(("vboxNetFltLinuxPacketHandler: got our own sk_buff, drop it.\n"));
+ dev_kfree_skb(pBuf);
+ return 0;
+ }
+
+#ifndef VBOXNETFLT_SG_SUPPORT
+ {
+ /*
+ * Get rid of fragmented packets, they cause too much trouble.
+ */
+ unsigned int uMacLen = pBuf->mac_len;
+ struct sk_buff *pCopy = skb_copy(pBuf, GFP_ATOMIC);
+ dev_kfree_skb(pBuf);
+ if (!pCopy)
+ {
+ LogRel(("VBoxNetFlt: Failed to allocate packet buffer, dropping the packet.\n"));
+ return 0;
+ }
+ pBuf = pCopy;
+ /* Somehow skb_copy ignores mac_len */
+ pBuf->mac_len = uMacLen;
+# if RTLNX_VER_MIN(2,6,27)
+ /* Restore VLAN tag stripped by host hardware */
+ if (vlan_tx_tag_present(pBuf) && skb_headroom(pBuf) >= VLAN_ETH_HLEN)
+ {
+ uint8_t *pMac = (uint8_t*)skb_mac_header(pBuf);
+ struct vlan_ethhdr *pVHdr = (struct vlan_ethhdr *)(pMac - VLAN_HLEN);
+ memmove(pVHdr, pMac, ETH_ALEN * 2);
+ pVHdr->h_vlan_proto = RT_H2N_U16(ETH_P_8021Q);
+ pVHdr->h_vlan_TCI = RT_H2N_U16(vlan_tx_tag_get(pBuf));
+ pBuf->mac_header -= VLAN_HLEN;
+ pBuf->mac_len += VLAN_HLEN;
+ }
+# endif /* RTLNX_VER_MIN(2,6,27) */
+
+# if RTLNX_VER_MIN(2,6,18)
+ Log3(("vboxNetFltLinuxPacketHandler: skb copy len=%u data_len=%u truesize=%u next=%p nr_frags=%u gso_size=%u gso_seqs=%u gso_type=%x frag_list=%p pkt_type=%x\n",
+ pBuf->len, pBuf->data_len, pBuf->truesize, pBuf->next, skb_shinfo(pBuf)->nr_frags, skb_shinfo(pBuf)->gso_size, skb_shinfo(pBuf)->gso_segs, skb_shinfo(pBuf)->gso_type, skb_shinfo(pBuf)->frag_list, pBuf->pkt_type));
+# if RTLNX_VER_MIN(2,6,22)
+ Log6(("vboxNetFltLinuxPacketHandler: packet dump follows:\n%.*Rhxd\n", pBuf->len-pBuf->data_len, skb_mac_header(pBuf)));
+# endif /* RTLNX_VER_MIN(2,6,22) */
+# else /* RTLNX_VER_MAX(2,6,18) */
+ Log3(("vboxNetFltLinuxPacketHandler: skb copy len=%u data_len=%u truesize=%u next=%p nr_frags=%u tso_size=%u tso_seqs=%u frag_list=%p pkt_type=%x\n",
+ pBuf->len, pBuf->data_len, pBuf->truesize, pBuf->next, skb_shinfo(pBuf)->nr_frags, skb_shinfo(pBuf)->tso_size, skb_shinfo(pBuf)->tso_segs, skb_shinfo(pBuf)->frag_list, pBuf->pkt_type));
+# endif /* RTLNX_VER_MAX(2,6,18) */
+ }
+#endif /* !VBOXNETFLT_SG_SUPPORT */
+
+#ifdef VBOXNETFLT_LINUX_NO_XMIT_QUEUE
+ /* Forward it to the internal network. */
+ vboxNetFltLinuxForwardToIntNet(pThis, pBuf);
+#else /* !VBOXNETFLT_LINUX_NO_XMIT_QUEUE */
+ /* Add the packet to transmit queue and schedule the bottom half. */
+ skb_queue_tail(&pThis->u.s.XmitQueue, pBuf);
+ schedule_work(&pThis->u.s.XmitTask);
+ Log6(("vboxNetFltLinuxPacketHandler: scheduled work %p for sk_buff %p\n",
+ &pThis->u.s.XmitTask, pBuf));
+#endif /* !VBOXNETFLT_LINUX_NO_XMIT_QUEUE */
+
+ /* It does not really matter what we return, it is ignored by the kernel. */
+ return 0;
+}
+
+/**
+ * Calculate the number of INTNETSEG segments the socket buffer will need.
+ *
+ * @returns Segment count.
+ * @param pBuf The socket buffer.
+ * @param pcbTemp Where to store the number of bytes of the part
+ * of the socket buffer that will be copied to
+ * a temporary storage.
+ */
+DECLINLINE(unsigned) vboxNetFltLinuxCalcSGSegments(struct sk_buff *pBuf, unsigned *pcbTemp)
+{
+ *pcbTemp = 0;
+#ifdef VBOXNETFLT_SG_SUPPORT
+ unsigned cSegs = 1 + skb_shinfo(pBuf)->nr_frags;
+ if (pBuf->ip_summed == CHECKSUM_PARTIAL && pBuf->pkt_type == PACKET_OUTGOING)
+ {
+ *pcbTemp = vboxNetFltLinuxGetChecksumStartOffset(pBuf) + VBOX_SKB_CSUM_OFFSET(pBuf) + sizeof(uint16_t);
+ }
+# if RTLNX_VER_MIN(2,6,27)
+ if (vlan_tx_tag_present(pBuf))
+ {
+ if (*pcbTemp)
+ *pcbTemp += VLAN_HLEN;
+ else
+ *pcbTemp = VLAN_ETH_HLEN;
+ }
+# endif /* RTLNX_VER_MIN(2,6,27) */
+ if (*pcbTemp)
+ ++cSegs;
+ struct sk_buff *pFrag;
+ for (pFrag = skb_shinfo(pBuf)->frag_list; pFrag; pFrag = pFrag->next)
+ {
+ Log6(("vboxNetFltLinuxCalcSGSegments: frag=%p len=%d data_len=%d frags=%d frag_list=%p next=%p\n",
+ pFrag, pFrag->len, pFrag->data_len, skb_shinfo(pFrag)->nr_frags, skb_shinfo(pFrag)->frag_list, pFrag->next));
+ cSegs += 1 + skb_shinfo(pFrag)->nr_frags;
+ }
+#else
+ unsigned cSegs = 1;
+#endif
+#ifdef PADD_RUNT_FRAMES_FROM_HOST
+ /* vboxNetFltLinuxSkBufToSG adds a padding segment if it's a runt. */
+ if (pBuf->len < 60)
+ cSegs++;
+#endif
+ return cSegs;
+}
+
+
+/**
+ * Destroy the intnet scatter / gather buffer created by
+ * vboxNetFltLinuxSkBufToSG.
+ *
+ * @param pSG The (scatter/)gather list.
+ * @param pBuf The original socket buffer that was used to create
+ * the scatter/gather list.
+ */
+static void vboxNetFltLinuxDestroySG(PINTNETSG pSG, struct sk_buff *pBuf)
+{
+#ifdef VBOXNETFLT_SG_SUPPORT
+ int i, iSeg = 1; /* Skip non-paged part of SKB */
+ /* Check if the extra buffer behind SG structure was used for modified packet header */
+ if (pBuf->data != pSG->aSegs[0].pv)
+ ++iSeg; /* Skip it as well */
+# ifdef LOG_ENABLED
+ if (pBuf->data_len)
+ Log6(("kunmap_atomic:"));
+# endif /* LOG_ENABLED */
+ /* iSeg now points to the first mapped fragment if there are any */
+ for (i = 0; i < skb_shinfo(pBuf)->nr_frags; i++)
+ {
+ Log6((" %p", pSG->aSegs[iSeg].pv));
+ VBOX_SKB_KUNMAP_FRAG(pSG->aSegs[iSeg++].pv);
+ }
+ struct sk_buff *pFragBuf;
+ for (pFragBuf = skb_shinfo(pBuf)->frag_list; pFragBuf; pFragBuf = pFragBuf->next)
+ {
+ ++iSeg; /* Non-fragment (unmapped) portion of chained SKB */
+ for (i = 0; i < skb_shinfo(pFragBuf)->nr_frags; i++)
+ {
+ Log6((" %p", pSG->aSegs[iSeg].pv));
+ VBOX_SKB_KUNMAP_FRAG(pSG->aSegs[iSeg++].pv);
+ }
+ }
+# ifdef LOG_ENABLED
+ if (pBuf->data_len)
+ Log6(("\n"));
+# endif /* LOG_ENABLED */
+#endif
+ NOREF(pSG);
+}
+
+#ifdef LOG_ENABLED
+/**
+ * Logging helper.
+ */
+static void vboxNetFltDumpPacket(PINTNETSG pSG, bool fEgress, const char *pszWhere, int iIncrement)
+{
+ int i, offSeg;
+ uint8_t *pInt, *pExt;
+ static int iPacketNo = 1;
+ iPacketNo += iIncrement;
+ if (fEgress)
+ {
+ pExt = pSG->aSegs[0].pv;
+ pInt = pExt + 6;
+ }
+ else
+ {
+ pInt = pSG->aSegs[0].pv;
+ pExt = pInt + 6;
+ }
+ Log(("VBoxNetFlt: (int)%02x:%02x:%02x:%02x:%02x:%02x"
+ " %s (%s)%02x:%02x:%02x:%02x:%02x:%02x (%u bytes) packet #%u\n",
+ pInt[0], pInt[1], pInt[2], pInt[3], pInt[4], pInt[5],
+ fEgress ? "-->" : "<--", pszWhere,
+ pExt[0], pExt[1], pExt[2], pExt[3], pExt[4], pExt[5],
+ pSG->cbTotal, iPacketNo));
+ if (pSG->cSegsUsed == 1)
+ {
+ Log4(("%.*Rhxd\n", pSG->aSegs[0].cb, pSG->aSegs[0].pv));
+ }
+ else
+ {
+ for (i = 0, offSeg = 0; i < pSG->cSegsUsed; i++)
+ {
+ Log4(("-- segment %d at 0x%x (%d bytes)\n --\n%.*Rhxd\n",
+ i, offSeg, pSG->aSegs[i].cb, pSG->aSegs[i].cb, pSG->aSegs[i].pv));
+ offSeg += pSG->aSegs[i].cb;
+ }
+ }
+}
+#else
+# define vboxNetFltDumpPacket(a, b, c, d) do {} while (0)
+#endif
+
+#ifdef VBOXNETFLT_WITH_GSO_RECV
+
+/**
+ * Worker for vboxNetFltLinuxForwardToIntNet that checks if we can forwards a
+ * GSO socket buffer without having to segment it.
+ *
+ * @returns true on success, false if needs segmenting.
+ * @param pThis The net filter instance.
+ * @param pSkb The GSO socket buffer.
+ * @param fSrc The source.
+ * @param pGsoCtx Where to return the GSO context on success.
+ */
+static bool vboxNetFltLinuxCanForwardAsGso(PVBOXNETFLTINS pThis, struct sk_buff *pSkb, uint32_t fSrc,
+ PPDMNETWORKGSO pGsoCtx)
+{
+ PDMNETWORKGSOTYPE enmGsoType;
+ uint16_t uEtherType;
+ unsigned int cbTransport;
+ unsigned int offTransport;
+ unsigned int cbTransportHdr;
+ unsigned uProtocol;
+ union
+ {
+ RTNETIPV4 IPv4;
+ RTNETIPV6 IPv6;
+ RTNETTCP Tcp;
+ uint8_t ab[40];
+ uint16_t au16[40/2];
+ uint32_t au32[40/4];
+ } Buf;
+
+ /*
+ * Check the GSO properties of the socket buffer and make sure it fits.
+ */
+ /** @todo Figure out how to handle SKB_GSO_TCP_ECN! */
+ if (RT_UNLIKELY( skb_shinfo(pSkb)->gso_type & ~(SKB_GSO_DODGY | SKB_GSO_TCPV6 | SKB_GSO_TCPV4) ))
+ {
+ Log5(("vboxNetFltLinuxCanForwardAsGso: gso_type=%#x\n", skb_shinfo(pSkb)->gso_type));
+ return false;
+ }
+ if (RT_UNLIKELY( skb_shinfo(pSkb)->gso_size < 1
+ || pSkb->len > VBOX_MAX_GSO_SIZE ))
+ {
+ Log5(("vboxNetFltLinuxCanForwardAsGso: gso_size=%#x skb_len=%#x (max=%#x)\n", skb_shinfo(pSkb)->gso_size, pSkb->len, VBOX_MAX_GSO_SIZE));
+ return false;
+ }
+
+ /*
+ * Switch on the ethertype.
+ */
+ uEtherType = pSkb->protocol;
+ if ( uEtherType == RT_H2N_U16_C(RTNET_ETHERTYPE_VLAN)
+ && pSkb->mac_len == sizeof(RTNETETHERHDR) + sizeof(uint32_t))
+ {
+ uint16_t const *puEtherType = skb_header_pointer(pSkb, sizeof(RTNETETHERHDR) + sizeof(uint16_t), sizeof(uint16_t), &Buf);
+ if (puEtherType)
+ uEtherType = *puEtherType;
+ }
+ switch (uEtherType)
+ {
+ case RT_H2N_U16_C(RTNET_ETHERTYPE_IPV4):
+ {
+ unsigned int cbHdr;
+ PCRTNETIPV4 pIPv4 = (PCRTNETIPV4)skb_header_pointer(pSkb, pSkb->mac_len, sizeof(Buf.IPv4), &Buf);
+ if (RT_UNLIKELY(!pIPv4))
+ {
+ Log5(("vboxNetFltLinuxCanForwardAsGso: failed to access IPv4 hdr\n"));
+ return false;
+ }
+
+ cbHdr = pIPv4->ip_hl * 4;
+ cbTransport = RT_N2H_U16(pIPv4->ip_len);
+ if (RT_UNLIKELY( cbHdr < RTNETIPV4_MIN_LEN
+ || cbHdr > cbTransport ))
+ {
+ Log5(("vboxNetFltLinuxCanForwardAsGso: invalid IPv4 lengths: ip_hl=%u ip_len=%u\n", pIPv4->ip_hl, RT_N2H_U16(pIPv4->ip_len)));
+ return false;
+ }
+ cbTransport -= cbHdr;
+ offTransport = pSkb->mac_len + cbHdr;
+ uProtocol = pIPv4->ip_p;
+ if (uProtocol == RTNETIPV4_PROT_TCP)
+ enmGsoType = PDMNETWORKGSOTYPE_IPV4_TCP;
+ else if (uProtocol == RTNETIPV4_PROT_UDP)
+ enmGsoType = PDMNETWORKGSOTYPE_IPV4_UDP;
+ else /** @todo IPv6: 4to6 tunneling */
+ enmGsoType = PDMNETWORKGSOTYPE_INVALID;
+ break;
+ }
+
+ case RT_H2N_U16_C(RTNET_ETHERTYPE_IPV6):
+ {
+ PCRTNETIPV6 pIPv6 = (PCRTNETIPV6)skb_header_pointer(pSkb, pSkb->mac_len, sizeof(Buf.IPv6), &Buf);
+ if (RT_UNLIKELY(!pIPv6))
+ {
+ Log5(("vboxNetFltLinuxCanForwardAsGso: failed to access IPv6 hdr\n"));
+ return false;
+ }
+
+ cbTransport = RT_N2H_U16(pIPv6->ip6_plen);
+ offTransport = pSkb->mac_len + sizeof(RTNETIPV6);
+ uProtocol = pIPv6->ip6_nxt;
+ /** @todo IPv6: Dig our way out of the other headers. */
+ if (uProtocol == RTNETIPV4_PROT_TCP)
+ enmGsoType = PDMNETWORKGSOTYPE_IPV6_TCP;
+ else if (uProtocol == RTNETIPV4_PROT_UDP)
+ enmGsoType = PDMNETWORKGSOTYPE_IPV6_UDP;
+ else
+ enmGsoType = PDMNETWORKGSOTYPE_INVALID;
+ break;
+ }
+
+ default:
+ Log5(("vboxNetFltLinuxCanForwardAsGso: uEtherType=%#x\n", RT_H2N_U16(uEtherType)));
+ return false;
+ }
+
+ if (enmGsoType == PDMNETWORKGSOTYPE_INVALID)
+ {
+ Log5(("vboxNetFltLinuxCanForwardAsGso: Unsupported protocol %d\n", uProtocol));
+ return false;
+ }
+
+ if (RT_UNLIKELY( offTransport + cbTransport <= offTransport
+ || offTransport + cbTransport > pSkb->len
+ || cbTransport < (uProtocol == RTNETIPV4_PROT_TCP ? RTNETTCP_MIN_LEN : RTNETUDP_MIN_LEN)) )
+ {
+ Log5(("vboxNetFltLinuxCanForwardAsGso: Bad transport length; off=%#x + cb=%#x => %#x; skb_len=%#x (%s)\n",
+ offTransport, cbTransport, offTransport + cbTransport, pSkb->len, PDMNetGsoTypeName(enmGsoType) ));
+ return false;
+ }
+
+ /*
+ * Check the TCP/UDP bits.
+ */
+ if (uProtocol == RTNETIPV4_PROT_TCP)
+ {
+ PCRTNETTCP pTcp = (PCRTNETTCP)skb_header_pointer(pSkb, offTransport, sizeof(Buf.Tcp), &Buf);
+ if (RT_UNLIKELY(!pTcp))
+ {
+ Log5(("vboxNetFltLinuxCanForwardAsGso: failed to access TCP hdr\n"));
+ return false;
+ }
+
+ cbTransportHdr = pTcp->th_off * 4;
+ pGsoCtx->cbHdrsSeg = offTransport + cbTransportHdr;
+ if (RT_UNLIKELY( cbTransportHdr < RTNETTCP_MIN_LEN
+ || cbTransportHdr > cbTransport
+ || offTransport + cbTransportHdr >= UINT8_MAX
+ || offTransport + cbTransportHdr >= pSkb->len ))
+ {
+ Log5(("vboxNetFltLinuxCanForwardAsGso: No space for TCP header; off=%#x cb=%#x skb_len=%#x\n", offTransport, cbTransportHdr, pSkb->len));
+ return false;
+ }
+
+ }
+ else
+ {
+ Assert(uProtocol == RTNETIPV4_PROT_UDP);
+ cbTransportHdr = sizeof(RTNETUDP);
+ pGsoCtx->cbHdrsSeg = offTransport; /* Exclude UDP header */
+ if (RT_UNLIKELY( offTransport + cbTransportHdr >= UINT8_MAX
+ || offTransport + cbTransportHdr >= pSkb->len ))
+ {
+ Log5(("vboxNetFltLinuxCanForwardAsGso: No space for UDP header; off=%#x skb_len=%#x\n", offTransport, pSkb->len));
+ return false;
+ }
+ }
+
+ /*
+ * We're good, init the GSO context.
+ */
+ pGsoCtx->u8Type = enmGsoType;
+ pGsoCtx->cbHdrsTotal = offTransport + cbTransportHdr;
+ pGsoCtx->cbMaxSeg = skb_shinfo(pSkb)->gso_size;
+ pGsoCtx->offHdr1 = pSkb->mac_len;
+ pGsoCtx->offHdr2 = offTransport;
+ pGsoCtx->u8Unused = 0;
+
+ return true;
+}
+
+/**
+ * Forward the socket buffer as a GSO internal network frame.
+ *
+ * @returns IPRT status code.
+ * @param pThis The net filter instance.
+ * @param pSkb The GSO socket buffer.
+ * @param fSrc The source.
+ * @param pGsoCtx Where to return the GSO context on success.
+ */
+static int vboxNetFltLinuxForwardAsGso(PVBOXNETFLTINS pThis, struct sk_buff *pSkb, uint32_t fSrc, PCPDMNETWORKGSO pGsoCtx)
+{
+ int rc;
+ unsigned cbExtra;
+ unsigned cSegs = vboxNetFltLinuxCalcSGSegments(pSkb, &cbExtra);
+ PINTNETSG pSG = (PINTNETSG)alloca(RT_UOFFSETOF_DYN(INTNETSG, aSegs[cSegs]) + cbExtra);
+ if (RT_LIKELY(pSG))
+ {
+ vboxNetFltLinuxSkBufToSG(pThis, pSkb, pSG, cbExtra, cSegs, fSrc, pGsoCtx);
+
+ vboxNetFltDumpPacket(pSG, false, (fSrc & INTNETTRUNKDIR_HOST) ? "host" : "wire", 1);
+ pThis->pSwitchPort->pfnRecv(pThis->pSwitchPort, NULL /* pvIf */, pSG, fSrc);
+
+ vboxNetFltLinuxDestroySG(pSG, pSkb);
+ rc = VINF_SUCCESS;
+ }
+ else
+ {
+ Log(("VBoxNetFlt: Dropping the sk_buff (failure case).\n"));
+ rc = VERR_NO_MEMORY;
+ }
+ return rc;
+}
+
+#endif /* VBOXNETFLT_WITH_GSO_RECV */
+
+/**
+ * Worker for vboxNetFltLinuxForwardToIntNet.
+ *
+ * @returns VINF_SUCCESS or VERR_NO_MEMORY.
+ * @param pThis The net filter instance.
+ * @param pBuf The socket buffer.
+ * @param fSrc The source.
+ */
+static int vboxNetFltLinuxForwardSegment(PVBOXNETFLTINS pThis, struct sk_buff *pBuf, uint32_t fSrc)
+{
+ int rc;
+ unsigned cbExtra;
+ unsigned cSegs = vboxNetFltLinuxCalcSGSegments(pBuf, &cbExtra);
+ PINTNETSG pSG = (PINTNETSG)alloca(RT_UOFFSETOF_DYN(INTNETSG, aSegs[cSegs]) + cbExtra);
+ if (RT_LIKELY(pSG))
+ {
+ vboxNetFltLinuxSkBufToSG(pThis, pBuf, pSG, cbExtra, cSegs, fSrc, NULL /*pGsoCtx*/);
+
+ vboxNetFltDumpPacket(pSG, false, (fSrc & INTNETTRUNKDIR_HOST) ? "host" : "wire", 1);
+ pThis->pSwitchPort->pfnRecv(pThis->pSwitchPort, NULL /* pvIf */, pSG, fSrc);
+
+ vboxNetFltLinuxDestroySG(pSG, pBuf);
+ rc = VINF_SUCCESS;
+ }
+ else
+ {
+ Log(("VBoxNetFlt: Failed to allocate SG buffer.\n"));
+ rc = VERR_NO_MEMORY;
+ }
+ return rc;
+}
+
+
+/**
+ * I won't disclose what I do, figure it out yourself, including pThis referencing.
+ *
+ * @param pThis The net filter instance.
+ * @param pBuf The socket buffer.
+ * @param fSrc Where the packet comes from.
+ */
+static void vboxNetFltLinuxForwardToIntNetInner(PVBOXNETFLTINS pThis, struct sk_buff *pBuf, uint32_t fSrc)
+{
+#ifdef VBOXNETFLT_WITH_GSO
+ if (skb_is_gso(pBuf))
+ {
+ PDMNETWORKGSO GsoCtx;
+ Log6(("vboxNetFltLinuxForwardToIntNetInner: skb len=%u data_len=%u truesize=%u next=%p"
+ " nr_frags=%u gso_size=%u gso_seqs=%u gso_type=%x frag_list=%p pkt_type=%x ip_summed=%d\n",
+ pBuf->len, pBuf->data_len, pBuf->truesize, pBuf->next,
+ skb_shinfo(pBuf)->nr_frags, skb_shinfo(pBuf)->gso_size,
+ skb_shinfo(pBuf)->gso_segs, skb_shinfo(pBuf)->gso_type,
+ skb_shinfo(pBuf)->frag_list, pBuf->pkt_type, pBuf->ip_summed));
+
+ if (RT_LIKELY(fSrc & INTNETTRUNKDIR_HOST))
+ {
+ /*
+ * skb_gso_segment does the following. Do we need to do it as well?
+ */
+# if RTLNX_VER_MIN(2,6,22)
+ skb_reset_mac_header(pBuf);
+ pBuf->mac_len = pBuf->network_header - pBuf->mac_header;
+# else
+ pBuf->mac.raw = pBuf->data;
+ pBuf->mac_len = pBuf->nh.raw - pBuf->data;
+# endif
+ }
+
+# ifdef VBOXNETFLT_WITH_GSO_RECV
+ if ( (skb_shinfo(pBuf)->gso_type & (SKB_GSO_TCPV6 | SKB_GSO_TCPV4))
+ && vboxNetFltLinuxCanForwardAsGso(pThis, pBuf, fSrc, &GsoCtx) )
+ vboxNetFltLinuxForwardAsGso(pThis, pBuf, fSrc, &GsoCtx);
+ else
+# endif /* VBOXNETFLT_WITH_GSO_RECV */
+ {
+ /* Need to segment the packet */
+ struct sk_buff *pNext;
+ struct sk_buff *pSegment = skb_gso_segment(pBuf, 0 /*supported features*/);
+ if (IS_ERR(pSegment))
+ {
+ LogRel(("VBoxNetFlt: Failed to segment a packet (%d).\n", PTR_ERR(pSegment)));
+ return;
+ }
+
+ for (; pSegment; pSegment = pNext)
+ {
+ Log6(("vboxNetFltLinuxForwardToIntNetInner: segment len=%u data_len=%u truesize=%u next=%p"
+ " nr_frags=%u gso_size=%u gso_seqs=%u gso_type=%x frag_list=%p pkt_type=%x\n",
+ pSegment->len, pSegment->data_len, pSegment->truesize, pSegment->next,
+ skb_shinfo(pSegment)->nr_frags, skb_shinfo(pSegment)->gso_size,
+ skb_shinfo(pSegment)->gso_segs, skb_shinfo(pSegment)->gso_type,
+ skb_shinfo(pSegment)->frag_list, pSegment->pkt_type));
+ pNext = pSegment->next;
+ pSegment->next = 0;
+ vboxNetFltLinuxForwardSegment(pThis, pSegment, fSrc);
+ dev_kfree_skb(pSegment);
+ }
+ }
+ }
+ else
+#endif /* VBOXNETFLT_WITH_GSO */
+ {
+ Log6(("vboxNetFltLinuxForwardToIntNetInner: ptk_type=%d ip_summed=%d len=%d"
+ " data_len=%d headroom=%d hdr_len=%d csum_offset=%d\n",
+ pBuf->pkt_type, pBuf->ip_summed, pBuf->len, pBuf->data_len, skb_headroom(pBuf),
+ skb_headlen(pBuf), vboxNetFltLinuxGetChecksumStartOffset(pBuf)));
+#ifndef VBOXNETFLT_SG_SUPPORT
+ if (pBuf->ip_summed == CHECKSUM_PARTIAL && pBuf->pkt_type == PACKET_OUTGOING)
+ {
+# if RTLNX_VER_MIN(2,6,19)
+ int rc = VBOX_SKB_CHECKSUM_HELP(pBuf);
+# else
+ /*
+ * Try to work around the problem with CentOS 4.7 and 5.2 (2.6.9
+ * and 2.6.18 kernels), they pass wrong 'h' pointer down. We take IP
+ * header length from the header itself and reconstruct 'h' pointer
+ * to TCP (or whatever) header.
+ */
+ unsigned char *tmp = pBuf->h.raw;
+ if (pBuf->h.raw == pBuf->nh.raw && pBuf->protocol == htons(ETH_P_IP))
+ pBuf->h.raw = pBuf->nh.raw + pBuf->nh.iph->ihl * 4;
+ int rc = VBOX_SKB_CHECKSUM_HELP(pBuf);
+ /* Restore the original (wrong) pointer. */
+ pBuf->h.raw = tmp;
+# endif
+ if (rc)
+ {
+ LogRel(("VBoxNetFlt: Failed to compute checksum, dropping the packet.\n"));
+ return;
+ }
+ }
+#endif /* !VBOXNETFLT_SG_SUPPORT */
+ vboxNetFltLinuxForwardSegment(pThis, pBuf, fSrc);
+ }
+}
+
+
+/**
+ * Temporarily adjust pBuf->data so it always points to the Ethernet header,
+ * then forward it to the internal network.
+ *
+ * @param pThis The net filter instance.
+ * @param pBuf The socket buffer. This is consumed by this function.
+ */
+static void vboxNetFltLinuxForwardToIntNet(PVBOXNETFLTINS pThis, struct sk_buff *pBuf)
+{
+ uint32_t fSrc = pBuf->pkt_type == PACKET_OUTGOING ? INTNETTRUNKDIR_HOST : INTNETTRUNKDIR_WIRE;
+
+ if (RT_UNLIKELY(fSrc & INTNETTRUNKDIR_WIRE))
+ {
+ /*
+ * The packet came from the wire and the driver has already consumed
+ * mac header. We need to restore it back. Moreover, after we are
+ * through with this skb we need to restore its original state!
+ */
+ skb_push(pBuf, pBuf->mac_len);
+ Log5(("vboxNetFltLinuxForwardToIntNet: mac_len=%d data=%p mac_header=%p network_header=%p\n",
+ pBuf->mac_len, pBuf->data, skb_mac_header(pBuf), skb_network_header(pBuf)));
+ }
+
+ vboxNetFltLinuxForwardToIntNetInner(pThis, pBuf, fSrc);
+
+ /*
+ * Restore the original state of skb as there are other handlers this skb
+ * will be provided to.
+ */
+ if (RT_UNLIKELY(fSrc & INTNETTRUNKDIR_WIRE))
+ skb_pull(pBuf, pBuf->mac_len);
+
+ dev_kfree_skb(pBuf);
+}
+
+
+#ifndef VBOXNETFLT_LINUX_NO_XMIT_QUEUE
+/**
+ * Work queue handler that forwards the socket buffers queued by
+ * vboxNetFltLinuxPacketHandler to the internal network.
+ *
+ * @param pWork The work queue.
+ */
+# if RTLNX_VER_MIN(2,6,20)
+static void vboxNetFltLinuxXmitTask(struct work_struct *pWork)
+# else
+static void vboxNetFltLinuxXmitTask(void *pWork)
+# endif
+{
+ PVBOXNETFLTINS pThis = VBOX_FLT_XT_TO_INST(pWork);
+ struct sk_buff *pBuf;
+
+ Log6(("vboxNetFltLinuxXmitTask: Got work %p.\n", pWork));
+
+ /*
+ * Active? Retain the instance and increment the busy counter.
+ */
+ if (vboxNetFltTryRetainBusyActive(pThis))
+ {
+ while ((pBuf = skb_dequeue(&pThis->u.s.XmitQueue)) != NULL)
+ vboxNetFltLinuxForwardToIntNet(pThis, pBuf);
+
+ vboxNetFltRelease(pThis, true /* fBusy */);
+ }
+ else
+ {
+ /** @todo Shouldn't we just drop the packets here? There is little point in
+ * making them accumulate when the VM is paused and it'll only waste
+ * kernel memory anyway... Hmm. maybe wait a short while (2-5 secs)
+ * before start draining the packets (goes for the intnet ring buf
+ * too)? */
+ }
+}
+#endif /* !VBOXNETFLT_LINUX_NO_XMIT_QUEUE */
+
+/**
+ * Reports the GSO capabilities of the hardware NIC.
+ *
+ * @param pThis The net filter instance. The caller hold a
+ * reference to this.
+ */
+static void vboxNetFltLinuxReportNicGsoCapabilities(PVBOXNETFLTINS pThis)
+{
+#if defined(VBOXNETFLT_WITH_GSO_XMIT_WIRE) || defined(VBOXNETFLT_WITH_GSO_XMIT_HOST)
+ if (vboxNetFltTryRetainBusyNotDisconnected(pThis))
+ {
+ struct net_device *pDev;
+ unsigned int fFeatures;
+
+ RTSpinlockAcquire(pThis->hSpinlock);
+
+ pDev = ASMAtomicUoReadPtrT(&pThis->u.s.pDev, struct net_device *);
+ if (pDev)
+ fFeatures = pDev->features;
+ else
+ fFeatures = 0;
+
+ RTSpinlockRelease(pThis->hSpinlock);
+
+ if (pThis->pSwitchPort)
+ {
+ /* Set/update the GSO capabilities of the NIC. */
+ uint32_t fGsoCapabilites = 0;
+ if (fFeatures & NETIF_F_TSO)
+ fGsoCapabilites |= RT_BIT_32(PDMNETWORKGSOTYPE_IPV4_TCP);
+ if (fFeatures & NETIF_F_TSO6)
+ fGsoCapabilites |= RT_BIT_32(PDMNETWORKGSOTYPE_IPV6_TCP);
+ Log3(("vboxNetFltLinuxReportNicGsoCapabilities: reporting wire %s%s\n",
+ (fGsoCapabilites & RT_BIT_32(PDMNETWORKGSOTYPE_IPV4_TCP)) ? "tso " : "",
+ (fGsoCapabilites & RT_BIT_32(PDMNETWORKGSOTYPE_IPV6_TCP)) ? "tso6 " : ""));
+ pThis->pSwitchPort->pfnReportGsoCapabilities(pThis->pSwitchPort, fGsoCapabilites, INTNETTRUNKDIR_WIRE);
+ }
+
+ vboxNetFltRelease(pThis, true /*fBusy*/);
+ }
+#endif /* VBOXNETFLT_WITH_GSO_XMIT_WIRE || VBOXNETFLT_WITH_GSO_XMIT_HOST */
+}
+
+/**
+ * Helper that determines whether the host (ignoreing us) is operating the
+ * interface in promiscuous mode or not.
+ */
+static bool vboxNetFltLinuxPromiscuous(PVBOXNETFLTINS pThis)
+{
+ bool fRc = false;
+ struct net_device * pDev = vboxNetFltLinuxRetainNetDev(pThis);
+ if (pDev)
+ {
+ fRc = !!(pDev->promiscuity - (ASMAtomicUoReadBool(&pThis->u.s.fPromiscuousSet) & 1));
+ LogFlow(("vboxNetFltPortOsIsPromiscuous: returns %d, pDev->promiscuity=%d, fPromiscuousSet=%d\n",
+ fRc, pDev->promiscuity, pThis->u.s.fPromiscuousSet));
+ vboxNetFltLinuxReleaseNetDev(pThis, pDev);
+ }
+ return fRc;
+}
+
+/**
+ * Does this device needs link state change signaled?
+ * Currently we need it for our own VBoxNetAdp and TAP.
+ */
+static bool vboxNetFltNeedsLinkState(PVBOXNETFLTINS pThis, struct net_device *pDev)
+{
+ if (pDev->ethtool_ops && pDev->ethtool_ops->get_drvinfo)
+ {
+ struct ethtool_drvinfo Info;
+
+ memset(&Info, 0, sizeof(Info));
+ Info.cmd = ETHTOOL_GDRVINFO;
+ pDev->ethtool_ops->get_drvinfo(pDev, &Info);
+ Log3(("%s: driver=%.*s version=%.*s bus_info=%.*s\n",
+ __FUNCTION__,
+ sizeof(Info.driver), Info.driver,
+ sizeof(Info.version), Info.version,
+ sizeof(Info.bus_info), Info.bus_info));
+
+ if (!strncmp(Info.driver, "vboxnet", sizeof(Info.driver)))
+ return true;
+
+#if RTLNX_VER_MIN(2,6,36) /* TAP started doing carrier */
+ return !strncmp(Info.driver, "tun", 4)
+ && !strncmp(Info.bus_info, "tap", 4);
+#endif
+ }
+
+ return false;
+}
+
+#if RTLNX_VER_MAX(2,6,18)
+DECLINLINE(void) netif_tx_lock_bh(struct net_device *pDev)
+{
+ spin_lock_bh(&pDev->xmit_lock);
+}
+
+DECLINLINE(void) netif_tx_unlock_bh(struct net_device *pDev)
+{
+ spin_unlock_bh(&pDev->xmit_lock);
+}
+#endif
+
+/**
+ * Some devices need link state change when filter attaches/detaches
+ * since the filter is their link in a sense.
+ */
+static void vboxNetFltSetLinkState(PVBOXNETFLTINS pThis, struct net_device *pDev, bool fLinkUp)
+{
+ if (vboxNetFltNeedsLinkState(pThis, pDev))
+ {
+ Log3(("%s: bringing device link %s\n",
+ __FUNCTION__, fLinkUp ? "up" : "down"));
+ netif_tx_lock_bh(pDev);
+ if (fLinkUp)
+ netif_carrier_on(pDev);
+ else
+ netif_carrier_off(pDev);
+ netif_tx_unlock_bh(pDev);
+ }
+}
+
+/**
+ * Internal worker for vboxNetFltLinuxNotifierCallback.
+ *
+ * @returns VBox status code.
+ * @param pThis The instance.
+ * @param pDev The device to attach to.
+ */
+static int vboxNetFltLinuxAttachToInterface(PVBOXNETFLTINS pThis, struct net_device *pDev)
+{
+ LogFlow(("vboxNetFltLinuxAttachToInterface: pThis=%p (%s)\n", pThis, pThis->szName));
+
+ /*
+ * Retain and store the device.
+ */
+ dev_hold(pDev);
+
+ RTSpinlockAcquire(pThis->hSpinlock);
+ ASMAtomicUoWritePtr(&pThis->u.s.pDev, pDev);
+ RTSpinlockRelease(pThis->hSpinlock);
+
+ Log(("vboxNetFltLinuxAttachToInterface: Device %p(%s) retained. ref=%d\n",
+ pDev, pDev->name,
+#if RTLNX_VER_MIN(2,6,37)
+ netdev_refcnt_read(pDev)
+#else
+ atomic_read(&pDev->refcnt)
+#endif
+ ));
+ Log(("vboxNetFltLinuxAttachToInterface: Got pDev=%p pThis=%p pThis->u.s.pDev=%p\n",
+ pDev, pThis, ASMAtomicUoReadPtrT(&pThis->u.s.pDev, struct net_device *)));
+
+ /* Get the mac address while we still have a valid net_device reference. */
+ memcpy(&pThis->u.s.MacAddr, pDev->dev_addr, sizeof(pThis->u.s.MacAddr));
+ /* Initialize MTU */
+ pThis->u.s.cbMtu = pDev->mtu;
+
+ /*
+ * Install a packet filter for this device with a protocol wildcard (ETH_P_ALL).
+ */
+ pThis->u.s.PacketType.type = __constant_htons(ETH_P_ALL);
+ pThis->u.s.PacketType.dev = pDev;
+ pThis->u.s.PacketType.func = vboxNetFltLinuxPacketHandler;
+ dev_add_pack(&pThis->u.s.PacketType);
+ ASMAtomicUoWriteBool(&pThis->u.s.fPacketHandler, true);
+ Log(("vboxNetFltLinuxAttachToInterface: this=%p: Packet handler installed.\n", pThis));
+
+#ifdef VBOXNETFLT_WITH_HOST2WIRE_FILTER
+ vboxNetFltLinuxHookDev(pThis, pDev);
+#endif
+
+ /*
+ * Are we the "carrier" for this device (e.g. vboxnet or tap)?
+ */
+ vboxNetFltSetLinkState(pThis, pDev, true);
+
+ /*
+ * Set indicators that require the spinlock. Be abit paranoid about racing
+ * the device notification handle.
+ */
+ RTSpinlockAcquire(pThis->hSpinlock);
+ pDev = ASMAtomicUoReadPtrT(&pThis->u.s.pDev, struct net_device *);
+ if (pDev)
+ {
+ ASMAtomicUoWriteBool(&pThis->fDisconnectedFromHost, false);
+ ASMAtomicUoWriteBool(&pThis->u.s.fRegistered, true);
+ pDev = NULL; /* don't dereference it */
+ }
+ RTSpinlockRelease(pThis->hSpinlock);
+
+ /*
+ * Report GSO capabilities
+ */
+ Assert(pThis->pSwitchPort);
+ if (vboxNetFltTryRetainBusyNotDisconnected(pThis))
+ {
+ vboxNetFltLinuxReportNicGsoCapabilities(pThis);
+ pThis->pSwitchPort->pfnReportMacAddress(pThis->pSwitchPort, &pThis->u.s.MacAddr);
+ pThis->pSwitchPort->pfnReportPromiscuousMode(pThis->pSwitchPort, vboxNetFltLinuxPromiscuous(pThis));
+ pThis->pSwitchPort->pfnReportNoPreemptDsts(pThis->pSwitchPort, INTNETTRUNKDIR_WIRE | INTNETTRUNKDIR_HOST);
+ vboxNetFltRelease(pThis, true /*fBusy*/);
+ }
+
+ LogRel(("VBoxNetFlt: attached to '%s' / %RTmac\n", pThis->szName, &pThis->u.s.MacAddr));
+ return VINF_SUCCESS;
+}
+
+
+static int vboxNetFltLinuxUnregisterDevice(PVBOXNETFLTINS pThis, struct net_device *pDev)
+{
+ bool fRegistered;
+ Assert(!pThis->fDisconnectedFromHost);
+
+#ifdef VBOXNETFLT_WITH_HOST2WIRE_FILTER
+ vboxNetFltLinuxUnhookDev(pThis, pDev);
+#endif
+
+ if (ASMAtomicCmpXchgBool(&pThis->u.s.fPacketHandler, false, true))
+ {
+ dev_remove_pack(&pThis->u.s.PacketType);
+ Log(("vboxNetFltLinuxUnregisterDevice: this=%p: packet handler removed.\n", pThis));
+ }
+
+ RTSpinlockAcquire(pThis->hSpinlock);
+ fRegistered = ASMAtomicXchgBool(&pThis->u.s.fRegistered, false);
+ if (fRegistered)
+ {
+ ASMAtomicWriteBool(&pThis->fDisconnectedFromHost, true);
+ ASMAtomicUoWriteNullPtr(&pThis->u.s.pDev);
+ }
+ RTSpinlockRelease(pThis->hSpinlock);
+
+ if (fRegistered)
+ {
+#ifndef VBOXNETFLT_LINUX_NO_XMIT_QUEUE
+ skb_queue_purge(&pThis->u.s.XmitQueue);
+#endif
+ Log(("vboxNetFltLinuxUnregisterDevice: this=%p: xmit queue purged.\n", pThis));
+ Log(("vboxNetFltLinuxUnregisterDevice: Device %p(%s) released. ref=%d\n",
+ pDev, pDev->name,
+#if RTLNX_VER_MIN(2,6,37)
+ netdev_refcnt_read(pDev)
+#else
+ atomic_read(&pDev->refcnt)
+#endif
+ ));
+ dev_put(pDev);
+ }
+
+ return NOTIFY_OK;
+}
+
+static int vboxNetFltLinuxDeviceIsUp(PVBOXNETFLTINS pThis, struct net_device *pDev)
+{
+ /* Check if we are not suspended and promiscuous mode has not been set. */
+ if ( pThis->enmTrunkState == INTNETTRUNKIFSTATE_ACTIVE
+ && !ASMAtomicUoReadBool(&pThis->u.s.fPromiscuousSet))
+ {
+ /* Note that there is no need for locking as the kernel got hold of the lock already. */
+ dev_set_promiscuity(pDev, 1);
+ ASMAtomicWriteBool(&pThis->u.s.fPromiscuousSet, true);
+ Log(("vboxNetFltLinuxDeviceIsUp: enabled promiscuous mode on %s (%d)\n", pThis->szName, pDev->promiscuity));
+ }
+ else
+ Log(("vboxNetFltLinuxDeviceIsUp: no need to enable promiscuous mode on %s (%d)\n", pThis->szName, pDev->promiscuity));
+ return NOTIFY_OK;
+}
+
+static int vboxNetFltLinuxDeviceGoingDown(PVBOXNETFLTINS pThis, struct net_device *pDev)
+{
+ /* Undo promiscuous mode if we has set it. */
+ if (ASMAtomicUoReadBool(&pThis->u.s.fPromiscuousSet))
+ {
+ /* Note that there is no need for locking as the kernel got hold of the lock already. */
+ dev_set_promiscuity(pDev, -1);
+ ASMAtomicWriteBool(&pThis->u.s.fPromiscuousSet, false);
+ Log(("vboxNetFltLinuxDeviceGoingDown: disabled promiscuous mode on %s (%d)\n", pThis->szName, pDev->promiscuity));
+ }
+ else
+ Log(("vboxNetFltLinuxDeviceGoingDown: no need to disable promiscuous mode on %s (%d)\n", pThis->szName, pDev->promiscuity));
+ return NOTIFY_OK;
+}
+
+/**
+ * Callback for listening to MTU change event.
+ *
+ * We need to track changes of host's inteface MTU to discard over-sized frames
+ * coming from the internal network as they may hang the TX queue of host's
+ * adapter.
+ *
+ * @returns NOTIFY_OK
+ * @param pThis The netfilter instance.
+ * @param pDev Pointer to device structure of host's interface.
+ */
+static int vboxNetFltLinuxDeviceMtuChange(PVBOXNETFLTINS pThis, struct net_device *pDev)
+{
+ ASMAtomicWriteU32(&pThis->u.s.cbMtu, pDev->mtu);
+ Log(("vboxNetFltLinuxDeviceMtuChange: set MTU for %s to %d\n", pThis->szName, pDev->mtu));
+ return NOTIFY_OK;
+}
+
+#ifdef LOG_ENABLED
+/** Stringify the NETDEV_XXX constants. */
+static const char *vboxNetFltLinuxGetNetDevEventName(unsigned long ulEventType)
+{
+ const char *pszEvent = "NETDEV_<unknown>";
+ switch (ulEventType)
+ {
+ case NETDEV_REGISTER: pszEvent = "NETDEV_REGISTER"; break;
+ case NETDEV_UNREGISTER: pszEvent = "NETDEV_UNREGISTER"; break;
+ case NETDEV_UP: pszEvent = "NETDEV_UP"; break;
+ case NETDEV_DOWN: pszEvent = "NETDEV_DOWN"; break;
+ case NETDEV_REBOOT: pszEvent = "NETDEV_REBOOT"; break;
+ case NETDEV_CHANGENAME: pszEvent = "NETDEV_CHANGENAME"; break;
+ case NETDEV_CHANGE: pszEvent = "NETDEV_CHANGE"; break;
+ case NETDEV_CHANGEMTU: pszEvent = "NETDEV_CHANGEMTU"; break;
+ case NETDEV_CHANGEADDR: pszEvent = "NETDEV_CHANGEADDR"; break;
+ case NETDEV_GOING_DOWN: pszEvent = "NETDEV_GOING_DOWN"; break;
+# ifdef NETDEV_FEAT_CHANGE
+ case NETDEV_FEAT_CHANGE: pszEvent = "NETDEV_FEAT_CHANGE"; break;
+# endif
+ }
+ return pszEvent;
+}
+#endif /* LOG_ENABLED */
+
+/**
+ * Callback for listening to netdevice events.
+ *
+ * This works the rediscovery, clean up on unregistration, promiscuity on
+ * up/down, and GSO feature changes from ethtool.
+ *
+ * @returns NOTIFY_OK
+ * @param self Pointer to our notifier registration block.
+ * @param ulEventType The event.
+ * @param ptr Event specific, but it is usually the device it
+ * relates to.
+ */
+static int vboxNetFltLinuxNotifierCallback(struct notifier_block *self, unsigned long ulEventType, void *ptr)
+
+{
+ PVBOXNETFLTINS pThis = VBOX_FLT_NB_TO_INST(self);
+ struct net_device *pMyDev = ASMAtomicUoReadPtrT(&pThis->u.s.pDev, struct net_device *);
+ struct net_device *pDev = VBOX_NETDEV_NOTIFIER_INFO_TO_DEV(ptr);
+ int rc = NOTIFY_OK;
+
+ Log(("VBoxNetFlt: got event %s(0x%lx) on %s, pDev=%p pThis=%p pThis->u.s.pDev=%p\n",
+ vboxNetFltLinuxGetNetDevEventName(ulEventType), ulEventType, pDev->name, pDev, pThis, pMyDev));
+
+ if (ulEventType == NETDEV_REGISTER)
+ {
+#if RTLNX_VER_MIN(2,6,24) /* cgroups/namespaces introduced */
+# if RTLNX_VER_MIN(2,6,26)
+# define VBOX_DEV_NET(dev) dev_net(dev)
+# define VBOX_NET_EQ(n1, n2) net_eq((n1), (n2))
+# else
+# define VBOX_DEV_NET(dev) ((dev)->nd_net)
+# define VBOX_NET_EQ(n1, n2) ((n1) == (n2))
+# endif
+ struct net *pMyNet = current->nsproxy->net_ns;
+ struct net *pDevNet = VBOX_DEV_NET(pDev);
+
+ if (VBOX_NET_EQ(pDevNet, pMyNet))
+#endif /* namespaces */
+ {
+ if (strcmp(pDev->name, pThis->szName) == 0)
+ {
+ vboxNetFltLinuxAttachToInterface(pThis, pDev);
+ }
+ }
+ }
+ else
+ {
+ if (pDev == pMyDev)
+ {
+ switch (ulEventType)
+ {
+ case NETDEV_UNREGISTER:
+ rc = vboxNetFltLinuxUnregisterDevice(pThis, pDev);
+ break;
+ case NETDEV_UP:
+ rc = vboxNetFltLinuxDeviceIsUp(pThis, pDev);
+ break;
+ case NETDEV_GOING_DOWN:
+ rc = vboxNetFltLinuxDeviceGoingDown(pThis, pDev);
+ break;
+ case NETDEV_CHANGEMTU:
+ rc = vboxNetFltLinuxDeviceMtuChange(pThis, pDev);
+ break;
+ case NETDEV_CHANGENAME:
+ break;
+#ifdef NETDEV_FEAT_CHANGE
+ case NETDEV_FEAT_CHANGE:
+ vboxNetFltLinuxReportNicGsoCapabilities(pThis);
+ break;
+#endif
+ }
+ }
+ }
+
+ return rc;
+}
+
+/*
+ * Initial enumeration of netdevs. Called with NETDEV_REGISTER by
+ * register_netdevice_notifier() under rtnl lock.
+ */
+static int vboxNetFltLinuxEnumeratorCallback(struct notifier_block *self, unsigned long ulEventType, void *ptr)
+{
+ PVBOXNETFLTINS pThis = ((PVBOXNETFLTNOTIFIER)self)->pThis;
+ struct net_device *dev = VBOX_NETDEV_NOTIFIER_INFO_TO_DEV(ptr);
+ struct in_device *in_dev;
+ struct inet6_dev *in6_dev;
+
+ if (ulEventType != NETDEV_REGISTER)
+ return NOTIFY_OK;
+
+ if (RT_UNLIKELY(pThis->pSwitchPort->pfnNotifyHostAddress == NULL))
+ return NOTIFY_OK;
+
+ /*
+ * IPv4
+ */
+#if RTLNX_VER_MIN(2,6,14)
+ in_dev = __in_dev_get_rtnl(dev);
+#else
+ in_dev = __in_dev_get(dev);
+#endif
+ if (in_dev != NULL)
+ {
+ struct in_ifaddr *ifa;
+
+ for (ifa = in_dev->ifa_list; ifa; ifa = ifa->ifa_next) {
+ if (VBOX_IPV4_IS_LOOPBACK(ifa->ifa_address))
+ return NOTIFY_OK;
+
+ if ( dev != pThis->u.s.pDev
+ && VBOX_IPV4_IS_LINKLOCAL_169(ifa->ifa_address))
+ continue;
+
+ Log(("%s: %s: IPv4 addr %RTnaipv4 mask %RTnaipv4\n",
+ __FUNCTION__, VBOX_NETDEV_NAME(dev),
+ ifa->ifa_address, ifa->ifa_mask));
+
+ pThis->pSwitchPort->pfnNotifyHostAddress(pThis->pSwitchPort,
+ /* :fAdded */ true, kIntNetAddrType_IPv4, &ifa->ifa_address);
+ }
+ }
+
+ /*
+ * IPv6
+ */
+ in6_dev = __in6_dev_get(dev);
+ if (in6_dev != NULL)
+ {
+ struct inet6_ifaddr *ifa;
+
+ read_lock_bh(&in6_dev->lock);
+#if RTLNX_VER_MIN(2,6,35)
+ list_for_each_entry(ifa, &in6_dev->addr_list, if_list)
+#else
+ for (ifa = in6_dev->addr_list; ifa != NULL; ifa = ifa->if_next)
+#endif
+ {
+ if ( dev != pThis->u.s.pDev
+ && ipv6_addr_type(&ifa->addr) & (IPV6_ADDR_LINKLOCAL | IPV6_ADDR_LOOPBACK))
+ continue;
+
+ Log(("%s: %s: IPv6 addr %RTnaipv6/%u\n",
+ __FUNCTION__, VBOX_NETDEV_NAME(dev),
+ &ifa->addr, (unsigned)ifa->prefix_len));
+
+ pThis->pSwitchPort->pfnNotifyHostAddress(pThis->pSwitchPort,
+ /* :fAdded */ true, kIntNetAddrType_IPv6, &ifa->addr);
+ }
+ read_unlock_bh(&in6_dev->lock);
+ }
+
+ return NOTIFY_OK;
+}
+
+
+static int vboxNetFltLinuxNotifierIPv4Callback(struct notifier_block *self, unsigned long ulEventType, void *ptr)
+{
+ PVBOXNETFLTINS pThis = RT_FROM_MEMBER(self, VBOXNETFLTINS, u.s.NotifierIPv4);
+ struct net_device *pDev, *pEventDev;
+ struct in_ifaddr *ifa = (struct in_ifaddr *)ptr;
+ bool fMyDev;
+ int rc = NOTIFY_OK;
+
+ pDev = vboxNetFltLinuxRetainNetDev(pThis);
+ pEventDev = ifa->ifa_dev->dev;
+ fMyDev = (pDev == pEventDev);
+ Log(("VBoxNetFlt: %s: IPv4 event %s(0x%lx) %s: addr %RTnaipv4 mask %RTnaipv4\n",
+ pDev ? VBOX_NETDEV_NAME(pDev) : "<unknown>",
+ vboxNetFltLinuxGetNetDevEventName(ulEventType), ulEventType,
+ pEventDev ? VBOX_NETDEV_NAME(pEventDev) : "<unknown>",
+ ifa->ifa_address, ifa->ifa_mask));
+
+ if (pDev != NULL)
+ vboxNetFltLinuxReleaseNetDev(pThis, pDev);
+
+ if (VBOX_IPV4_IS_LOOPBACK(ifa->ifa_address))
+ return NOTIFY_OK;
+
+ if ( !fMyDev
+ && VBOX_IPV4_IS_LINKLOCAL_169(ifa->ifa_address))
+ return NOTIFY_OK;
+
+ if (pThis->pSwitchPort->pfnNotifyHostAddress)
+ {
+ bool fAdded;
+ if (ulEventType == NETDEV_UP)
+ fAdded = true;
+ else if (ulEventType == NETDEV_DOWN)
+ fAdded = false;
+ else
+ return NOTIFY_OK;
+
+ pThis->pSwitchPort->pfnNotifyHostAddress(pThis->pSwitchPort, fAdded,
+ kIntNetAddrType_IPv4, &ifa->ifa_local);
+ }
+
+ return rc;
+}
+
+
+static int vboxNetFltLinuxNotifierIPv6Callback(struct notifier_block *self, unsigned long ulEventType, void *ptr)
+{
+ PVBOXNETFLTINS pThis = RT_FROM_MEMBER(self, VBOXNETFLTINS, u.s.NotifierIPv6);
+ struct net_device *pDev, *pEventDev;
+ struct inet6_ifaddr *ifa = (struct inet6_ifaddr *)ptr;
+ bool fMyDev;
+ int rc = NOTIFY_OK;
+
+ pDev = vboxNetFltLinuxRetainNetDev(pThis);
+ pEventDev = ifa->idev->dev;
+ fMyDev = (pDev == pEventDev);
+ Log(("VBoxNetFlt: %s: IPv6 event %s(0x%lx) %s: %RTnaipv6\n",
+ pDev ? VBOX_NETDEV_NAME(pDev) : "<unknown>",
+ vboxNetFltLinuxGetNetDevEventName(ulEventType), ulEventType,
+ pEventDev ? VBOX_NETDEV_NAME(pEventDev) : "<unknown>",
+ &ifa->addr));
+
+ if (pDev != NULL)
+ vboxNetFltLinuxReleaseNetDev(pThis, pDev);
+
+ if ( !fMyDev
+ && ipv6_addr_type(&ifa->addr) & (IPV6_ADDR_LINKLOCAL | IPV6_ADDR_LOOPBACK))
+ return NOTIFY_OK;
+
+ if (pThis->pSwitchPort->pfnNotifyHostAddress)
+ {
+ bool fAdded;
+ if (ulEventType == NETDEV_UP)
+ fAdded = true;
+ else if (ulEventType == NETDEV_DOWN)
+ fAdded = false;
+ else
+ return NOTIFY_OK;
+
+ pThis->pSwitchPort->pfnNotifyHostAddress(pThis->pSwitchPort, fAdded,
+ kIntNetAddrType_IPv6, &ifa->addr);
+ }
+
+ return rc;
+}
+
+
+bool vboxNetFltOsMaybeRediscovered(PVBOXNETFLTINS pThis)
+{
+ return !ASMAtomicUoReadBool(&pThis->fDisconnectedFromHost);
+}
+
+int vboxNetFltPortOsXmit(PVBOXNETFLTINS pThis, void *pvIfData, PINTNETSG pSG, uint32_t fDst)
+{
+ struct net_device * pDev;
+ int err;
+ int rc = VINF_SUCCESS;
+ IPRT_LINUX_SAVE_EFL_AC();
+ NOREF(pvIfData);
+
+ LogFlow(("vboxNetFltPortOsXmit: pThis=%p (%s)\n", pThis, pThis->szName));
+
+ pDev = vboxNetFltLinuxRetainNetDev(pThis);
+ if (pDev)
+ {
+ /*
+ * Create a sk_buff for the gather list and push it onto the wire.
+ */
+ if (fDst & INTNETTRUNKDIR_WIRE)
+ {
+ struct sk_buff *pBuf = vboxNetFltLinuxSkBufFromSG(pThis, pSG, true);
+ if (pBuf)
+ {
+ vboxNetFltDumpPacket(pSG, true, "wire", 1);
+ Log6(("vboxNetFltPortOsXmit: pBuf->cb dump:\n%.*Rhxd\n", sizeof(pBuf->cb), pBuf->cb));
+ Log6(("vboxNetFltPortOsXmit: dev_queue_xmit(%p)\n", pBuf));
+ err = dev_queue_xmit(pBuf);
+ if (err)
+ rc = RTErrConvertFromErrno(err);
+ }
+ else
+ rc = VERR_NO_MEMORY;
+ }
+
+ /*
+ * Create a sk_buff for the gather list and push it onto the host stack.
+ */
+ if (fDst & INTNETTRUNKDIR_HOST)
+ {
+ struct sk_buff *pBuf = vboxNetFltLinuxSkBufFromSG(pThis, pSG, false);
+ if (pBuf)
+ {
+ vboxNetFltDumpPacket(pSG, true, "host", (fDst & INTNETTRUNKDIR_WIRE) ? 0 : 1);
+ Log6(("vboxNetFltPortOsXmit: pBuf->cb dump:\n%.*Rhxd\n", sizeof(pBuf->cb), pBuf->cb));
+ Log6(("vboxNetFltPortOsXmit: netif_rx_ni(%p)\n", pBuf));
+#if RTLNX_VER_MIN(5,18,0) || RTLNX_RHEL_MIN(9,1)
+ local_bh_disable();
+ err = netif_rx(pBuf);
+ local_bh_enable();
+#else
+ err = netif_rx_ni(pBuf);
+#endif
+ if (err)
+ rc = RTErrConvertFromErrno(err);
+ }
+ else
+ rc = VERR_NO_MEMORY;
+ }
+
+ vboxNetFltLinuxReleaseNetDev(pThis, pDev);
+ }
+
+ IPRT_LINUX_RESTORE_EFL_AC();
+ return rc;
+}
+
+
+void vboxNetFltPortOsSetActive(PVBOXNETFLTINS pThis, bool fActive)
+{
+ struct net_device *pDev;
+ IPRT_LINUX_SAVE_EFL_AC();
+
+ LogFlow(("vboxNetFltPortOsSetActive: pThis=%p (%s), fActive=%RTbool, fDisablePromiscuous=%RTbool\n",
+ pThis, pThis->szName, fActive, pThis->fDisablePromiscuous));
+
+ if (pThis->fDisablePromiscuous)
+ return;
+
+ pDev = vboxNetFltLinuxRetainNetDev(pThis);
+ if (pDev)
+ {
+ /*
+ * This api is a bit weird, the best reference is the code.
+ *
+ * Also, we have a bit or race conditions wrt the maintenance of
+ * host the interface promiscuity for vboxNetFltPortOsIsPromiscuous.
+ */
+#ifdef LOG_ENABLED
+ u_int16_t fIf;
+ unsigned const cPromiscBefore = pDev->promiscuity;
+#endif
+ if (fActive)
+ {
+ Assert(!pThis->u.s.fPromiscuousSet);
+
+ rtnl_lock();
+ dev_set_promiscuity(pDev, 1);
+ rtnl_unlock();
+ pThis->u.s.fPromiscuousSet = true;
+ Log(("vboxNetFltPortOsSetActive: enabled promiscuous mode on %s (%d)\n", pThis->szName, pDev->promiscuity));
+ }
+ else
+ {
+ if (pThis->u.s.fPromiscuousSet)
+ {
+ rtnl_lock();
+ dev_set_promiscuity(pDev, -1);
+ rtnl_unlock();
+ Log(("vboxNetFltPortOsSetActive: disabled promiscuous mode on %s (%d)\n", pThis->szName, pDev->promiscuity));
+ }
+ pThis->u.s.fPromiscuousSet = false;
+
+#ifdef LOG_ENABLED
+ fIf = dev_get_flags(pDev);
+ Log(("VBoxNetFlt: fIf=%#x; %d->%d\n", fIf, cPromiscBefore, pDev->promiscuity));
+#endif
+ }
+
+ vboxNetFltLinuxReleaseNetDev(pThis, pDev);
+ }
+ IPRT_LINUX_RESTORE_EFL_AC();
+}
+
+
+int vboxNetFltOsDisconnectIt(PVBOXNETFLTINS pThis)
+{
+ /*
+ * Remove packet handler when we get disconnected from internal switch as
+ * we don't want the handler to forward packets to disconnected switch.
+ */
+ if (ASMAtomicCmpXchgBool(&pThis->u.s.fPacketHandler, false, true))
+ {
+ IPRT_LINUX_SAVE_EFL_AC();
+ dev_remove_pack(&pThis->u.s.PacketType);
+ Log(("vboxNetFltOsDisconnectIt: this=%p: Packet handler removed.\n", pThis));
+ IPRT_LINUX_RESTORE_EFL_AC();
+ }
+ return VINF_SUCCESS;
+}
+
+
+int vboxNetFltOsConnectIt(PVBOXNETFLTINS pThis)
+{
+ IPRT_LINUX_SAVE_EFL_AC();
+
+ /*
+ * Report the GSO capabilities of the host and device (if connected).
+ * Note! No need to mark ourselves busy here.
+ */
+ /** @todo duplicate work here now? Attach */
+#if defined(VBOXNETFLT_WITH_GSO_XMIT_HOST)
+ Log3(("vboxNetFltOsConnectIt: reporting host tso tso6\n"));
+ pThis->pSwitchPort->pfnReportGsoCapabilities(pThis->pSwitchPort,
+ 0
+ | RT_BIT_32(PDMNETWORKGSOTYPE_IPV4_TCP)
+ | RT_BIT_32(PDMNETWORKGSOTYPE_IPV6_TCP)
+ , INTNETTRUNKDIR_HOST);
+
+#endif
+ vboxNetFltLinuxReportNicGsoCapabilities(pThis);
+
+ IPRT_LINUX_RESTORE_EFL_AC();
+ return VINF_SUCCESS;
+}
+
+
+void vboxNetFltOsDeleteInstance(PVBOXNETFLTINS pThis)
+{
+ struct net_device *pDev;
+ bool fRegistered;
+ IPRT_LINUX_SAVE_EFL_AC();
+
+#ifdef VBOXNETFLT_WITH_HOST2WIRE_FILTER
+ vboxNetFltLinuxUnhookDev(pThis, NULL);
+#endif
+
+ /** @todo This code may race vboxNetFltLinuxUnregisterDevice (very very
+ * unlikely, but none the less). Since it doesn't actually update the
+ * state (just reads it), it is likely to panic in some interesting
+ * ways. */
+
+ RTSpinlockAcquire(pThis->hSpinlock);
+ pDev = ASMAtomicUoReadPtrT(&pThis->u.s.pDev, struct net_device *);
+ fRegistered = ASMAtomicXchgBool(&pThis->u.s.fRegistered, false);
+ RTSpinlockRelease(pThis->hSpinlock);
+
+ if (fRegistered)
+ {
+ vboxNetFltSetLinkState(pThis, pDev, false);
+
+#ifndef VBOXNETFLT_LINUX_NO_XMIT_QUEUE
+ skb_queue_purge(&pThis->u.s.XmitQueue);
+#endif
+ Log(("vboxNetFltOsDeleteInstance: this=%p: xmit queue purged.\n", pThis));
+ Log(("vboxNetFltOsDeleteInstance: Device %p(%s) released. ref=%d\n",
+ pDev, pDev->name,
+#if RTLNX_VER_MIN(2,6,37)
+ netdev_refcnt_read(pDev)
+#else
+ atomic_read(&pDev->refcnt)
+#endif
+ ));
+ dev_put(pDev);
+ }
+
+ unregister_inet6addr_notifier(&pThis->u.s.NotifierIPv6);
+ unregister_inetaddr_notifier(&pThis->u.s.NotifierIPv4);
+
+ Log(("vboxNetFltOsDeleteInstance: this=%p: Notifier removed.\n", pThis));
+ unregister_netdevice_notifier(&pThis->u.s.Notifier);
+ module_put(THIS_MODULE);
+
+ IPRT_LINUX_RESTORE_EFL_AC();
+}
+
+
+int vboxNetFltOsInitInstance(PVBOXNETFLTINS pThis, void *pvContext)
+{
+ int err;
+ IPRT_LINUX_SAVE_EFL_AC();
+ NOREF(pvContext);
+
+ pThis->u.s.Notifier.notifier_call = vboxNetFltLinuxNotifierCallback;
+ err = register_netdevice_notifier(&pThis->u.s.Notifier);
+ if (err)
+ {
+ IPRT_LINUX_RESTORE_EFL_AC();
+ return VERR_INTNET_FLT_IF_FAILED;
+ }
+ if (!pThis->u.s.fRegistered)
+ {
+ unregister_netdevice_notifier(&pThis->u.s.Notifier);
+ LogRel(("VBoxNetFlt: failed to find %s.\n", pThis->szName));
+ IPRT_LINUX_RESTORE_EFL_AC();
+ return VERR_INTNET_FLT_IF_NOT_FOUND;
+ }
+
+ Log(("vboxNetFltOsInitInstance: this=%p: Notifier installed.\n", pThis));
+ if ( pThis->fDisconnectedFromHost
+ || !try_module_get(THIS_MODULE))
+ {
+ IPRT_LINUX_RESTORE_EFL_AC();
+ return VERR_INTNET_FLT_IF_FAILED;
+ }
+
+ if (pThis->pSwitchPort->pfnNotifyHostAddress)
+ {
+ VBOXNETFLTNOTIFIER Enumerator;
+
+ /*
+ * register_inetaddr_notifier() and register_inet6addr_notifier()
+ * do not call the callback for existing devices. Enumerating
+ * all network devices explicitly is a bit of an ifdef mess,
+ * so co-opt register_netdevice_notifier() to do that for us.
+ */
+ RT_ZERO(Enumerator);
+ Enumerator.Notifier.notifier_call = vboxNetFltLinuxEnumeratorCallback;
+ Enumerator.pThis = pThis;
+
+ err = register_netdevice_notifier(&Enumerator.Notifier);
+ if (err)
+ {
+ LogRel(("%s: failed to enumerate network devices: error %d\n", __FUNCTION__, err));
+ IPRT_LINUX_RESTORE_EFL_AC();
+ return VINF_SUCCESS;
+ }
+
+ unregister_netdevice_notifier(&Enumerator.Notifier);
+
+ pThis->u.s.NotifierIPv4.notifier_call = vboxNetFltLinuxNotifierIPv4Callback;
+ err = register_inetaddr_notifier(&pThis->u.s.NotifierIPv4);
+ if (err)
+ LogRel(("%s: failed to register IPv4 notifier: error %d\n", __FUNCTION__, err));
+
+ pThis->u.s.NotifierIPv6.notifier_call = vboxNetFltLinuxNotifierIPv6Callback;
+ err = register_inet6addr_notifier(&pThis->u.s.NotifierIPv6);
+ if (err)
+ LogRel(("%s: failed to register IPv6 notifier: error %d\n", __FUNCTION__, err));
+ }
+
+ IPRT_LINUX_RESTORE_EFL_AC();
+ return VINF_SUCCESS;
+}
+
+int vboxNetFltOsPreInitInstance(PVBOXNETFLTINS pThis)
+{
+ IPRT_LINUX_SAVE_EFL_AC();
+
+ /*
+ * Init the linux specific members.
+ */
+ ASMAtomicUoWriteNullPtr(&pThis->u.s.pDev);
+ pThis->u.s.fRegistered = false;
+ pThis->u.s.fPromiscuousSet = false;
+ pThis->u.s.fPacketHandler = false;
+ memset(&pThis->u.s.PacketType, 0, sizeof(pThis->u.s.PacketType));
+#ifndef VBOXNETFLT_LINUX_NO_XMIT_QUEUE
+ skb_queue_head_init(&pThis->u.s.XmitQueue);
+# if RTLNX_VER_MIN(2,6,20)
+ INIT_WORK(&pThis->u.s.XmitTask, vboxNetFltLinuxXmitTask);
+# else
+ INIT_WORK(&pThis->u.s.XmitTask, vboxNetFltLinuxXmitTask, &pThis->u.s.XmitTask);
+# endif
+#endif
+
+ IPRT_LINUX_RESTORE_EFL_AC();
+ return VINF_SUCCESS;
+}
+
+
+void vboxNetFltPortOsNotifyMacAddress(PVBOXNETFLTINS pThis, void *pvIfData, PCRTMAC pMac)
+{
+ NOREF(pThis); NOREF(pvIfData); NOREF(pMac);
+}
+
+
+int vboxNetFltPortOsConnectInterface(PVBOXNETFLTINS pThis, void *pvIf, void **pvIfData)
+{
+ /* Nothing to do */
+ NOREF(pThis); NOREF(pvIf); NOREF(pvIfData);
+ return VINF_SUCCESS;
+}
+
+
+int vboxNetFltPortOsDisconnectInterface(PVBOXNETFLTINS pThis, void *pvIfData)
+{
+ /* Nothing to do */
+ NOREF(pThis); NOREF(pvIfData);
+ return VINF_SUCCESS;
+}
+
diff --git a/src/VBox/HostDrivers/VBoxNetFlt/linux/files_vboxnetflt b/src/VBox/HostDrivers/VBoxNetFlt/linux/files_vboxnetflt
new file mode 100755
index 00000000..f2f8f1f4
--- /dev/null
+++ b/src/VBox/HostDrivers/VBoxNetFlt/linux/files_vboxnetflt
@@ -0,0 +1,113 @@
+#!/bin/sh
+# $Id: files_vboxnetflt $
+## @file
+# Shared file between Makefile.kmk and export_modules.sh.
+#
+
+#
+# Copyright (C) 2007-2023 Oracle and/or its affiliates.
+#
+# This file is part of VirtualBox base platform packages, as
+# available from https://www.virtualbox.org.
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation, in version 3 of the
+# License.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, see <https://www.gnu.org/licenses>.
+#
+# The contents of this file may alternatively be used under the terms
+# of the Common Development and Distribution License Version 1.0
+# (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+# in the VirtualBox distribution, in which case the provisions of the
+# CDDL are applicable instead of those of the GPL.
+#
+# You may elect to license modified versions of this file under the
+# terms and conditions of either the GPL or the CDDL or both.
+#
+# SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+#
+
+VBOX_VBOXNETFLT_SOURCES=" \
+ ${PATH_ROOT}/include/iprt/alloc.h=>include/iprt/alloc.h \
+ ${PATH_ROOT}/include/iprt/alloca.h=>include/iprt/alloca.h \
+ ${PATH_ROOT}/include/iprt/asm.h=>include/iprt/asm.h \
+ ${PATH_ROOT}/include/iprt/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/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/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/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/uuid.h=>include/iprt/uuid.h \
+ ${PATH_ROOT}/include/iprt/x86-helpers.h=>include/iprt/x86-helpers.h \
+ ${PATH_ROOT}/include/iprt/linux/version.h=>include/iprt/linux/version.h \
+ ${PATH_ROOT}/include/iprt/nocrt/limits.h=>include/iprt/nocrt/limits.h \
+ ${PATH_ROOT}/include/VBox/cdefs.h=>include/VBox/cdefs.h \
+ ${PATH_ROOT}/include/VBox/err.h=>include/VBox/err.h \
+ ${PATH_ROOT}/include/VBox/log.h=>include/VBox/log.h \
+ ${PATH_ROOT}/include/VBox/intnet.h=>include/VBox/intnet.h \
+ ${PATH_ROOT}/include/VBox/intnetinline.h=>include/VBox/intnetinline.h \
+ ${PATH_ROOT}/include/VBox/vmm/pdmnetinline.h=>include/VBox/vmm/pdmnetinline.h \
+ ${PATH_ROOT}/include/VBox/param.h=>include/VBox/param.h \
+ ${PATH_ROOT}/include/VBox/vmm/stam.h=>include/VBox/vmm/stam.h \
+ ${PATH_ROOT}/include/VBox/sup.h=>include/VBox/sup.h \
+ ${PATH_ROOT}/include/VBox/types.h=>include/VBox/types.h \
+ ${PATH_ROOT}/include/VBox/SUPDrvMangling.h=>include/VBox/SUPDrvMangling.h \
+ ${PATH_ROOT}/src/VBox/HostDrivers/VBoxNetFlt/linux/VBoxNetFlt-linux.c=>linux/VBoxNetFlt-linux.c \
+ ${PATH_ROOT}/src/VBox/HostDrivers/VBoxNetFlt/VBoxNetFlt.c=>VBoxNetFlt.c \
+ ${PATH_ROOT}/src/VBox/HostDrivers/VBoxNetFlt/VBoxNetFltInternal.h=>VBoxNetFltInternal.h \
+ ${PATH_ROOT}/src/VBox/HostDrivers/Support/SUPDrvIDC.h=>SUPDrvIDC.h \
+ ${PATH_ROOT}/src/VBox/HostDrivers/Support/SUPR0IdcClient.c=>SUPR0IdcClient.c \
+ ${PATH_ROOT}/src/VBox/HostDrivers/Support/SUPR0IdcClientComponent.c=>SUPR0IdcClientComponent.c \
+ ${PATH_ROOT}/src/VBox/HostDrivers/Support/SUPR0IdcClientInternal.h=>SUPR0IdcClientInternal.h \
+ ${PATH_ROOT}/src/VBox/HostDrivers/Support/linux/SUPR0IdcClient-linux.c=>linux/SUPR0IdcClient-linux.c \
+ ${PATH_ROOT}/src/VBox/Installer/linux/Makefile-footer.gmk=>Makefile-footer.gmk \
+ ${PATH_ROOT}/src/VBox/Installer/linux/Makefile-header.gmk=>Makefile-header.gmk \
+ ${PATH_ROOT}/src/VBox/Runtime/common/math/gcc/divdi3.c=>math/gcc/divdi3.c \
+ ${PATH_ROOT}/src/VBox/Runtime/common/math/gcc/moddi3.c=>math/gcc/moddi3.c \
+ ${PATH_ROOT}/src/VBox/Runtime/common/math/gcc/qdivrem.c=>math/gcc/qdivrem.c \
+ ${PATH_ROOT}/src/VBox/Runtime/common/math/gcc/quad.h=>math/gcc/quad.h \
+ ${PATH_ROOT}/src/VBox/Runtime/common/math/gcc/udivdi3.c=>math/gcc/udivdi3.c \
+ ${PATH_ROOT}/src/VBox/Runtime/common/math/gcc/udivmoddi4.c=>math/gcc/udivmoddi4.c \
+ ${PATH_ROOT}/src/VBox/Runtime/common/math/gcc/umoddi3.c=>math/gcc/umoddi3.c \
+ ${PATH_ROOT}/src/VBox/Runtime/r0drv/linux/the-linux-kernel.h=>r0drv/linux/the-linux-kernel.h \
+ ${PATH_OUT}/version-generated.h=>version-generated.h \
+ ${PATH_OUT}/revision-generated.h=>revision-generated.h \
+ ${PATH_OUT}/product-generated.h=>product-generated.h \
+"
+
diff --git a/src/VBox/HostDrivers/VBoxNetFlt/solaris/VBoxNetFlt-solaris.c b/src/VBox/HostDrivers/VBoxNetFlt/solaris/VBoxNetFlt-solaris.c
new file mode 100644
index 00000000..f455ec3c
--- /dev/null
+++ b/src/VBox/HostDrivers/VBoxNetFlt/solaris/VBoxNetFlt-solaris.c
@@ -0,0 +1,4042 @@
+/* $Id: VBoxNetFlt-solaris.c $ */
+/** @file
+ * VBoxNetFlt - Network Filter Driver (Host), Solaris Specific Code.
+ */
+
+/*
+ * Copyright (C) 2008-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#define LOG_GROUP LOG_GROUP_NET_FLT_DRV
+#include <VBox/log.h>
+#include <VBox/err.h>
+#include <VBox/intnetinline.h>
+#include <VBox/version.h>
+#include <iprt/string.h>
+#include <iprt/initterm.h>
+#include <iprt/assert.h>
+#include <iprt/alloca.h>
+#include <iprt/net.h>
+#include <iprt/mem.h>
+#include <iprt/thread.h>
+#include <iprt/spinlock.h>
+#include <iprt/crc.h>
+#include <iprt/err.h>
+#include <iprt/ctype.h>
+#define VBOXNETFLT_SOLARIS_IPV6_POLLING
+#ifdef VBOXNETFLT_SOLARIS_IPV6_POLLING
+# include <iprt/timer.h>
+# include <iprt/time.h>
+#endif
+
+#include <inet/ip.h>
+#include <net/if.h>
+#include <sys/socket.h>
+#include <sys/kstr.h>
+#include <sys/file.h>
+#include <sys/sockio.h>
+#include <sys/strsubr.h>
+#include <sys/pathname.h>
+#include <sys/t_kuser.h>
+
+#include <sys/types.h>
+#include <sys/dlpi.h>
+#include <sys/types.h>
+#include <sys/time.h>
+#include <sys/param.h>
+#include <sys/ethernet.h>
+#include <sys/stat.h>
+#include <sys/stream.h>
+#include <sys/stropts.h>
+#include <sys/strsun.h>
+#include <sys/modctl.h>
+#include <sys/ddi.h>
+#include <sys/sunddi.h>
+#include <sys/sunldi.h>
+#include <sys/ctf_api.h>
+
+// Workaround for very strange define in sys/user.h
+// #define u (curproc->p_user) /* user is now part of proc structure */
+#ifdef u
+#undef u
+#endif
+
+#define VBOXNETFLT_OS_SPECFIC 1
+#include "../VBoxNetFltInternal.h"
+
+
+/*********************************************************************************************************************************
+* Defined Constants And Macros *
+*********************************************************************************************************************************/
+/** The module name. */
+#define DEVICE_NAME "vboxflt"
+/** The module descriptions as seen in 'modinfo'. */
+#define DEVICE_DESC_DRV "VirtualBox NetDrv"
+#define DEVICE_DESC_MOD "VirtualBox NetMod"
+
+#ifdef VBOXNETFLT_SOLARIS_IPV6_POLLING
+/** Driver properties */
+# define VBOXNETFLT_IP6POLLINTERVAL "ipv6-pollinterval"
+#endif
+
+/** Maximum loopback packet queue size per interface */
+#define VBOXNETFLT_LOOPBACK_SIZE 32
+
+/** VLAN tag masking, should probably be in IPRT? */
+#define VLAN_ID(vlan) (((vlan) >> 0) & 0x0fffu)
+#define VLAN_CFI(vlan) (((vlan) >> 12) & 0x0001u)
+#define VLAN_PRI(vlan) (((vlan) >> 13) & 0x0007u)
+#define VLAN_TAG(pri,cfi,vid) (((pri) << 13) | ((cfi) << 12) | ((vid) << 0))
+
+typedef struct VLANHEADER
+{
+ uint16_t Type;
+ uint16_t Data;
+} VLANHEADER;
+typedef struct VLANHEADER *PVLANHEADER;
+
+
+/*********************************************************************************************************************************
+* Global Functions *
+*********************************************************************************************************************************/
+/**
+ * Stream Driver hooks.
+ */
+static int VBoxNetFltSolarisGetInfo(dev_info_t *pDip, ddi_info_cmd_t enmCmd, void *pArg, void **ppvResult);
+static int VBoxNetFltSolarisAttach(dev_info_t *pDip, ddi_attach_cmd_t enmCmd);
+static int VBoxNetFltSolarisDetach(dev_info_t *pDip, ddi_detach_cmd_t enmCmd);
+static int VBoxNetFltSolarisQuiesceNotNeeded(dev_info_t *pDip);
+
+/**
+ * Stream Module hooks.
+ */
+static int VBoxNetFltSolarisModOpen(queue_t *pQueue, dev_t *pDev, int fFile, int fStream, cred_t *pCred);
+static int VBoxNetFltSolarisModClose(queue_t *pQueue, int fFile, cred_t *pCred);
+static int VBoxNetFltSolarisModReadPut(queue_t *pQueue, mblk_t *pMsg);
+static int VBoxNetFltSolarisModWritePut(queue_t *pQueue, mblk_t *pMsg);
+
+
+/*********************************************************************************************************************************
+* Structures and Typedefs *
+*********************************************************************************************************************************/
+/**
+ * Streams: module info.
+ */
+static struct module_info g_VBoxNetFltSolarisModInfo =
+{
+ 0xbad, /* module id */
+ DEVICE_NAME,
+ 0, /* min. packet size */
+ INFPSZ, /* max. packet size */
+ 0, /* hi-water mark */
+ 0 /* lo-water mark */
+};
+
+/**
+ * Streams: read queue hooks.
+ */
+static struct qinit g_VBoxNetFltSolarisReadQ =
+{
+ VBoxNetFltSolarisModReadPut,
+ NULL, /* service */
+ VBoxNetFltSolarisModOpen,
+ VBoxNetFltSolarisModClose,
+ NULL, /* admin (reserved) */
+ &g_VBoxNetFltSolarisModInfo,
+ NULL /* module stats */
+};
+
+/**
+ * Streams: write queue hooks.
+ */
+static struct qinit g_VBoxNetFltSolarisWriteQ =
+{
+ VBoxNetFltSolarisModWritePut,
+ NULL, /* service */
+ NULL, /* open */
+ NULL, /* close */
+ NULL, /* admin (reserved) */
+ &g_VBoxNetFltSolarisModInfo,
+ NULL /* module stats */
+};
+
+/**
+ * Streams: IO stream tab.
+ */
+static struct streamtab g_VBoxNetFltSolarisStreamTab =
+{
+ &g_VBoxNetFltSolarisReadQ,
+ &g_VBoxNetFltSolarisWriteQ,
+ NULL, /* muxread init */
+ NULL /* muxwrite init */
+};
+
+/**
+ * cb_ops: driver char/block entry points
+ */
+static struct cb_ops g_VBoxNetFltSolarisCbOps =
+{
+ nulldev, /* cb open */
+ nulldev, /* cb close */
+ nodev, /* b strategy */
+ nodev, /* b dump */
+ nodev, /* b print */
+ nodev, /* cb read */
+ nodev, /* cb write */
+ nodev, /* cb ioctl */
+ nodev, /* c devmap */
+ nodev, /* c mmap */
+ nodev, /* c segmap */
+ nochpoll, /* c poll */
+ ddi_prop_op, /* property ops */
+ &g_VBoxNetFltSolarisStreamTab,
+ D_NEW | D_MP | D_MTQPAIR | D_MTOUTPERIM | D_MTOCEXCL, /* compat. flag */
+ CB_REV /* revision */
+};
+
+/**
+ * dev_ops: driver entry/exit and other ops.
+ */
+static struct dev_ops g_VBoxNetFltSolarisDevOps =
+{
+ DEVO_REV, /* driver build revision */
+ 0, /* ref count */
+ VBoxNetFltSolarisGetInfo,
+ nulldev, /* identify */
+ nulldev, /* probe */
+ VBoxNetFltSolarisAttach,
+ VBoxNetFltSolarisDetach,
+ nodev, /* reset */
+ &g_VBoxNetFltSolarisCbOps,
+ (struct bus_ops *)0,
+ nodev, /* power */
+ VBoxNetFltSolarisQuiesceNotNeeded
+};
+
+/**
+ * modldrv: export driver specifics to kernel
+ */
+static struct modldrv g_VBoxNetFltSolarisDriver =
+{
+ &mod_driverops, /* extern from kernel */
+ DEVICE_DESC_DRV " " VBOX_VERSION_STRING "r" RT_XSTR(VBOX_SVN_REV),
+ &g_VBoxNetFltSolarisDevOps
+};
+
+/**
+ * fmodsw: streams module ops
+ */
+static struct fmodsw g_VBoxNetFltSolarisModOps =
+{
+ DEVICE_NAME,
+ &g_VBoxNetFltSolarisStreamTab,
+ D_NEW | D_MP | D_MTQPAIR | D_MTOUTPERIM | D_MTOCEXCL
+};
+
+/**
+ * modlstrmod: streams module specifics to kernel
+ */
+static struct modlstrmod g_VBoxNetFltSolarisModule =
+{
+ &mod_strmodops, /* extern from kernel */
+ DEVICE_DESC_MOD " " VBOX_VERSION_STRING "r" RT_XSTR(VBOX_SVN_REV),
+ &g_VBoxNetFltSolarisModOps
+};
+
+/**
+ * modlinkage: export install/remove/info to the kernel
+ */
+static struct modlinkage g_VBoxNetFltSolarisModLinkage =
+{
+ MODREV_1, /* loadable module system revision */
+ {
+ &g_VBoxNetFltSolarisDriver, /* streams driver framework */
+ &g_VBoxNetFltSolarisModule, /* streams module framework */
+ NULL /* terminate array of linkage structures */
+ }
+};
+
+struct vboxnetflt_state_t;
+
+/**
+ * vboxnetflt_dladdr_t: DL SAP address format
+ */
+typedef struct vboxnetflt_dladdr_t
+{
+ ether_addr_t Mac;
+ uint16_t SAP;
+} vboxnetflt_dladdr_t;
+
+#define VBOXNETFLT_DLADDRL sizeof(vboxnetflt_dladdr_t)
+
+/**
+ * which stream is this?
+ */
+typedef enum VBOXNETFLTSTREAMTYPE
+{
+ kUndefined = 0,
+ kIp4Stream = 0x1b,
+ kIp6Stream = 0xcc,
+ kArpStream = 0xab,
+ kPromiscStream = 0xdf
+} VBOXNETFLTSTREAMTYPE;
+
+/**
+ * loopback packet identifier
+ */
+typedef struct VBOXNETFLTPACKETID
+{
+ struct VBOXNETFLTPACKETID *pNext;
+ uint16_t cbPacket;
+ uint16_t Checksum;
+ RTMAC SrcMac;
+ RTMAC DstMac;
+} VBOXNETFLTPACKETID;
+typedef struct VBOXNETFLTPACKETID *PVBOXNETFLTPACKETID;
+
+/**
+ * vboxnetflt_stream_t: per-stream data (multiple streams per interface)
+ */
+typedef struct vboxnetflt_stream_t
+{
+ int DevMinor; /* minor device no. (for clone) */
+ queue_t *pReadQueue; /* read side queue */
+ struct vboxnetflt_stream_t *pNext; /* next stream in list */
+ PVBOXNETFLTINS volatile pThis; /* the backend instance */
+ VBOXNETFLTSTREAMTYPE Type; /* the type of the stream */
+} vboxnetflt_stream_t;
+
+/**
+ * vboxnetflt_promisc_stream_t: per-interface dedicated stream data
+ */
+typedef struct vboxnetflt_promisc_stream_t
+{
+ vboxnetflt_stream_t Stream; /* dedicated/promiscuous stream */
+ bool fPromisc; /* cached promiscuous value */
+ bool fRawMode; /* whether raw mode request was successful */
+ uint32_t ModeReqId; /* track MIOCTLs for swallowing our fake request acknowledgements */
+#ifdef VBOXNETFLT_SOLARIS_IPV6_POLLING
+ PRTTIMER pIp6Timer; /* ipv6 stream poll timer for dynamic ipv6 stream attachment */
+#endif
+ size_t cLoopback; /* loopback queue size list */
+ timeout_id_t volatile TimeoutId; /* timeout id of promisc. req */
+ PVBOXNETFLTPACKETID pHead; /* loopback packet identifier head */
+ PVBOXNETFLTPACKETID pTail; /* loopback packet identifier tail */
+} vboxnetflt_promisc_stream_t;
+
+typedef struct vboxnetflt_promisc_params_t
+{
+ PVBOXNETFLTINS pThis; /* the backend instance */
+ bool fPromiscOn; /* whether promiscuous req. on or off */
+} vboxnetflt_promisc_params_t;
+
+
+/*********************************************************************************************************************************
+* Internal Functions *
+*********************************************************************************************************************************/
+static int vboxNetFltSolarisSetRawMode(vboxnetflt_promisc_stream_t *pPromiscStream);
+/* static int vboxNetFltSolarisSetFastMode(queue_t *pQueue); */
+
+static int vboxNetFltSolarisPhysAddrReq(queue_t *pQueue);
+static void vboxNetFltSolarisCachePhysAddr(PVBOXNETFLTINS pThis, mblk_t *pPhysAddrAckMsg);
+static int vboxNetFltSolarisBindReq(queue_t *pQueue, int SAP);
+static int vboxNetFltSolarisNotifyReq(queue_t *pQueue);
+
+/* static int vboxNetFltSolarisUnitDataToRaw(PVBOXNETFLTINS pThis, mblk_t *pMsg, mblk_t **ppRawMsg); */
+static int vboxNetFltSolarisRawToUnitData(mblk_t *pMsg, mblk_t **ppDlpiMsg);
+
+static inline void vboxNetFltSolarisInitPacketId(PVBOXNETFLTPACKETID pTag, mblk_t *pMsg);
+static int vboxNetFltSolarisQueueLoopback(PVBOXNETFLTINS pThis, vboxnetflt_promisc_stream_t *pPromiscStream, mblk_t *pMsg);
+static bool vboxNetFltSolarisIsOurMBlk(PVBOXNETFLTINS pThis, vboxnetflt_promisc_stream_t *pPromiscStream, mblk_t *pMsg);
+
+static mblk_t *vboxNetFltSolarisMBlkFromSG(PVBOXNETFLTINS pThis, PINTNETSG pSG, uint32_t fDst);
+static unsigned vboxNetFltSolarisMBlkCalcSGSegs(PVBOXNETFLTINS pThis, mblk_t *pMsg);
+static int vboxNetFltSolarisMBlkToSG(PVBOXNETFLTINS pThis, mblk_t *pMsg, PINTNETSG pSG, unsigned cSegs, uint32_t fSrc);
+static int vboxNetFltSolarisRecv(PVBOXNETFLTINS pThis, vboxnetflt_stream_t *pStream, queue_t *pQueue, mblk_t *pMsg);
+/* static mblk_t *vboxNetFltSolarisFixChecksums(mblk_t *pMsg); */
+/* static void vboxNetFltSolarisAnalyzeMBlk(mblk_t *pMsg); */
+
+
+/*********************************************************************************************************************************
+* Global Variables *
+*********************************************************************************************************************************/
+/** Global device info handle. */
+static dev_info_t *g_pVBoxNetFltSolarisDip = NULL;
+
+/** The (common) global data. */
+static VBOXNETFLTGLOBALS g_VBoxNetFltSolarisGlobals;
+
+/** The list of all opened streams. */
+vboxnetflt_stream_t *g_VBoxNetFltSolarisStreams = NULL;
+
+/** Global mutex protecting open/close. */
+static RTSEMFASTMUTEX g_VBoxNetFltSolarisMtx = NIL_RTSEMFASTMUTEX;
+
+/** Global credentials using during open/close. */
+static cred_t *g_pVBoxNetFltSolarisCred = NULL;
+
+/**
+ * g_VBoxNetFltInstance is the current PVBOXNETFLTINS to be associated with the stream being created
+ * in ModOpen. This is just shared global data between the dynamic attach and the ModOpen procedure.
+ */
+PVBOXNETFLTINS volatile g_VBoxNetFltSolarisInstance = NULL;
+
+/** Goes along with the instance to determine type of stream being opened/created. */
+VBOXNETFLTSTREAMTYPE volatile g_VBoxNetFltSolarisStreamType = kUndefined;
+
+#ifdef VBOXNETFLT_SOLARIS_IPV6_POLLING
+/** Global IPv6 polling interval */
+static int g_VBoxNetFltSolarisPollInterval = -1;
+#endif
+
+static int s_off_vnode = -1;
+#define VNODE_FOR_FILE_T(filetpointer) (*(struct vnode **)((char *)(filetpointer) + s_off_vnode))
+
+
+static int
+vboxNetFltSolarisCtfGetMemberOffset(ctf_file_t *pCtfFile, const char *pszStruct, const char *pszMember, int *pOffset)
+{
+ AssertReturn(pCtfFile, VERR_INVALID_PARAMETER);
+ AssertReturn(pszStruct, VERR_INVALID_PARAMETER);
+ AssertReturn(pszMember, VERR_INVALID_PARAMETER);
+ AssertReturn(pOffset, VERR_INVALID_PARAMETER);
+
+ ctf_id_t TypeId = ctf_lookup_by_name(pCtfFile, pszStruct);
+ if (TypeId != CTF_ERR)
+ {
+ ctf_membinfo_t MemberInfo;
+ bzero(&MemberInfo, sizeof(MemberInfo));
+ if (ctf_member_info(pCtfFile, TypeId, pszMember, &MemberInfo) != CTF_ERR)
+ {
+ *pOffset = (MemberInfo.ctm_offset >> 3);
+ LogRel((DEVICE_NAME ":%s::%s at %d\n", pszStruct, pszMember, *pOffset));
+ return VINF_SUCCESS;
+ }
+ else
+ LogRel((DEVICE_NAME ":ctf_member_info failed for struct %s member %s\n", pszStruct, pszMember));
+ }
+ else
+ LogRel((DEVICE_NAME ":ctf_lookup_by_name failed for struct %s\n", pszStruct));
+
+ return VERR_NOT_FOUND;
+}
+
+
+static int
+vboxNetFltSolarisProbeCtf(void)
+{
+ /*
+ * CTF probing for fluid f_vnode member in file_t.
+ */
+ int rc = VERR_INTERNAL_ERROR;
+ modctl_t *pModCtl = mod_hold_by_name("genunix");
+ if (pModCtl)
+ {
+ int err;
+ mutex_enter(&mod_lock);
+ ctf_file_t *pCtfFile = ctf_modopen(pModCtl->mod_mp, &err);
+ mutex_exit(&mod_lock);
+ if (pCtfFile)
+ {
+ rc = vboxNetFltSolarisCtfGetMemberOffset(pCtfFile, "file_t", "f_vnode", &s_off_vnode);
+ ctf_close(pCtfFile);
+ }
+ else
+ LogRel((DEVICE_NAME ":ctf_modopen failed. err=%d\n", err));
+
+ mod_release_mod(pModCtl);
+ }
+ else
+ LogRel((DEVICE_NAME ":mod_hold_by_name failed.\n"));
+
+ return rc;
+}
+
+
+/**
+ * Kernel entry points
+ */
+int _init(void)
+{
+ LogFunc((DEVICE_NAME ":_init\n"));
+
+ /*
+ * Prevent module autounloading.
+ */
+ modctl_t *pModCtl = mod_getctl(&g_VBoxNetFltSolarisModLinkage);
+ if (pModCtl)
+ pModCtl->mod_loadflags |= MOD_NOAUTOUNLOAD;
+ else
+ LogRel((DEVICE_NAME ":failed to disable autounloading!\n"));
+
+ /*
+ * Initialize IPRT.
+ */
+ int rc = RTR0Init(0);
+ if (RT_SUCCESS(rc))
+ {
+ rc = vboxNetFltSolarisProbeCtf();
+ if (RT_SUCCESS(rc))
+ {
+ /*
+ * Initialize Solaris specific globals here.
+ */
+ g_VBoxNetFltSolarisStreams = NULL;
+ g_VBoxNetFltSolarisInstance = NULL;
+ g_pVBoxNetFltSolarisCred = crdup(kcred);
+ if (RT_LIKELY(g_pVBoxNetFltSolarisCred))
+ {
+ rc = RTSemFastMutexCreate(&g_VBoxNetFltSolarisMtx);
+ if (RT_SUCCESS(rc))
+ {
+ /*
+ * Initialize the globals and connect to the support driver.
+ *
+ * This will call back vboxNetFltOsOpenSupDrv (and maybe vboxNetFltOsCloseSupDrv)
+ * for establishing the connect to the support driver.
+ */
+ memset(&g_VBoxNetFltSolarisGlobals, 0, sizeof(g_VBoxNetFltSolarisGlobals));
+ rc = vboxNetFltInitGlobalsAndIdc(&g_VBoxNetFltSolarisGlobals);
+ if (RT_SUCCESS(rc))
+ {
+ rc = mod_install(&g_VBoxNetFltSolarisModLinkage);
+ if (!rc)
+ return rc;
+
+ LogRel((DEVICE_NAME ":mod_install failed. rc=%d\n", rc));
+ vboxNetFltTryDeleteIdcAndGlobals(&g_VBoxNetFltSolarisGlobals);
+ }
+ else
+ LogRel((DEVICE_NAME ":failed to initialize globals.\n"));
+
+ RTSemFastMutexDestroy(g_VBoxNetFltSolarisMtx);
+ g_VBoxNetFltSolarisMtx = NIL_RTSEMFASTMUTEX;
+ }
+ }
+ else
+ {
+ LogRel((DEVICE_NAME ":failed to allocate credentials.\n"));
+ rc = VERR_NO_MEMORY;
+ }
+ }
+ else
+ LogRel((DEVICE_NAME ":vboxNetFltSolarisProbeCtf failed. rc=%d\n", rc));
+
+ RTR0Term();
+ }
+ else
+ LogRel((DEVICE_NAME ":failed to initialize IPRT (rc=%d)\n", rc));
+
+ memset(&g_VBoxNetFltSolarisGlobals, 0, sizeof(g_VBoxNetFltSolarisGlobals));
+ return RTErrConvertToErrno(rc);
+}
+
+
+int _fini(void)
+{
+ int rc;
+ LogFunc((DEVICE_NAME ":_fini\n"));
+
+ /*
+ * Undo the work done during start (in reverse order).
+ */
+ rc = vboxNetFltTryDeleteIdcAndGlobals(&g_VBoxNetFltSolarisGlobals);
+ if (RT_FAILURE(rc))
+ {
+ LogRel((DEVICE_NAME ":_fini - busy!\n"));
+ return EBUSY;
+ }
+
+ rc = mod_remove(&g_VBoxNetFltSolarisModLinkage);
+ if (!rc)
+ {
+ if (g_pVBoxNetFltSolarisCred)
+ {
+ crfree(g_pVBoxNetFltSolarisCred);
+ g_pVBoxNetFltSolarisCred = NULL;
+ }
+
+ if (g_VBoxNetFltSolarisMtx != NIL_RTSEMFASTMUTEX)
+ {
+ RTSemFastMutexDestroy(g_VBoxNetFltSolarisMtx);
+ g_VBoxNetFltSolarisMtx = NIL_RTSEMFASTMUTEX;
+ }
+
+ RTR0Term();
+ }
+
+ return rc;
+}
+
+
+int _info(struct modinfo *pModInfo)
+{
+ LogFunc((DEVICE_NAME ":_info\n"));
+
+ int rc = mod_info(&g_VBoxNetFltSolarisModLinkage, pModInfo);
+
+ Log((DEVICE_NAME ":_info returns %d\n", rc));
+ return rc;
+}
+
+
+/**
+ * Attach entry point, to attach a device to the system or resume it.
+ *
+ * @param pDip The module structure instance.
+ * @param enmCmd Operation type (attach/resume).
+ *
+ * @returns corresponding solaris error code.
+ */
+static int VBoxNetFltSolarisAttach(dev_info_t *pDip, ddi_attach_cmd_t enmCmd)
+{
+ LogFunc((DEVICE_NAME ":VBoxNetFltSolarisAttach pDip=%p enmCmd=%d\n", pDip, enmCmd));
+
+ switch (enmCmd)
+ {
+ case DDI_ATTACH:
+ {
+ int rc = ddi_create_minor_node(pDip, DEVICE_NAME, S_IFCHR, 0 /* instance */, DDI_PSEUDO, CLONE_DEV);
+ if (rc == DDI_SUCCESS)
+ {
+ g_pVBoxNetFltSolarisDip = pDip;
+#ifdef VBOXNETFLT_SOLARIS_IPV6_POLLING
+ /*
+ * Get the user prop. for polling interval.
+ */
+ int Interval = ddi_getprop(DDI_DEV_T_ANY, pDip, DDI_PROP_DONTPASS, VBOXNETFLT_IP6POLLINTERVAL, -1 /* default */);
+ if (Interval == -1)
+ Log((DEVICE_NAME ":vboxNetFltSolarisSetupIp6Polling: no poll interval property specified. Skipping Ipv6 polling.\n"));
+ else if (Interval < 1 || Interval > 120)
+ {
+ LogRel((DEVICE_NAME ":vboxNetFltSolarisSetupIp6Polling: Invalid polling interval %d. Expected between 1 and 120 secs.\n",
+ Interval));
+ Interval = -1;
+ }
+
+ g_VBoxNetFltSolarisPollInterval = Interval;
+#endif
+ ddi_report_dev(pDip);
+ return DDI_SUCCESS;
+ }
+ else
+ LogRel((DEVICE_NAME ":VBoxNetFltSolarisAttach failed to create minor node. rc%d\n", rc));
+ return DDI_FAILURE;
+ }
+
+ case DDI_RESUME:
+ {
+ /* Nothing to do here... */
+ return DDI_SUCCESS;
+ }
+
+ /* case DDI_PM_RESUME: */
+ 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 Operation type (detach/suspend).
+ *
+ * @returns corresponding solaris error code.
+ */
+static int VBoxNetFltSolarisDetach(dev_info_t *pDip, ddi_detach_cmd_t enmCmd)
+{
+ LogFunc((DEVICE_NAME ":VBoxNetFltSolarisDetach pDip=%p enmCmd=%d\n", pDip, enmCmd));
+
+ switch (enmCmd)
+ {
+ case DDI_DETACH:
+ {
+ ddi_remove_minor_node(pDip, NULL);
+ return DDI_SUCCESS;
+ }
+
+ case DDI_RESUME:
+ {
+ /* Nothing to do here... */
+ return DDI_SUCCESS;
+ }
+
+ /* case DDI_PM_SUSPEND: */
+ /* case DDI_HOT_PLUG_DETACH: */
+ default:
+ return DDI_FAILURE;
+ }
+}
+
+
+/**
+ * Quiesce not-needed entry point, as Solaris 10 doesn't have any
+ * ddi_quiesce_not_needed() function.
+ *
+ * @param pDip The module structure instance.
+ *
+ * @return corresponding solaris error code.
+ */
+static int VBoxNetFltSolarisQuiesceNotNeeded(dev_info_t *pDip)
+{
+ 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.
+ *
+ * @returns corresponding solaris error code.
+ */
+static int VBoxNetFltSolarisGetInfo(dev_info_t *pDip, ddi_info_cmd_t enmCmd, void *pvArg, void **ppvResult)
+{
+ LogFunc((DEVICE_NAME ":VBoxNetFltSolarisGetInfo pDip=%p enmCmd=%d pArg=%p instance=%d\n", pDip, enmCmd,
+ getminor((dev_t)pvArg)));
+
+ switch (enmCmd)
+ {
+ case DDI_INFO_DEVT2DEVINFO:
+ {
+ *ppvResult = g_pVBoxNetFltSolarisDip;
+ return *ppvResult ? DDI_SUCCESS : DDI_FAILURE;
+ }
+
+ case DDI_INFO_DEVT2INSTANCE:
+ {
+ /* There can only be a single-instance of this driver and thus its instance number is 0. */
+ *ppvResult = (void *)0;
+ return DDI_SUCCESS;
+ }
+ }
+
+ return DDI_FAILURE;
+}
+
+
+/**
+ * Stream module open entry point, initializes the queue and allows streams processing.
+ *
+ * @param pQueue Pointer to the read queue (cannot be NULL).
+ * @param pDev Pointer to the dev_t associated with the driver at the end of the stream.
+ * @param fOpenMode Open mode (always 0 for streams driver, thus ignored).
+ * @param fStreamMode Stream open mode.
+ * @param pCred Pointer to user credentials.
+ *
+ * @returns corresponding solaris error code.
+ */
+static int VBoxNetFltSolarisModOpen(queue_t *pQueue, dev_t *pDev, int fOpenMode, int fStreamMode, cred_t *pCred)
+{
+ Assert(pQueue);
+
+ LogFunc((DEVICE_NAME ":VBoxNetFltSolarisModOpen pQueue=%p pDev=%p fOpenMode=%d fStreamMode=%d\n", pQueue, pDev,
+ fOpenMode, fStreamMode));
+
+ /*
+ * Already open?
+ */
+ if (pQueue->q_ptr)
+ {
+ LogRel((DEVICE_NAME ":VBoxNetFltSolarisModOpen invalid open.\n"));
+ return ENOENT;
+ }
+
+ /*
+ * Check that the request was initiated by our code.
+ *
+ * This ASSUMES that crdup() will return a copy with a unique address and
+ * not do any kind of clever pooling. This check will when combined with
+ * g_VBoxNetFltSolarisMtx prevent races and that the instance gets
+ * associated with the wrong streams.
+ */
+ if (pCred != g_pVBoxNetFltSolarisCred)
+ {
+ LogRel((DEVICE_NAME ":VBoxNetFltSolarisModOpen invalid credentials.\n"));
+ return EACCES;
+ }
+
+ /*
+ * Check for the VirtualBox instance.
+ */
+ PVBOXNETFLTINS pThis = g_VBoxNetFltSolarisInstance;
+ if (!pThis)
+ {
+ LogRel((DEVICE_NAME ":VBoxNetFltSolarisModOpen failed to get VirtualBox instance.\n"));
+ return ENOENT;
+ }
+
+ /*
+ * Check VirtualBox stream type.
+ */
+ if ( g_VBoxNetFltSolarisStreamType != kPromiscStream
+ && g_VBoxNetFltSolarisStreamType != kArpStream
+ && g_VBoxNetFltSolarisStreamType != kIp6Stream
+ && g_VBoxNetFltSolarisStreamType != kIp4Stream)
+ {
+ LogRel((DEVICE_NAME ":VBoxNetFltSolarisModOpen failed due to undefined VirtualBox open mode. Type=%d\n",
+ g_VBoxNetFltSolarisStreamType));
+ return ENOENT;
+ }
+
+ /*
+ * Get minor number. For clone opens provide a new dev_t.
+ */
+ minor_t DevMinor = 0;
+ vboxnetflt_stream_t *pStream = NULL;
+ vboxnetflt_stream_t **ppPrevStream = &g_VBoxNetFltSolarisStreams;
+ if (fStreamMode == CLONEOPEN)
+ {
+ for (; (pStream = *ppPrevStream) != NULL; ppPrevStream = &pStream->pNext)
+ {
+ if (DevMinor < pStream->DevMinor)
+ break;
+ DevMinor++;
+ }
+ *pDev = makedevice(getmajor(*pDev), DevMinor);
+ }
+ else
+ DevMinor = getminor(*pDev);
+
+ if (g_VBoxNetFltSolarisStreamType == kPromiscStream)
+ {
+ vboxnetflt_promisc_stream_t *pPromiscStream = RTMemAlloc(sizeof(vboxnetflt_promisc_stream_t));
+ if (RT_UNLIKELY(!pPromiscStream))
+ {
+ LogRel((DEVICE_NAME ":VBoxNetFltSolarisModOpen failed to allocate promiscuous stream data.\n"));
+ return ENOMEM;
+ }
+
+ pPromiscStream->fPromisc = false;
+ pPromiscStream->fRawMode = false;
+ pPromiscStream->ModeReqId = 0;
+ pPromiscStream->pHead = NULL;
+ pPromiscStream->pTail = NULL;
+ pPromiscStream->cLoopback = 0;
+ pPromiscStream->TimeoutId = 0;
+#ifdef VBOXNETFLT_SOLARIS_IPV6_POLLING
+ pPromiscStream->pIp6Timer = NULL;
+#endif
+ pStream = (vboxnetflt_stream_t *)pPromiscStream;
+ }
+ else
+ {
+ /*
+ * Allocate & initialize per-stream data. Hook it into the (read and write) queue's module specific data.
+ */
+ pStream = RTMemAlloc(sizeof(vboxnetflt_stream_t));
+ if (RT_UNLIKELY(!pStream))
+ {
+ LogRel((DEVICE_NAME ":VBoxNetFltSolarisModOpen failed to allocate stream data.\n"));
+ return ENOMEM;
+ }
+ }
+ pStream->DevMinor = DevMinor;
+ pStream->pReadQueue = pQueue;
+
+ /*
+ * Pick up the current global VBOXNETFLTINS instance as
+ * the one that we will associate this stream with.
+ */
+ ASMAtomicUoWritePtr(&pStream->pThis, pThis);
+ pStream->Type = g_VBoxNetFltSolarisStreamType;
+ switch (pStream->Type)
+ {
+ case kIp4Stream: ASMAtomicUoWritePtr((void**)&pThis->u.s.pIp4Stream, pStream); break;
+ case kIp6Stream: ASMAtomicUoWritePtr((void**)&pThis->u.s.pIp6Stream, pStream); break;
+ case kArpStream: ASMAtomicUoWritePtr((void**)&pThis->u.s.pArpStream, pStream); break;
+ case kPromiscStream: ASMAtomicUoWritePtr((void**)&pThis->u.s.pPromiscStream, pStream); break;
+ default: /* Heh. */
+ {
+ LogRel((DEVICE_NAME ":VBoxNetFltSolarisModOpen huh!? Invalid stream type %d\n", pStream->Type));
+ RTMemFree(pStream);
+ return EINVAL;
+ }
+ }
+
+ pQueue->q_ptr = pStream;
+ WR(pQueue)->q_ptr = pStream;
+
+ /*
+ * Link it to the list of streams.
+ */
+ pStream->pNext = *ppPrevStream;
+ *ppPrevStream = pStream;
+
+ /*
+ * Increment IntNet reference count for this stream.
+ */
+ vboxNetFltRetain(pThis, false /* fBusy */);
+
+ qprocson(pQueue);
+
+ /*
+ * Don't hold the spinlocks across putnext calls as it could
+ * (and does mostly) re-enter the put procedure on the same thread.
+ */
+ if (pStream->Type == kPromiscStream)
+ {
+ vboxnetflt_promisc_stream_t *pPromiscStream = (vboxnetflt_promisc_stream_t *)pStream;
+
+ /*
+ * Bind to SAP 0 (DL_ETHER).
+ * Note: We don't support DL_TPR (token passing ring) SAP as that is unnecessary asynchronous
+ * work to get DL_INFO_REQ acknowledgements and determine SAP based on the Mac Type etc.
+ * Besides TPR doesn't really exist anymore practically as far as I know.
+ */
+ int rc = vboxNetFltSolarisBindReq(pStream->pReadQueue, 0 /* SAP */);
+ if (RT_LIKELY(RT_SUCCESS(rc)))
+ {
+ /*
+ * Request the physical address (we cache the acknowledgement).
+ */
+ rc = vboxNetFltSolarisPhysAddrReq(pStream->pReadQueue);
+ if (RT_LIKELY(RT_SUCCESS(rc)))
+ {
+ /*
+ * Ask for DLPI link notifications, don't bother check for errors here.
+ */
+ vboxNetFltSolarisNotifyReq(pStream->pReadQueue);
+
+ /*
+ * Enable raw mode.
+ */
+ rc = vboxNetFltSolarisSetRawMode(pPromiscStream);
+ if (RT_FAILURE(rc))
+ LogRel((DEVICE_NAME ":vboxNetFltSolarisSetRawMode failed rc=%Rrc.\n", rc));
+ }
+ else
+ LogRel((DEVICE_NAME ":vboxNetFltSolarisSetRawMode failed rc=%Rrc.\n", rc));
+ }
+ else
+ LogRel((DEVICE_NAME ":vboxNetFltSolarisBindReq failed rc=%Rrc.\n", rc));
+ }
+
+ NOREF(fOpenMode);
+
+ Log((DEVICE_NAME ":VBoxNetFltSolarisModOpen returns 0, DevMinor=%d pQueue=%p\n", DevMinor, pStream->pReadQueue));
+
+ return 0;
+}
+
+
+/**
+ * Stream module close entry point, undoes the work done on open and closes the stream.
+ *
+ * @param pQueue Pointer to the read queue (cannot be NULL).
+ * @param fOpenMode Open mode (always 0 for streams driver, thus ignored).
+ * @param pCred Pointer to user credentials.
+ *
+ * @returns corresponding solaris error code.
+ */
+static int VBoxNetFltSolarisModClose(queue_t *pQueue, int fOpenMode, cred_t *pCred)
+{
+ Assert(pQueue);
+
+ LogFunc((DEVICE_NAME ":VBoxNetFltSolarisModClose pQueue=%p fOpenMode=%d\n", pQueue, fOpenMode));
+
+ vboxnetflt_stream_t *pStream = NULL;
+ vboxnetflt_stream_t **ppPrevStream = NULL;
+
+ /*
+ * Get instance data.
+ */
+ pStream = (vboxnetflt_stream_t *)pQueue->q_ptr;
+ if (RT_UNLIKELY(!pStream))
+ {
+ LogRel((DEVICE_NAME ":VBoxNetFltSolarisModClose failed to get stream.\n"));
+ return ENXIO;
+ }
+
+ if (pStream->Type == kPromiscStream)
+ {
+ /*
+ * If there are any timeout scheduled, we need to make sure they are cancelled.
+ */
+ vboxnetflt_promisc_stream_t *pPromiscStream = (vboxnetflt_promisc_stream_t *)pStream;
+ timeout_id_t TimeoutId = ASMAtomicReadPtr(&pPromiscStream->TimeoutId);
+ if (TimeoutId)
+ {
+ quntimeout(WR(pPromiscStream->Stream.pReadQueue), TimeoutId);
+ ASMAtomicWritePtr(&pPromiscStream->TimeoutId, NULL);
+ }
+
+ flushq(pQueue, FLUSHALL);
+ flushq(WR(pQueue), FLUSHALL);
+ }
+
+ qprocsoff(pQueue);
+
+ if (pStream->Type == kPromiscStream)
+ {
+ vboxnetflt_promisc_stream_t *pPromiscStream = (vboxnetflt_promisc_stream_t *)pStream;
+
+ mutex_enter(&pStream->pThis->u.s.hMtx);
+
+ /*
+ * Free-up loopback buffers.
+ */
+ PVBOXNETFLTPACKETID pCur = pPromiscStream->pHead;
+ while (pCur)
+ {
+ PVBOXNETFLTPACKETID pNext = pCur->pNext;
+ RTMemFree(pCur);
+ pCur = pNext;
+ }
+ pPromiscStream->pHead = NULL;
+ pPromiscStream->pTail = NULL;
+ pPromiscStream->cLoopback = 0;
+
+#ifdef VBOXNETFLT_SOLARIS_IPV6_POLLING
+ /*
+ * Sheer paranoia.
+ */
+ if (pPromiscStream->pIp6Timer != NULL)
+ {
+ RTTimerStop(pPromiscStream->pIp6Timer);
+ RTTimerDestroy(pPromiscStream->pIp6Timer);
+ ASMAtomicUoWriteNullPtr(&pPromiscStream->pIp6Timer);
+ }
+#endif
+
+ mutex_exit(&pStream->pThis->u.s.hMtx);
+ }
+
+ /*
+ * Unlink it from the list of streams.
+ */
+ for (ppPrevStream = &g_VBoxNetFltSolarisStreams; (pStream = *ppPrevStream) != NULL; ppPrevStream = &pStream->pNext)
+ if (pStream == (vboxnetflt_stream_t *)pQueue->q_ptr)
+ break;
+ *ppPrevStream = pStream->pNext;
+
+ /*
+ * Delete the stream.
+ */
+ switch (pStream->Type)
+ {
+ case kIp4Stream: ASMAtomicUoWriteNullPtr(&pStream->pThis->u.s.pIp4Stream); break;
+ case kIp6Stream: ASMAtomicUoWriteNullPtr(&pStream->pThis->u.s.pIp6Stream); break;
+ case kArpStream: ASMAtomicUoWriteNullPtr(&pStream->pThis->u.s.pArpStream); break;
+ case kPromiscStream: ASMAtomicUoWriteNullPtr(&pStream->pThis->u.s.pPromiscStream); break;
+ default: /* Heh. */
+ {
+ AssertRelease(pStream->Type);
+ break;
+ }
+ }
+
+ /*
+ * Decrement IntNet reference count for this stream.
+ */
+ vboxNetFltRelease(pStream->pThis, false /* fBusy */);
+
+ RTMemFree(pStream);
+ pQueue->q_ptr = NULL;
+ WR(pQueue)->q_ptr = NULL;
+
+ NOREF(fOpenMode);
+ NOREF(pCred);
+
+ return 0;
+}
+
+
+/**
+ * Read side put procedure for processing messages in the read queue.
+ * All streams, bound and unbound share this read procedure.
+ *
+ * @param pQueue Pointer to the read queue.
+ * @param pMsg Pointer to the message.
+ *
+ * @returns corresponding solaris error code.
+ */
+static int VBoxNetFltSolarisModReadPut(queue_t *pQueue, mblk_t *pMsg)
+{
+ if (!pMsg)
+ return 0;
+
+ LogFunc((DEVICE_NAME ":VBoxNetFltSolarisModReadPut pQueue=%p pMsg=%p\n", pQueue, pMsg));
+
+ bool fSendUpstream = true;
+ vboxnetflt_stream_t *pStream = pQueue->q_ptr;
+ PVBOXNETFLTINS pThis = NULL;
+
+ /*
+ * In the unlikely case where VirtualBox crashed and this filter
+ * is somehow still in the host stream we must try not to panic the host.
+ */
+ if ( pStream
+ && pStream->Type == kPromiscStream)
+ {
+ fSendUpstream = false;
+ pThis = ASMAtomicUoReadPtrT(&pStream->pThis, PVBOXNETFLTINS);
+ if (RT_LIKELY(pThis))
+ {
+ /*
+ * Retain the instance if we're filtering regardless of we are active or not
+ * The reason being even when we are inactive we reference the instance (e.g
+ * the promiscuous OFF acknowledgement case).
+ */
+ RTSpinlockAcquire(pThis->hSpinlock);
+ const bool fActive = pThis->enmTrunkState == INTNETTRUNKIFSTATE_ACTIVE;
+ vboxNetFltRetain(pThis, true /* fBusy */);
+ RTSpinlockRelease(pThis->hSpinlock);
+
+ vboxnetflt_promisc_stream_t *pPromiscStream = (vboxnetflt_promisc_stream_t *)pStream;
+
+ switch (DB_TYPE(pMsg))
+ {
+ case M_DATA:
+ {
+ Log((DEVICE_NAME ":VBoxNetFltSolarisModReadPut M_DATA\n"));
+
+ if ( fActive
+ && pPromiscStream->fRawMode)
+ {
+ vboxNetFltSolarisRecv(pThis, pStream, pQueue, pMsg);
+ }
+ break;
+ }
+
+ case M_PROTO:
+ case M_PCPROTO:
+ {
+ union DL_primitives *pPrim = (union DL_primitives *)pMsg->b_rptr;
+ t_uscalar_t Prim = pPrim->dl_primitive;
+
+ Log((DEVICE_NAME ":VBoxNetFltSolarisModReadPut: M_PCPROTO %d\n", Prim));
+ switch (Prim)
+ {
+ case DL_NOTIFY_IND:
+ {
+ if (MBLKL(pMsg) < DL_NOTIFY_IND_SIZE)
+ {
+ LogRel((DEVICE_NAME ":VBoxNetFltSolarisModReadPut: Invalid notification size; expected>=%d"
+ " got=%d\n", DL_NOTIFY_IND_SIZE, MBLKL(pMsg)));
+ break;
+ }
+
+ dl_notify_ind_t *pNotifyInd = (dl_notify_ind_t *)pMsg->b_rptr;
+ switch (pNotifyInd->dl_notification)
+ {
+ case DL_NOTE_PHYS_ADDR:
+ {
+ if (pNotifyInd->dl_data != DL_CURR_PHYS_ADDR)
+ break;
+
+ size_t cOffset = pNotifyInd->dl_addr_offset;
+ size_t cbAddr = pNotifyInd->dl_addr_length;
+
+ if (!cOffset || !cbAddr)
+ {
+ LogRel((DEVICE_NAME ":VBoxNetFltSolarisModReadPut: DL_NOTE_PHYS_ADDR."
+ "Invalid offset/addr.\n"));
+ fSendUpstream = false;
+ break;
+ }
+
+ bcopy(pMsg->b_rptr + cOffset, &pThis->u.s.MacAddr, sizeof(pThis->u.s.MacAddr));
+ Log((DEVICE_NAME ":VBoxNetFltSolarisModReadPut: DL_NOTE_PHYS_ADDR. New Mac=%.*Rhxs\n",
+ sizeof(pThis->u.s.MacAddr), &pThis->u.s.MacAddr));
+ break;
+ }
+
+ case DL_NOTE_LINK_UP:
+ {
+ if (ASMAtomicXchgBool(&pThis->fDisconnectedFromHost, false))
+ Log((DEVICE_NAME ":VBoxNetFltSolarisModReadPut: DL_NOTE_LINK_UP.\n"));
+ break;
+ }
+
+ case DL_NOTE_LINK_DOWN:
+ {
+ if (!ASMAtomicXchgBool(&pThis->fDisconnectedFromHost, true))
+ Log((DEVICE_NAME ":VBoxNetFltSolarisModReadPut: DL_NOTE_LINK_DOWN.\n"));
+ break;
+ }
+ }
+ break;
+ }
+
+ case DL_BIND_ACK:
+ {
+ /*
+ * Swallow our bind request acknowledgement.
+ */
+ Log((DEVICE_NAME ":VBoxNetFltSolarisModReadPut: DL_BIND_ACK. Bound to requested SAP!\n"));
+ break;
+ }
+
+ case DL_PHYS_ADDR_ACK:
+ {
+ /*
+ * Swallow our physical address request acknowledgement.
+ */
+ vboxNetFltSolarisCachePhysAddr(pThis, pMsg);
+ break;
+ }
+
+ case DL_OK_ACK:
+ {
+ /*
+ * Swallow our fake promiscuous request acknowledgement.
+ */
+ dl_ok_ack_t *pOkAck = (dl_ok_ack_t *)pMsg->b_rptr;
+ if (pOkAck->dl_correct_primitive == DL_PROMISCON_REQ)
+ {
+ Log((DEVICE_NAME ":VBoxNetFltSolarisModReadPut: M_PCPROTO: DL_OK_ACK: fPromisc is ON.\n"));
+ pPromiscStream->fPromisc = true;
+ }
+ else if (pOkAck->dl_correct_primitive == DL_PROMISCOFF_REQ)
+ {
+ Log((DEVICE_NAME ":VBoxNetFltSolarisModReadPut: M_PCPROTO: DL_OK_ACK: fPromisc is OFF.\n"));
+ pPromiscStream->fPromisc = false;
+ }
+ break;
+ }
+ }
+ break;
+ }
+
+ case M_IOCACK:
+ {
+ /*
+ * Swallow our fake raw/fast path mode request acknowledgement.
+ */
+ struct iocblk *pIOC = (struct iocblk *)pMsg->b_rptr;
+ if (pIOC->ioc_id == pPromiscStream->ModeReqId)
+ {
+ pPromiscStream->fRawMode = true;
+ Log((DEVICE_NAME ":VBoxNetFltSolarisModReadPut: Mode acknowledgement. RawMode is %s\n",
+ pPromiscStream->fRawMode ? "ON" : "OFF"));
+ }
+ break;
+ }
+
+ case M_IOCNAK:
+ {
+ /*
+ * Swallow our fake raw/fast path mode request not acknowledged.
+ */
+ struct iocblk *pIOC = (struct iocblk *)pMsg->b_rptr;
+ if (pIOC->ioc_id == pPromiscStream->ModeReqId)
+ {
+ pPromiscStream->fRawMode = false;
+ LogRel((DEVICE_NAME ":VBoxNetFltSolarisModReadPut: WARNING! Mode not acknowledged. RawMode is %s\n",
+ pPromiscStream->fRawMode ? "ON" : "OFF"));
+ }
+ break;
+ }
+
+ case M_FLUSH:
+ {
+ /*
+ * We must support flushing queues.
+ */
+ Log((DEVICE_NAME ":VBoxNetFltSolarisModReadPut: M_FLUSH\n"));
+ if (*pMsg->b_rptr & FLUSHR)
+ flushq(pQueue, FLUSHALL);
+ break;
+ }
+ }
+
+ vboxNetFltRelease(pThis, true /* fBusy */);
+ }
+ else
+ LogRel((DEVICE_NAME ":VBoxNetFltSolarisModReadPut: Could not find VirtualBox instance!!\n"));
+ }
+
+ if (fSendUpstream)
+ {
+ /*
+ * Don't queue up things here, can cause bad things to happen when the system
+ * is under heavy loads and we need to jam across high priority messages which
+ * if it's not done properly will end up in an infinite loop.
+ */
+ putnext(pQueue, pMsg);
+ }
+ else
+ {
+ /*
+ * We need to free up the message if we don't pass it through.
+ */
+ freemsg(pMsg);
+ }
+
+ return 0;
+}
+
+
+/**
+ * Write side put procedure for processing messages in the write queue.
+ * All streams, bound and unbound share this write procedure.
+ *
+ * @param pQueue Pointer to the write queue.
+ * @param pMsg Pointer to the message.
+ *
+ * @returns corresponding solaris error code.
+ */
+static int VBoxNetFltSolarisModWritePut(queue_t *pQueue, mblk_t *pMsg)
+{
+ LogFunc((DEVICE_NAME ":VBoxNetFltSolarisModWritePut pQueue=%p pMsg=%p\n", pQueue, pMsg));
+
+ putnext(pQueue, pMsg);
+ return 0;
+}
+
+
+/**
+ * Put the stream in raw mode.
+ *
+ * @returns VBox status code.
+ * @param pPromiscStream Pointer to the read queue.
+ */
+static int vboxNetFltSolarisSetRawMode(vboxnetflt_promisc_stream_t *pPromiscStream)
+{
+ LogFunc((DEVICE_NAME ":vboxNetFltSolarisSetRawMode pPromiscStream=%p\n", pPromiscStream));
+
+ mblk_t *pRawMsg = NULL;
+ pRawMsg = mkiocb(DLIOCRAW);
+ if (RT_UNLIKELY(!pRawMsg))
+ return VERR_NO_MEMORY;
+
+ queue_t *pQueue = pPromiscStream->Stream.pReadQueue;
+ if (!pQueue)
+ return VERR_INVALID_POINTER;
+
+ struct iocblk *pIOC = (struct iocblk *)pRawMsg->b_rptr;
+ pPromiscStream->ModeReqId = pIOC->ioc_id;
+ pIOC->ioc_count = 0;
+
+ qreply(pQueue, pRawMsg);
+ return VINF_SUCCESS;
+}
+
+
+#if 0
+/**
+ * Put the stream back in fast path mode.
+ *
+ * @returns VBox status code.
+ * @param pQueue Pointer to the read queue.
+ */
+static int vboxNetFltSolarisSetFastMode(queue_t *pQueue)
+{
+ LogFunc((DEVICE_NAME ":vboxNetFltSolarisSetFastMode pQueue=%p\n", pQueue));
+
+ mblk_t *pFastMsg = mkiocb(DL_IOC_HDR_INFO);
+ if (RT_UNLIKELY(!pFastMsg))
+ return VERR_NO_MEMORY;
+
+ vboxnetflt_stream_t *pStream = pQueue->q_ptr;
+ struct iocblk *pIOC = (struct iocblk *)pFastMsg->b_rptr;
+ pStream->ModeReqId = pIOC->ioc_id;
+
+ size_t cbReq = sizeof(dl_unitdata_req_t) + sizeof(vboxnetflt_dladdr_t);
+ mblk_t *pDataReqMsg = allocb(cbReq, BPRI_MED);
+ if (RT_UNLIKELY(!pDataReqMsg))
+ return VERR_NO_MEMORY;
+
+ DB_TYPE(pDataReqMsg) = M_PROTO;
+ dl_unitdata_req_t *pDataReq = (dl_unitdata_req_t *)pDataReqMsg->b_rptr;
+ pDataReq->dl_primitive = DL_UNITDATA_REQ;
+ pDataReq->dl_dest_addr_length = sizeof(vboxnetflt_dladdr_t);
+ pDataReq->dl_dest_addr_offset = sizeof(dl_unitdata_req_t);
+ pDataReq->dl_priority.dl_min = 0;
+ pDataReq->dl_priority.dl_max = 0;
+
+ bzero(pDataReqMsg->b_rptr + sizeof(dl_unitdata_req_t), sizeof(vboxnetflt_dladdr_t));
+ pDataReqMsg->b_wptr = pDataReqMsg->b_rptr + cbReq;
+
+ /*
+ * Link the data format request message into the header ioctl message.
+ */
+ pFastMsg->b_cont = pDataReqMsg;
+ pIOC->ioc_count = msgdsize(pDataReqMsg);
+
+ qreply(pQueue, pFastMsg);
+ return VINF_SUCCESS;
+}
+#endif
+
+
+/**
+ * Callback function for qwriter to send promiscuous request messages
+ * downstream.
+ *
+ * @param pQueue Pointer to the write queue.
+ * @param fPromisc Whether to send promiscuous ON or OFF requests.
+ *
+ * @returns VBox status code.
+ */
+static int vboxNetFltSolarisPromiscReq(queue_t *pQueue, bool fPromisc)
+{
+ LogFunc((DEVICE_NAME ":vboxNetFltSolarisPromiscReq pQueue=%p fPromisc=%d\n", pQueue, fPromisc));
+
+ t_uscalar_t Cmd;
+ size_t cbReq = 0;
+ if (fPromisc)
+ {
+ Cmd = DL_PROMISCON_REQ;
+ cbReq = DL_PROMISCON_REQ_SIZE;
+ }
+ else
+ {
+ Cmd = DL_PROMISCOFF_REQ;
+ cbReq = DL_PROMISCOFF_REQ_SIZE;
+ }
+
+ mblk_t *pPromiscPhysMsg = mexchange(NULL, NULL, cbReq, M_PROTO, Cmd);
+ if (RT_UNLIKELY(!pPromiscPhysMsg))
+ return VERR_NO_MEMORY;
+
+ mblk_t *pPromiscSapMsg = mexchange(NULL, NULL, cbReq, M_PROTO, Cmd);
+ if (RT_UNLIKELY(!pPromiscSapMsg))
+ {
+ freemsg(pPromiscPhysMsg);
+ return VERR_NO_MEMORY;
+ }
+
+ if (fPromisc)
+ {
+ ((dl_promiscon_req_t *)pPromiscPhysMsg->b_rptr)->dl_level = DL_PROMISC_PHYS;
+ ((dl_promiscon_req_t *)pPromiscSapMsg->b_rptr)->dl_level = DL_PROMISC_SAP;
+ }
+ else
+ {
+ ((dl_promiscoff_req_t *)pPromiscPhysMsg->b_rptr)->dl_level = DL_PROMISC_PHYS;
+ ((dl_promiscoff_req_t *)pPromiscSapMsg->b_rptr)->dl_level = DL_PROMISC_SAP;
+ }
+
+ putnext(pQueue, pPromiscPhysMsg);
+ putnext(pQueue, pPromiscSapMsg);
+
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * Callback wrapper for qwriter() to safely send promiscuous requests. This is
+ * called at the outer perimeter with exclusive lock held.
+ *
+ * @param pQueue Pointer to the write queue.
+ * @param pMsg A one byte message indicates a Promisc ON, otherwise
+ * a promiscuous OFF request. See
+ * vboxNetFltSolarisPromiscReqWrap().
+ */
+static void vboxNetFltSolarisPromiscReqWrapExcl(queue_t *pQueue, mblk_t *pMsg)
+{
+ /*
+ * Paranoia.
+ */
+ AssertReturnVoid(pQueue);
+ if (RT_UNLIKELY(!pMsg))
+ LogRel((DEVICE_NAME ":VBoxNetFltSolarisPromiscReqWrapExcl pQueue=%p missing message!\n", pQueue));
+
+ bool fPromisc = (MBLKL(pMsg) == 1);
+ freemsg(pMsg);
+ pMsg = NULL;
+ int rc = vboxNetFltSolarisPromiscReq(pQueue, fPromisc);
+ if (RT_FAILURE(rc))
+ LogRel((DEVICE_NAME ":VBoxNetFltSolarisPromiscReqWrapExcl vboxNetFltSolarisPromiscReq failed. rc=%d\n", rc));
+}
+
+
+/**
+ * Callback wrapper for qtimeout() to safely send promiscuous requests. This is
+ * called at the inner perimeter with shared lock.
+ *
+ * @param pvData Pointer to vboxnetflt_promisc_params_t. See
+ * vboxNetFltPortOsSetActive().
+ */
+static void vboxNetFltSolarisPromiscReqWrap(void *pvData)
+{
+ vboxnetflt_promisc_params_t *pParams = pvData;
+ if (RT_LIKELY(pParams))
+ {
+ PVBOXNETFLTINS pThis = pParams->pThis;
+ vboxnetflt_promisc_stream_t *pPromiscStream = ASMAtomicUoReadPtrT(&pThis->u.s.pPromiscStream,
+ vboxnetflt_promisc_stream_t *);
+ if ( pPromiscStream
+ && pPromiscStream->Stream.pReadQueue)
+ {
+ /*
+ * Use size of message to indicate to qwriter callback whether it must send
+ * promiscuous On or Off messages. This is ugly but easier and more efficient than
+ * scheduling two separate qwriter callbacks with prepared messages to putnext.
+ */
+ size_t cbMsg = pParams->fPromiscOn ? 1 : 2;
+ mblk_t *pMsg = allocb(cbMsg, BPRI_HI);
+ if (RT_UNLIKELY(!pMsg))
+ {
+ LogRel((DEVICE_NAME ":Failed to alloc message of %u bytes\n", cbMsg));
+ return;
+ }
+
+ /*
+ * Move the data pointer so we can use MBLKL, as MBLKSIZE gets the db_lim which is
+ * always aligned.
+ */
+ pMsg->b_wptr += cbMsg;
+
+ /*
+ * Upgrade inner perimeter lock to exclusive outer perimeter lock and
+ * then call putnext while we are at the outer perimeter.
+ */
+ qwriter(WR(pPromiscStream->Stream.pReadQueue), pMsg, vboxNetFltSolarisPromiscReqWrapExcl, PERIM_OUTER);
+ ASMAtomicWritePtr(&pPromiscStream->TimeoutId, NULL);
+ }
+ RTMemFree(pParams);
+ }
+}
+
+
+/**
+ * Send a fake physical address request downstream.
+ *
+ * @returns VBox status code.
+ * @param pQueue Pointer to the read queue.
+ */
+static int vboxNetFltSolarisPhysAddrReq(queue_t *pQueue)
+{
+ LogFunc((DEVICE_NAME ":vboxNetFltSolarisPhysAddrReq pQueue=%p\n", pQueue));
+
+ t_uscalar_t Cmd = DL_PHYS_ADDR_REQ;
+ size_t cbReq = DL_PHYS_ADDR_REQ_SIZE;
+ mblk_t *pPhysAddrMsg = mexchange(NULL, NULL, cbReq, M_PROTO, Cmd);
+ if (RT_UNLIKELY(!pPhysAddrMsg))
+ return VERR_NO_MEMORY;
+
+ dl_phys_addr_req_t *pPhysAddrReq = (dl_phys_addr_req_t *)pPhysAddrMsg->b_rptr;
+ pPhysAddrReq->dl_addr_type = DL_CURR_PHYS_ADDR;
+
+ qreply(pQueue, pPhysAddrMsg);
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * Cache the MAC address into the VirtualBox instance given a physical
+ * address acknowledgement message.
+ *
+ * @param pThis The instance.
+ * @param pMsg Pointer to the physical address acknowledgement message.
+ */
+static void vboxNetFltSolarisCachePhysAddr(PVBOXNETFLTINS pThis, mblk_t *pMsg)
+{
+ LogFunc((DEVICE_NAME ":vboxNetFltSolarisCachePhysAddr pThis=%p pMsg=%p\n", pThis, pMsg));
+
+ AssertCompile(sizeof(RTMAC) == ETHERADDRL);
+ dl_phys_addr_ack_t *pPhysAddrAck = (dl_phys_addr_ack_t *)pMsg->b_rptr;
+ if (pPhysAddrAck->dl_addr_length == sizeof(pThis->u.s.MacAddr))
+ {
+ bcopy(pMsg->b_rptr + pPhysAddrAck->dl_addr_offset, &pThis->u.s.MacAddr, sizeof(pThis->u.s.MacAddr));
+
+ Log((DEVICE_NAME ":vboxNetFltSolarisCachePhysAddr: DL_PHYS_ADDR_ACK: Mac=%.*Rhxs\n",
+ sizeof(pThis->u.s.MacAddr), &pThis->u.s.MacAddr));
+
+ if (vboxNetFltTryRetainBusyNotDisconnected(pThis))
+ {
+ Assert(pThis->pSwitchPort);
+ if (pThis->pSwitchPort)
+ pThis->pSwitchPort->pfnReportMacAddress(pThis->pSwitchPort, &pThis->u.s.MacAddr);
+ vboxNetFltRelease(pThis, true /*fBusy*/);
+ }
+ }
+ else
+ {
+ LogRel((DEVICE_NAME ":vboxNetFltSolarisCachePhysAddr: Invalid address size. expected=%d got=%d\n", ETHERADDRL,
+ pPhysAddrAck->dl_addr_length));
+ }
+}
+
+
+/**
+ * Prepare DLPI bind request to a SAP.
+ *
+ * @returns VBox status code.
+ * @param pQueue Pointer to the read queue.
+ * @param SAP The SAP to bind the stream to.
+ */
+static int vboxNetFltSolarisBindReq(queue_t *pQueue, int SAP)
+{
+ LogFunc((DEVICE_NAME ":vboxNetFltSolarisBindReq SAP=%d\n", SAP));
+
+ mblk_t *pBindMsg = mexchange(NULL, NULL, DL_BIND_REQ_SIZE, M_PROTO, DL_BIND_REQ);
+ if (RT_UNLIKELY(!pBindMsg))
+ return VERR_NO_MEMORY;
+
+ dl_bind_req_t *pBindReq = (dl_bind_req_t *)pBindMsg->b_rptr;
+ pBindReq->dl_sap = SAP;
+ pBindReq->dl_max_conind = 0;
+ pBindReq->dl_conn_mgmt = 0;
+ pBindReq->dl_xidtest_flg = 0;
+ pBindReq->dl_service_mode = DL_CLDLS;
+
+ qreply(pQueue, pBindMsg);
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * Prepare DLPI notifications request.
+ *
+ * @returns VBox status code.
+ * @param pQueue Pointer to the read queue.
+ */
+static int vboxNetFltSolarisNotifyReq(queue_t *pQueue)
+{
+ LogFunc((DEVICE_NAME ":vboxNetFltSolarisNotifyReq\n"));
+
+ mblk_t *pNotifyMsg = mexchange(NULL, NULL, DL_NOTIFY_REQ_SIZE, M_PROTO, DL_NOTIFY_REQ);
+ if (RT_UNLIKELY(!pNotifyMsg))
+ return VERR_NO_MEMORY;
+
+ dl_notify_req_t *pNotifyReq = (dl_notify_req_t *)pNotifyMsg->b_rptr;
+ pNotifyReq->dl_notifications = DL_NOTE_LINK_UP | DL_NOTE_LINK_DOWN | DL_NOTE_PHYS_ADDR;
+
+ qreply(pQueue, pNotifyMsg);
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * Opens the required device and returns the vnode_t associated with it.
+ * We require this for the funny attach/detach routine.
+ *
+ * @returns VBox status code.
+ * @param pszDev The device path.
+ * @param ppVNode Where to store the vnode_t pointer associated with the opened device.
+ * @param ppVNodeHeld Where to store the vnode_t required during closing of the device.
+ * @param ppUser Open handle required while closing the device.
+ */
+static int vboxNetFltSolarisOpenDev(char *pszDev, vnode_t **ppVNode, vnode_t **ppVNodeHeld, TIUSER **ppUser)
+{
+ int rc;
+ vnode_t *pVNodeHeld = NULL;
+ rc = lookupname(pszDev, UIO_SYSSPACE, FOLLOW, NULLVPP, &pVNodeHeld);
+ if ( !rc
+ && pVNodeHeld)
+ {
+ TIUSER *pUser;
+ rc = t_kopen((file_t *)NULL, pVNodeHeld->v_rdev, FREAD | FWRITE, &pUser, kcred);
+ if (!rc)
+ {
+ if ( pUser
+ && pUser->fp
+ && VNODE_FOR_FILE_T(pUser->fp))
+ {
+ *ppVNode = VNODE_FOR_FILE_T(pUser->fp);
+ *ppVNodeHeld = pVNodeHeld;
+ *ppUser = pUser;
+ return VINF_SUCCESS;
+ }
+ else
+ {
+ LogRel((DEVICE_NAME ":vboxNetFltSolarisOpenDev failed. pUser=%p fp=%p f_vnode=%p\n", pUser,
+ pUser ? pUser->fp : NULL, pUser && pUser->fp ? VNODE_FOR_FILE_T(pUser->fp) : NULL));
+ }
+
+ if (pUser)
+ t_kclose(pUser, 0);
+ }
+ else
+ LogRel((DEVICE_NAME ":vboxNetFltSolarisOpenDev t_kopen failed. rc=%d\n", rc));
+
+ VN_RELE(pVNodeHeld);
+ }
+ else
+ LogRel((DEVICE_NAME ":vboxNetFltSolarisOpenDev lookupname failed. rc=%d pVNodeHeld=%p\n", rc, pVNodeHeld));
+
+ return VERR_PATH_NOT_FOUND;
+}
+
+
+/**
+ * Close the device opened using vboxNetFltSolarisOpenDev.
+ *
+ * @param pVNodeHeld Pointer to the held vnode of the device.
+ * @param pUser Pointer to the file handle.
+ */
+static void vboxNetFltSolarisCloseDev(vnode_t *pVNodeHeld, TIUSER *pUser)
+{
+ t_kclose(pUser, 0);
+ VN_RELE(pVNodeHeld);
+}
+
+
+/**
+ * Set the DLPI style-2 PPA via an attach request, Synchronous.
+ * Waits for request acknowledgement and verifies the result.
+ *
+ * @returns VBox status code.
+ * @param hDevice Layered device handle.
+ * @param PPA Physical Point of Attachment (PPA) number.
+ */
+static int vboxNetFltSolarisAttachReq(ldi_handle_t hDevice, int PPA)
+{
+ int rc;
+ mblk_t *pAttachMsg = mexchange(NULL, NULL, DL_ATTACH_REQ_SIZE, M_PROTO, DL_ATTACH_REQ);
+ if (RT_UNLIKELY(!pAttachMsg))
+ return VERR_NO_MEMORY;
+
+ dl_attach_req_t *pAttachReq = (dl_attach_req_t *)pAttachMsg->b_rptr;
+ pAttachReq->dl_ppa = PPA;
+
+ rc = ldi_putmsg(hDevice, pAttachMsg);
+ if (!rc)
+ {
+ rc = ldi_getmsg(hDevice, &pAttachMsg, NULL);
+ if (!rc)
+ {
+ /*
+ * Verify if the attach succeeded.
+ */
+ size_t cbMsg = MBLKL(pAttachMsg);
+ if (cbMsg >= sizeof(t_uscalar_t))
+ {
+ union DL_primitives *pPrim = (union DL_primitives *)pAttachMsg->b_rptr;
+ t_uscalar_t AckPrim = pPrim->dl_primitive;
+
+ if ( AckPrim == DL_OK_ACK /* Success! */
+ && cbMsg == DL_OK_ACK_SIZE)
+ {
+ rc = VINF_SUCCESS;
+ }
+ else if ( AckPrim == DL_ERROR_ACK /* Error Ack. */
+ && cbMsg == DL_ERROR_ACK_SIZE)
+ {
+ LogRel((DEVICE_NAME ":vboxNetFltSolarisAttachReq ldi_getmsg succeeded, but unsupported op.\n"));
+ rc = VERR_NOT_SUPPORTED;
+ }
+ else /* Garbled reply */
+ {
+ LogRel((DEVICE_NAME ":vboxNetFltSolarisAttachReq ldi_getmsg succeeded, but invalid op."
+ " expected %d recvd %d\n", DL_OK_ACK, AckPrim));
+ rc = VERR_INVALID_FUNCTION;
+ }
+ }
+ else
+ {
+ LogRel((DEVICE_NAME ":vboxNetFltSolarisAttachReq ldi_getmsg succeeded, but invalid size %d expected %d\n", cbMsg,
+ DL_OK_ACK_SIZE));
+ rc = VERR_INVALID_FUNCTION;
+ }
+ }
+ else
+ {
+ LogRel((DEVICE_NAME ":vboxNetFltSolarisAttachReq ldi_getmsg failed. rc=%d\n", rc));
+ rc = VERR_INVALID_FUNCTION;
+ }
+ }
+ else
+ {
+ LogRel((DEVICE_NAME ":vboxNetFltSolarisAttachReq ldi_putmsg failed. rc=%d\n", rc));
+ rc = VERR_UNRESOLVED_ERROR;
+ }
+
+ freemsg(pAttachMsg);
+ return rc;
+}
+
+
+/**
+ * Get the logical interface flags from the stream.
+ *
+ * @returns VBox status code.
+ * @param hDevice Layered device handle.
+ * @param pInterface Pointer to the interface.
+ */
+static int vboxNetFltSolarisGetIfFlags(ldi_handle_t hDevice, struct lifreq *pInterface)
+{
+ struct strioctl IOCReq;
+ int rc;
+ int ret;
+ IOCReq.ic_cmd = SIOCGLIFFLAGS;
+ IOCReq.ic_timout = 40;
+ IOCReq.ic_len = sizeof(struct lifreq);
+ IOCReq.ic_dp = (caddr_t)pInterface;
+ rc = ldi_ioctl(hDevice, I_STR, (intptr_t)&IOCReq, FKIOCTL, kcred, &ret);
+ if (!rc)
+ return VINF_SUCCESS;
+
+ return RTErrConvertFromErrno(rc);
+}
+
+
+/**
+ * Sets the multiplexor ID from the interface.
+ *
+ * @returns VBox status code.
+ * @param pVNode Pointer to the device vnode.
+ * @param pInterface Pointer to the interface.
+ */
+static int vboxNetFltSolarisSetMuxId(vnode_t *pVNode, struct lifreq *pInterface)
+{
+ struct strioctl IOCReq;
+ int rc;
+ int ret;
+ IOCReq.ic_cmd = SIOCSLIFMUXID;
+ IOCReq.ic_timout = 40;
+ IOCReq.ic_len = sizeof(struct lifreq);
+ IOCReq.ic_dp = (caddr_t)pInterface;
+
+ rc = strioctl(pVNode, I_STR, (intptr_t)&IOCReq, 0, K_TO_K, kcred, &ret);
+ if (!rc)
+ return VINF_SUCCESS;
+
+ return RTErrConvertFromErrno(rc);
+}
+
+
+/**
+ * Get the multiplexor file descriptor of the lower stream.
+ *
+ * @returns VBox status code.
+ * @param pVNode Pointer to the device vnode.
+ * @param MuxId The multiplexor ID.
+ * @param pFd Where to store the lower stream file descriptor.
+ */
+static int vboxNetFltSolarisMuxIdToFd(vnode_t *pVNode, int MuxId, int *pFd)
+{
+ int ret;
+
+ *pFd = -1; /* silence compiler warnings from -Wmaybe-uninitialized */
+ int rc = strioctl(pVNode, _I_MUXID2FD, (intptr_t)MuxId, 0, K_TO_K, kcred, &ret);
+ if (!rc)
+ {
+ *pFd = ret;
+ return VINF_SUCCESS;
+ }
+
+ return RTErrConvertFromErrno(rc);
+}
+
+
+/**
+ * Relinks the lower and the upper IPv4 stream.
+ *
+ * @returns VBox status code.
+ * @param pVNode Pointer to the device vnode.
+ * @param pInterface Pointer to the interface.
+ * @param IpMuxFd The IP multiplexor ID.
+ * @param ArpMuxFd The ARP multiplexor ID.
+ */
+static int vboxNetFltSolarisRelinkIp4(vnode_t *pVNode, struct lifreq *pInterface, int IpMuxFd, int ArpMuxFd)
+{
+ LogFunc((DEVICE_NAME ":vboxNetFltSolarisRelinkIp4: pVNode=%p pInterface=%p IpMuxFd=%d ArpMuxFd=%d\n", pVNode,
+ pInterface, IpMuxFd, ArpMuxFd));
+
+ int NewIpMuxId;
+ int NewArpMuxId;
+ int rc = strioctl(pVNode, I_PLINK, (intptr_t)IpMuxFd, 0, K_TO_K, kcred, &NewIpMuxId);
+ int rc2 = strioctl(pVNode, I_PLINK, (intptr_t)ArpMuxFd, 0, K_TO_K, kcred, &NewArpMuxId);
+ if ( !rc
+ && !rc2)
+ {
+ pInterface->lifr_ip_muxid = NewIpMuxId;
+ pInterface->lifr_arp_muxid = NewArpMuxId;
+ rc = vboxNetFltSolarisSetMuxId(pVNode, pInterface);
+ if (RT_SUCCESS(rc))
+ return VINF_SUCCESS;
+
+ LogRel((DEVICE_NAME ":vboxNetFltSolarisRelinkIp4: failed to set new Mux Id.\n"));
+ }
+ else
+ LogRel((DEVICE_NAME ":vboxNetFltSolarisRelinkIp4: failed to link.\n"));
+
+ return VERR_GENERAL_FAILURE;
+}
+
+
+/**
+ * Relinks the lower and the upper IPv6 stream.
+ *
+ * @returns VBox status code.
+ * @param pVNode Pointer to the device vnode.
+ * @param pInterface Pointer to the interface.
+ * @param Ip6MuxFd The IPv6 multiplexor ID.
+ */
+static int vboxNetFltSolarisRelinkIp6(vnode_t *pVNode, struct lifreq *pInterface, int Ip6MuxFd)
+{
+ LogFunc((DEVICE_NAME ":vboxNetFltSolarisRelinkIp6: pVNode=%p pInterface=%p Ip6MuxFd=%d\n", pVNode, pInterface, Ip6MuxFd));
+
+ int NewIp6MuxId;
+ int rc = strioctl(pVNode, I_PLINK, (intptr_t)Ip6MuxFd, 0, K_TO_K, kcred, &NewIp6MuxId);
+ if (!rc)
+ {
+ pInterface->lifr_ip_muxid = NewIp6MuxId;
+ rc = vboxNetFltSolarisSetMuxId(pVNode, pInterface);
+ if (RT_SUCCESS(rc))
+ return VINF_SUCCESS;
+
+ LogRel((DEVICE_NAME ":vboxNetFltSolarisRelinkIp6: failed to set new Mux Id.\n"));
+ }
+ else
+ LogRel((DEVICE_NAME ":vboxNetFltSolarisRelinkIp6: failed to link.\n"));
+
+ return VERR_GENERAL_FAILURE;
+}
+
+
+/**
+ * Dynamically find the position on the host stack where to attach/detach ourselves.
+ *
+ * @returns VBox status code.
+ * @param fAttach Is this an attach or detach.
+ * @param pVNode Pointer to the lower stream vnode.
+ * @param pModPos Where to store the module position.
+ */
+static int vboxNetFltSolarisDetermineModPos(bool fAttach, vnode_t *pVNode, int *pModPos)
+{
+ LogFunc((DEVICE_NAME ":vboxNetFltSolarisDetermineModPos: fAttach=%d pVNode=%p pModPos=%p\n", fAttach, pVNode, pModPos));
+
+ int cMod;
+ int rc = strioctl(pVNode, I_LIST, (intptr_t)NULL, 0, K_TO_K, kcred, &cMod);
+ if (!rc)
+ {
+ if (cMod < 1)
+ {
+ LogRel((DEVICE_NAME ":vboxNetFltSolarisDetermineModPos: too few modules on host interface. cMod=%d\n"));
+ return VERR_OUT_OF_RANGE;
+ }
+
+ /*
+ * While attaching we make sure we are at the bottom most of the stack, excepting
+ * the host driver.
+ */
+ Log((DEVICE_NAME ":vboxNetFltSolarisDetermineModPos: cMod=%d\n", cMod));
+ if (fAttach)
+ {
+ *pModPos = cMod - 1;
+ return VINF_SUCCESS;
+ }
+
+ /*
+ * Detaching is a bit more complicated; since user could have altered the stack positions
+ * we take the safe approach by finding our position.
+ */
+ struct str_list StrList;
+ StrList.sl_nmods = cMod;
+ StrList.sl_modlist = RTMemAllocZ(cMod * sizeof(struct str_list));
+ if (RT_UNLIKELY(!StrList.sl_modlist))
+ {
+ Log((DEVICE_NAME ":vboxNetFltSolarisDetermineModPos: failed to alloc memory for StrList.\n"));
+ return VERR_NO_MEMORY;
+ }
+
+ /*
+ * Get the list of all modules on the stack.
+ */
+ int ret;
+ rc = strioctl(pVNode, I_LIST, (intptr_t)&StrList, 0, K_TO_K, kcred, &ret);
+ if (!rc)
+ {
+ /*
+ * Find our filter.
+ */
+ for (int i = 0; i < StrList.sl_nmods; i++)
+ {
+ if (!strcmp(DEVICE_NAME, StrList.sl_modlist[i].l_name))
+ {
+ Log((DEVICE_NAME ":vboxNetFltSolarisDetermineModPos: Success! Found %s at %d.\n", DEVICE_NAME, i));
+ *pModPos = i;
+ RTMemFree(StrList.sl_modlist);
+ return VINF_SUCCESS;
+ }
+ }
+
+ LogRel((DEVICE_NAME ":vboxNetFltSolarisDetermineModPos: failed to find %s in the host stack.\n", DEVICE_NAME));
+ }
+ else
+ LogRel((DEVICE_NAME ":vboxNetFltSolarisDetermineModPos: failed to get module information. rc=%d\n"));
+
+ RTMemFree(StrList.sl_modlist);
+ }
+ else
+ LogRel((DEVICE_NAME ":vboxNetFltSolarisDetermineModPos: failed to get list of modules on host interface. rc=%d\n", rc));
+ return VERR_GENERAL_FAILURE;
+}
+
+
+/**
+ * Opens up the DLPI style 2 link that requires explicit PPA attach
+ * phase.
+ *
+ * @returns VBox status code.
+ * @param pThis The instance.
+ * @param pDevId Where to store the opened LDI device id.
+ */
+static int vboxNetFltSolarisOpenStyle2(PVBOXNETFLTINS pThis, ldi_ident_t *pDevId)
+{
+ /*
+ * Strip out PPA from the device name, eg: "ce3".
+ */
+ char *pszDev = RTStrDup(pThis->szName);
+ if (!pszDev)
+ return VERR_NO_MEMORY;
+
+ char *pszEnd = strchr(pszDev, '\0');
+ while (--pszEnd > pszDev)
+ if (!RT_C_IS_DIGIT(*pszEnd))
+ break;
+ pszEnd++;
+
+ int rc = VERR_GENERAL_FAILURE;
+ long PPA = -1;
+ if ( pszEnd
+ && ddi_strtol(pszEnd, NULL, 10, &PPA) == 0)
+ {
+ *pszEnd = '\0';
+ char szDev[128];
+ RTStrPrintf(szDev, sizeof(szDev), "/dev/%s", pszDev);
+
+ /*
+ * Try open the device as DPLI style 2.
+ */
+ rc = ldi_open_by_name(szDev, FREAD | FWRITE, kcred, &pThis->u.s.hIface, *pDevId);
+ if (!rc)
+ {
+ /*
+ * Attach the PPA explictly.
+ */
+ rc = vboxNetFltSolarisAttachReq(pThis->u.s.hIface, (int)PPA);
+ if (RT_SUCCESS(rc))
+ {
+ RTStrFree(pszDev);
+ return rc;
+ }
+
+ ldi_close(pThis->u.s.hIface, FREAD | FWRITE, kcred);
+ pThis->u.s.hIface = NULL;
+ LogRel((DEVICE_NAME ":vboxNetFltSolarisOpenStyle2 dl_attach failed. rc=%d szDev=%s PPA=%d rc=%d\n", rc, szDev, PPA));
+ }
+ else
+ LogRel((DEVICE_NAME ":vboxNetFltSolarisOpenStyle2 Failed to open. rc=%d szDev=%s PPA=%d\n", rc, szDev, PPA));
+ }
+ else
+ LogRel((DEVICE_NAME ":vboxNetFltSolarisOpenStyle2 Failed to construct PPA. pszDev=%s pszEnd=%s.\n", pszDev, pszEnd));
+
+ RTStrFree(pszDev);
+ return VERR_INTNET_FLT_IF_FAILED;
+}
+
+
+/**
+ * Opens up dedicated stream on top of the interface.
+ * As a side-effect, the stream gets opened during
+ * the I_PUSH phase.
+ *
+ * @param pThis The instance.
+ */
+static int vboxNetFltSolarisOpenStream(PVBOXNETFLTINS pThis)
+{
+ ldi_ident_t DevId;
+ DevId = ldi_ident_from_anon();
+ int ret;
+
+ /*
+ * Figure out if this is a VLAN interface or not based on the interface name.
+ * Only works for the VLAN PPA-hack based names. See @bugref{4854} for details.
+ */
+ char *pszEnd = strchr(pThis->szName, '\0');
+ while (--pszEnd > pThis->szName)
+ if (!RT_C_IS_DIGIT(*pszEnd))
+ break;
+ pszEnd++;
+ uint32_t PPA = RTStrToUInt32(pszEnd);
+ if (PPA > 1000)
+ {
+ pThis->u.s.fVLAN = true;
+ LogRel((DEVICE_NAME ": %s detected as VLAN interface with VID=%u.\n", pThis->szName, PPA / 1000U));
+ }
+
+ /*
+ * Try style-1 open first.
+ */
+ char szDev[128];
+ RTStrPrintf(szDev, sizeof(szDev), "/dev/net/%s", pThis->szName);
+ int rc = ldi_open_by_name(szDev, FREAD | FWRITE, kcred, &pThis->u.s.hIface, DevId);
+ if ( rc
+ && rc == ENODEV) /* ENODEV is returned when resolvepath fails, not ENOENT */
+ {
+ /*
+ * Fallback to non-ClearView style-1 open.
+ */
+ RTStrPrintf(szDev, sizeof(szDev), "/dev/%s", pThis->szName);
+ rc = ldi_open_by_name(szDev, FREAD | FWRITE, kcred, &pThis->u.s.hIface, DevId);
+ }
+
+ if (rc)
+ {
+ /*
+ * Try DLPI style 2.
+ */
+ rc = vboxNetFltSolarisOpenStyle2(pThis, &DevId);
+ if (RT_FAILURE(rc))
+ LogRel((DEVICE_NAME ":vboxNetFltSolarisOpenStream vboxNetFltSolarisOpenStyle2 failed. rc=%d\n", rc));
+ else
+ rc = 0;
+ }
+
+ ldi_ident_release(DevId);
+ if (rc)
+ {
+ LogRel((DEVICE_NAME ":vboxNetFltSolarisOpenStream Failed to open '%s' rc=%d pszName='%s'\n", szDev, rc, pThis->szName));
+ return VERR_INTNET_FLT_IF_FAILED;
+ }
+
+ rc = ldi_ioctl(pThis->u.s.hIface, I_FIND, (intptr_t)DEVICE_NAME, FKIOCTL, kcred, &ret);
+ if (!rc)
+ {
+ if (!ret)
+ {
+ if (RT_LIKELY(g_pVBoxNetFltSolarisCred)) /* Paranoia */
+ {
+ rc = RTSemFastMutexRequest(g_VBoxNetFltSolarisMtx);
+ AssertRCReturn(rc, rc);
+
+ g_VBoxNetFltSolarisInstance = pThis;
+ g_VBoxNetFltSolarisStreamType = kPromiscStream;
+
+ rc = ldi_ioctl(pThis->u.s.hIface, I_PUSH, (intptr_t)DEVICE_NAME, FKIOCTL, g_pVBoxNetFltSolarisCred, &ret);
+
+ g_VBoxNetFltSolarisInstance = NULL;
+ g_VBoxNetFltSolarisStreamType = kUndefined;
+
+ RTSemFastMutexRelease(g_VBoxNetFltSolarisMtx);
+ }
+ else
+ {
+ LogRel((DEVICE_NAME ":vboxNetFltSolarisOpenStream huh!? Missing credentials.\n"));
+ rc = VERR_INVALID_POINTER;
+ }
+
+ if (!rc)
+ return VINF_SUCCESS;
+
+ LogRel((DEVICE_NAME ":vboxNetFltSolarisOpenStream Failed to push filter onto host interface '%s'\n", pThis->szName));
+ }
+ else
+ return VINF_SUCCESS;
+ }
+ else
+ LogRel((DEVICE_NAME ":vboxNetFltSolarisOpenStream Failed to search for filter in interface '%s'.\n", pThis->szName));
+
+ ldi_close(pThis->u.s.hIface, FREAD | FWRITE, kcred);
+ pThis->u.s.hIface = NULL;
+
+ return VERR_INTNET_FLT_IF_FAILED;
+}
+
+
+/**
+ * Closes the interface, thereby closing the dedicated stream.
+ *
+ * @param pThis The instance.
+ */
+static void vboxNetFltSolarisCloseStream(PVBOXNETFLTINS pThis)
+{
+ LogFunc((DEVICE_NAME ":vboxNetFltSolarisCloseStream pThis=%p\n"));
+
+ if (pThis->u.s.hIface)
+ {
+ ldi_close(pThis->u.s.hIface, FREAD | FWRITE, kcred);
+ pThis->u.s.hIface = NULL;
+ }
+}
+
+
+/**
+ * Dynamically attach under IPv4 and ARP streams on the host stack.
+ *
+ * @returns VBox status code.
+ * @param pThis The instance.
+ * @param fAttach Is this an attach or detach.
+ */
+static int vboxNetFltSolarisAttachIp4(PVBOXNETFLTINS pThis, bool fAttach)
+{
+ LogFunc((DEVICE_NAME ":vboxNetFltSolarisAttachIp4 pThis=%p fAttach=%d\n", pThis, fAttach));
+
+ /*
+ * Statutory Warning: Hackish code ahead.
+ */
+ char *pszModName = DEVICE_NAME;
+
+ struct lifreq Ip4Interface;
+ bzero(&Ip4Interface, sizeof(Ip4Interface));
+ Ip4Interface.lifr_addr.ss_family = AF_INET;
+ strncpy(Ip4Interface.lifr_name, pThis->szName, sizeof(Ip4Interface.lifr_name));
+
+ struct strmodconf StrMod;
+ StrMod.mod_name = pszModName;
+ StrMod.pos = -1; /* this is filled in later. */
+
+ struct strmodconf ArpStrMod;
+ bcopy(&StrMod, &ArpStrMod, sizeof(StrMod));
+
+ int rc;
+ int rc2;
+ int ret;
+ ldi_ident_t DeviceIdent = ldi_ident_from_anon();
+ ldi_handle_t Ip4DevHandle;
+ ldi_handle_t ArpDevHandle;
+
+ /*
+ * Open the IP and ARP streams as layered devices.
+ */
+ rc = ldi_open_by_name(IP_DEV_NAME, FREAD | FWRITE, kcred, &Ip4DevHandle, DeviceIdent);
+ if (rc)
+ {
+ LogRel((DEVICE_NAME ":vboxNetFltSolarisAttachIp4: failed to open the IP stream on '%s'.\n", pThis->szName));
+ ldi_ident_release(DeviceIdent);
+ return VERR_INTNET_FLT_IF_FAILED;
+ }
+
+ rc = ldi_open_by_name("/dev/arp", FREAD | FWRITE, kcred, &ArpDevHandle, DeviceIdent);
+ if (rc)
+ {
+ LogRel((DEVICE_NAME ":vboxNetFltSolarisAttachIp4: failed to open the ARP stream on '%s'.\n", pThis->szName));
+ ldi_ident_release(DeviceIdent);
+ ldi_close(Ip4DevHandle, FREAD | FWRITE, kcred);
+ return VERR_INTNET_FLT_IF_FAILED;
+ }
+
+ ldi_ident_release(DeviceIdent);
+
+ /*
+ * Obtain the interface flags from IPv4.
+ */
+ rc = vboxNetFltSolarisGetIfFlags(Ip4DevHandle, &Ip4Interface);
+ if (RT_SUCCESS(rc))
+ {
+ /*
+ * Open the UDP stream. We sort of cheat here and obtain the vnode so that we can perform
+ * things that are not possible from the layered interface.
+ */
+ vnode_t *pUdp4VNode = NULL;
+ vnode_t *pUdp4VNodeHeld = NULL;
+ TIUSER *pUdp4User = NULL;
+ rc = vboxNetFltSolarisOpenDev(UDP_DEV_NAME, &pUdp4VNode, &pUdp4VNodeHeld, &pUdp4User);
+ if (RT_SUCCESS(rc))
+ {
+ /*
+ * Get the multiplexor IDs.
+ */
+ rc = ldi_ioctl(Ip4DevHandle, SIOCGLIFMUXID, (intptr_t)&Ip4Interface, FKIOCTL, kcred, &ret);
+ if (!rc)
+ {
+ /*
+ * Get the multiplex file descriptor to the lower streams. Generally this is lost
+ * once a module is I_PLINK, we need to reobtain it for inserting/removing ourselves from the stack.
+ */
+ int Ip4MuxFd;
+ int ArpMuxFd;
+ rc = vboxNetFltSolarisMuxIdToFd(pUdp4VNode, Ip4Interface.lifr_ip_muxid, &Ip4MuxFd);
+ rc2 = vboxNetFltSolarisMuxIdToFd(pUdp4VNode, Ip4Interface.lifr_arp_muxid, &ArpMuxFd);
+ if ( RT_SUCCESS(rc)
+ && RT_SUCCESS(rc2))
+ {
+ /*
+ * We need to I_PUNLINK on these multiplexor IDs before we can start
+ * operating on the lower stream as insertions are direct operations on the lower stream.
+ */
+ rc = strioctl(pUdp4VNode, I_PUNLINK, (intptr_t)Ip4Interface.lifr_ip_muxid, 0, K_TO_K, kcred, &ret);
+ rc2 = strioctl(pUdp4VNode, I_PUNLINK, (intptr_t)Ip4Interface.lifr_arp_muxid, 0, K_TO_K, kcred, &ret);
+ if ( !rc
+ && !rc2)
+ {
+ /*
+ * Obtain the vnode from the useless userland file descriptor.
+ */
+ file_t *pIpFile = getf(Ip4MuxFd);
+ file_t *pArpFile = getf(ArpMuxFd);
+ if ( pIpFile
+ && pArpFile
+ && VNODE_FOR_FILE_T(pArpFile)
+ && VNODE_FOR_FILE_T(pIpFile))
+ {
+ vnode_t *pIp4VNode = VNODE_FOR_FILE_T(pIpFile);
+ vnode_t *pArpVNode = VNODE_FOR_FILE_T(pArpFile);
+
+ /*
+ * Find the position on the host stack for attaching/detaching ourselves.
+ */
+ rc = vboxNetFltSolarisDetermineModPos(fAttach, pIp4VNode, &StrMod.pos);
+ rc2 = vboxNetFltSolarisDetermineModPos(fAttach, pArpVNode, &ArpStrMod.pos);
+ if ( RT_SUCCESS(rc)
+ && RT_SUCCESS(rc2))
+ {
+ /*
+ * Inject/Eject from the host IP stack.
+ */
+
+ /*
+ * Set global data which will be grabbed by ModOpen.
+ * There is a known (though very unlikely) race here because
+ * of the inability to pass user data while inserting.
+ */
+ rc = RTSemFastMutexRequest(g_VBoxNetFltSolarisMtx);
+ AssertRCReturn(rc, rc);
+
+ if (fAttach)
+ {
+ g_VBoxNetFltSolarisInstance = pThis;
+ g_VBoxNetFltSolarisStreamType = kIp4Stream;
+ }
+
+ rc = strioctl(pIp4VNode, fAttach ? _I_INSERT : _I_REMOVE, (intptr_t)&StrMod, 0, K_TO_K,
+ g_pVBoxNetFltSolarisCred, &ret);
+
+ if (fAttach)
+ {
+ g_VBoxNetFltSolarisInstance = NULL;
+ g_VBoxNetFltSolarisStreamType = kUndefined;
+ }
+
+ RTSemFastMutexRelease(g_VBoxNetFltSolarisMtx);
+
+ if (!rc)
+ {
+ /*
+ * Inject/Eject from the host ARP stack.
+ */
+ rc = RTSemFastMutexRequest(g_VBoxNetFltSolarisMtx);
+ AssertRCReturn(rc, rc);
+
+ if (fAttach)
+ {
+ g_VBoxNetFltSolarisInstance = pThis;
+ g_VBoxNetFltSolarisStreamType = kArpStream;
+ }
+
+ rc = strioctl(pArpVNode, fAttach ? _I_INSERT : _I_REMOVE, (intptr_t)&ArpStrMod, 0, K_TO_K,
+ g_pVBoxNetFltSolarisCred, &ret);
+
+ if (fAttach)
+ {
+ g_VBoxNetFltSolarisInstance = NULL;
+ g_VBoxNetFltSolarisStreamType = kUndefined;
+ }
+
+ RTSemFastMutexRelease(g_VBoxNetFltSolarisMtx);
+
+ if (!rc)
+ {
+ /*
+ * Our job's not yet over; we need to relink the upper and lower streams
+ * otherwise we've pretty much screwed up the host interface.
+ */
+ rc = vboxNetFltSolarisRelinkIp4(pUdp4VNode, &Ip4Interface, Ip4MuxFd, ArpMuxFd);
+ if (RT_SUCCESS(rc))
+ {
+ /*
+ * Close the devices ONLY during the return from function case; otherwise
+ * we end up close twice which is an instant kernel panic.
+ */
+ vboxNetFltSolarisCloseDev(pUdp4VNodeHeld, pUdp4User);
+ ldi_close(ArpDevHandle, FREAD | FWRITE, kcred);
+ ldi_close(Ip4DevHandle, FREAD | FWRITE, kcred);
+ releasef(Ip4MuxFd);
+ releasef(ArpMuxFd);
+
+ Log((DEVICE_NAME ":vboxNetFltSolarisAttachIp4: Success! %s %s@(IPv4:%d Arp:%d) "
+ "%s interface %s\n", fAttach ? "Injected" : "Ejected", StrMod.mod_name,
+ StrMod.pos, ArpStrMod.pos, fAttach ? "to" : "from", pThis->szName));
+ return VINF_SUCCESS;
+ }
+ else
+ {
+ LogRel((DEVICE_NAME ":vboxNetFltSolarisAttachIp4: Relinking failed. Mode=%s rc=%d.\n",
+ fAttach ? "inject" : "eject", rc));
+ }
+
+ /*
+ * Try failing gracefully during attach.
+ */
+ if (fAttach)
+ strioctl(pArpVNode, _I_REMOVE, (intptr_t)&StrMod, 0, K_TO_K, kcred, &ret);
+ }
+ else
+ {
+ LogRel((DEVICE_NAME ":vboxNetFltSolarisAttachIp4: failed to %s the ARP stack. rc=%d\n",
+ fAttach ? "inject into" : "eject from", rc));
+ }
+
+ if (fAttach)
+ strioctl(pIp4VNode, _I_REMOVE, (intptr_t)&StrMod, 0, K_TO_K, kcred, &ret);
+
+ vboxNetFltSolarisRelinkIp4(pUdp4VNode, &Ip4Interface, Ip4MuxFd, ArpMuxFd);
+ }
+ else
+ {
+ LogRel((DEVICE_NAME ":vboxNetFltSolarisAttachIp4: failed to %s the IP stack. rc=%d\n",
+ fAttach ? "inject into" : "eject from", rc));
+ }
+ }
+ else
+ {
+ LogRel((DEVICE_NAME ":vboxNetFltSolarisAttachIp4: failed to find position. rc=%d rc2=%d\n", rc,
+ rc2));
+ }
+
+ releasef(Ip4MuxFd);
+ releasef(ArpMuxFd);
+ }
+ else
+ LogRel((DEVICE_NAME ":vboxNetFltSolarisAttachIp4: failed to get vnode from MuxFd.\n"));
+ }
+ else
+ {
+ LogRel((DEVICE_NAME ":vboxNetFltSolarisAttachIp4: failed to unlink upper stream rc=%d rc2=%d.\n", rc,
+ rc2));
+ }
+ }
+ else
+ LogRel((DEVICE_NAME ":vboxNetFltSolarisAttachIp4: failed to get MuxFd from MuxId. rc=%d rc2=%d\n", rc, rc2));
+ }
+ else
+ LogRel((DEVICE_NAME ":vboxNetFltSolarisAttachIp4: failed to get Mux Ids. rc=%d\n", rc));
+ vboxNetFltSolarisCloseDev(pUdp4VNodeHeld, pUdp4User);
+ }
+ else
+ LogRel((DEVICE_NAME ":vboxNetFltSolarisAttachIp4: failed to open UDP. rc=%d\n", rc));
+
+ rc = VERR_INTNET_FLT_IF_FAILED;
+ }
+ else
+ {
+ /*
+ * This would happen for interfaces that are not plumbed.
+ */
+ LogRel((DEVICE_NAME ":vboxNetFltSolarisAttachIp4: Warning: seems '%s' is unplumbed.\n", pThis->szName));
+ rc = VINF_SUCCESS;
+ }
+
+ ldi_close(ArpDevHandle, FREAD | FWRITE, kcred);
+ ldi_close(Ip4DevHandle, FREAD | FWRITE, kcred);
+
+ return rc;
+}
+
+
+/**
+ * Dynamically attach under IPv6 on the host stack.
+ *
+ * @returns VBox status code.
+ * @param pThis The instance.
+ * @param fAttach Is this an attach or detach.
+ */
+static int vboxNetFltSolarisAttachIp6(PVBOXNETFLTINS pThis, bool fAttach)
+{
+ LogFunc((DEVICE_NAME ":vboxNetFltSolarisAttachIp6 pThis=%p fAttach=%d\n", pThis, fAttach));
+
+ /*
+ * Statutory Warning: Hackish code ahead.
+ */
+ char *pszModName = DEVICE_NAME;
+
+ struct lifreq Ip6Interface;
+ bzero(&Ip6Interface, sizeof(Ip6Interface));
+ Ip6Interface.lifr_addr.ss_family = AF_INET6;
+ strncpy(Ip6Interface.lifr_name, pThis->szName, sizeof(Ip6Interface.lifr_name));
+
+ struct strmodconf StrMod;
+ StrMod.mod_name = pszModName;
+ StrMod.pos = -1; /* this is filled in later. */
+
+ int rc;
+ int ret;
+ ldi_ident_t DeviceIdent = ldi_ident_from_anon();
+ ldi_handle_t Ip6DevHandle;
+
+ /*
+ * Open the IPv6 stream as a layered devices.
+ */
+ rc = ldi_open_by_name(IP6_DEV_NAME, FREAD | FWRITE, kcred, &Ip6DevHandle, DeviceIdent);
+ ldi_ident_release(DeviceIdent);
+ if (rc)
+ {
+ LogRel((DEVICE_NAME ":vboxNetFltSolarisAttachIp6: failed to open the IPv6 stream on '%s'.\n", pThis->szName));
+ return VERR_INTNET_FLT_IF_FAILED;
+ }
+
+ /*
+ * Obtain the interface flags from IPv6.
+ */
+ rc = vboxNetFltSolarisGetIfFlags(Ip6DevHandle, &Ip6Interface);
+ if (RT_SUCCESS(rc))
+ {
+ /*
+ * Open the UDP stream. We sort of cheat here and obtain the vnode so that we can perform
+ * things that are not possible from the layered interface.
+ */
+ vnode_t *pUdp6VNode = NULL;
+ vnode_t *pUdp6VNodeHeld = NULL;
+ TIUSER *pUdp6User = NULL;
+ rc = vboxNetFltSolarisOpenDev(UDP6_DEV_NAME, &pUdp6VNode, &pUdp6VNodeHeld, &pUdp6User);
+ if (RT_SUCCESS(rc))
+ {
+ /*
+ * Get the multiplexor IDs.
+ */
+ rc = ldi_ioctl(Ip6DevHandle, SIOCGLIFMUXID, (intptr_t)&Ip6Interface, FKIOCTL, kcred, &ret);
+ if (!rc)
+ {
+ /*
+ * Get the multiplex file descriptor to the lower streams. Generally this is lost
+ * once a module is I_PLINK, we need to reobtain it for inserting/removing ourselves from the stack.
+ */
+ int Ip6MuxFd;
+ rc = vboxNetFltSolarisMuxIdToFd(pUdp6VNode, Ip6Interface.lifr_ip_muxid, &Ip6MuxFd);
+ if (RT_SUCCESS(rc))
+ {
+ /*
+ * We need to I_PUNLINK on these multiplexor IDs before we can start
+ * operating on the lower stream as insertions are direct operations on the lower stream.
+ */
+ rc = strioctl(pUdp6VNode, I_PUNLINK, (intptr_t)Ip6Interface.lifr_ip_muxid, 0, K_TO_K, kcred, &ret);
+ if (!rc)
+ {
+ /*
+ * Obtain the vnode from the useless userland file descriptor.
+ */
+ file_t *pIpFile = getf(Ip6MuxFd);
+ if ( pIpFile
+ && VNODE_FOR_FILE_T(pIpFile))
+ {
+ vnode_t *pIp6VNode = VNODE_FOR_FILE_T(pIpFile);
+
+ /*
+ * Find the position on the host stack for attaching/detaching ourselves.
+ */
+ rc = vboxNetFltSolarisDetermineModPos(fAttach, pIp6VNode, &StrMod.pos);
+ if (RT_SUCCESS(rc))
+ {
+ /*
+ * Set global data which will be grabbed by ModOpen.
+ * There is a known (though very unlikely) race here because
+ * of the inability to pass user data while inserting.
+ */
+ rc = RTSemFastMutexRequest(g_VBoxNetFltSolarisMtx);
+ AssertRCReturn(rc, rc);
+
+ if (fAttach)
+ {
+ g_VBoxNetFltSolarisInstance = pThis;
+ g_VBoxNetFltSolarisStreamType = kIp6Stream;
+ }
+
+ /*
+ * Inject/Eject from the host IPv6 stack.
+ */
+ rc = strioctl(pIp6VNode, fAttach ? _I_INSERT : _I_REMOVE, (intptr_t)&StrMod, 0, K_TO_K,
+ g_pVBoxNetFltSolarisCred, &ret);
+
+ if (fAttach)
+ {
+ g_VBoxNetFltSolarisInstance = NULL;
+ g_VBoxNetFltSolarisStreamType = kUndefined;
+ }
+
+ RTSemFastMutexRelease(g_VBoxNetFltSolarisMtx);
+
+ if (!rc)
+ {
+ /*
+ * Our job's not yet over; we need to relink the upper and lower streams
+ * otherwise we've pretty much screwed up the host interface.
+ */
+ rc = vboxNetFltSolarisRelinkIp6(pUdp6VNode, &Ip6Interface, Ip6MuxFd);
+ if (RT_SUCCESS(rc))
+ {
+ /*
+ * Close the devices ONLY during the return from function case; otherwise
+ * we end up close twice which is an instant kernel panic.
+ */
+ vboxNetFltSolarisCloseDev(pUdp6VNodeHeld, pUdp6User);
+ ldi_close(Ip6DevHandle, FREAD | FWRITE, kcred);
+ releasef(Ip6MuxFd);
+
+ Log((DEVICE_NAME ":vboxNetFltSolarisAttachIp6: Success! %s %s@(IPv6:%d) "
+ "%s interface %s\n", fAttach ? "Injected" : "Ejected", StrMod.mod_name,
+ StrMod.pos, fAttach ? "to" : "from", pThis->szName));
+ return VINF_SUCCESS;
+ }
+ else
+ {
+ LogRel((DEVICE_NAME ":vboxNetFltSolarisAttachIp6: Relinking failed. Mode=%s rc=%d.\n",
+ fAttach ? "inject" : "eject", rc));
+ }
+
+ if (fAttach)
+ strioctl(pIp6VNode, _I_REMOVE, (intptr_t)&StrMod, 0, K_TO_K, kcred, &ret);
+
+ vboxNetFltSolarisRelinkIp6(pUdp6VNode, &Ip6Interface, Ip6MuxFd);
+ }
+ else
+ {
+ LogRel((DEVICE_NAME ":vboxNetFltSolarisAttachIp6: failed to %s the IP stack. rc=%d\n",
+ fAttach ? "inject into" : "eject from", rc));
+ }
+ }
+ else
+ LogRel((DEVICE_NAME ":vboxNetFltSolarisAttachIp6: failed to find position. rc=%d\n", rc));
+
+ releasef(Ip6MuxFd);
+ }
+ else
+ LogRel((DEVICE_NAME ":vboxNetFltSolarisAttachIp6: failed to get vnode from MuxFd.\n"));
+ }
+ else
+ LogRel((DEVICE_NAME ":vboxNetFltSolarisAttachIp6: failed to unlink upper stream rc=%d.\n", rc));
+ }
+ else
+ LogRel((DEVICE_NAME ":vboxNetFltSolarisAttachIp6: failed to get MuxFd from MuxId. rc=%d\n", rc));
+ }
+ else
+ LogRel((DEVICE_NAME ":vboxNetFltSolarisAttachIp6: failed to get Mux Ids. rc=%d\n", rc));
+
+ vboxNetFltSolarisCloseDev(pUdp6VNodeHeld, pUdp6User);
+ }
+ else
+ LogRel((DEVICE_NAME ":vboxNetFltSolarisAttachIp6: failed to open UDP. rc=%d\n", rc));
+
+ rc = VERR_INTNET_FLT_IF_FAILED;
+ }
+ else
+ {
+ Log((DEVICE_NAME ":vboxNetFltSolarisAttachIp6: failed to get IPv6 flags.\n", pThis->szName));
+ rc = VERR_INTNET_FLT_IF_NOT_FOUND;
+ }
+
+ ldi_close(Ip6DevHandle, FREAD | FWRITE, kcred);
+
+ return rc;
+}
+
+
+#ifdef VBOXNETFLT_SOLARIS_IPV6_POLLING
+/**
+ * Ipv6 dynamic attachment timer callback to attach to the Ipv6 stream if needed.
+ *
+ * @param pTimer Pointer to the timer.
+ * @param pvData Opaque pointer to the instance.
+ * @param iTick Timer tick (unused).
+ */
+static void vboxNetFltSolarispIp6Timer(PRTTIMER pTimer, void *pvData, uint64_t iTick)
+{
+ LogFunc((DEVICE_NAME ":vboxNetFltSolarispIp6Timer pTimer=%p pvData=%p\n", pTimer, pvData));
+
+ PVBOXNETFLTINS pThis = (PVBOXNETFLTINS)pvData;
+ if ( RT_LIKELY(pThis)
+ && RT_LIKELY(pTimer))
+ {
+ vboxnetflt_stream_t *pIp6Stream = ASMAtomicUoReadPtrT(&pThis->u.s.pIp6Stream, vboxnetflt_stream_t *);
+ bool fIp6Attaching = ASMAtomicUoReadBool(&pThis->u.s.fAttaching);
+ if ( !pIp6Stream
+ && !fIp6Attaching)
+ {
+ int rc = RTSemFastMutexRequest(pThis->u.s.hPollMtx);
+ if (RT_SUCCESS(rc))
+ {
+ ASMAtomicUoWriteBool(&pThis->u.s.fAttaching, true);
+
+ vboxNetFltSolarisAttachIp6(pThis, true /* fAttach */);
+
+ ASMAtomicUoWriteBool(&pThis->u.s.fAttaching, false);
+ RTSemFastMutexRelease(pThis->u.s.hPollMtx);
+ }
+ else
+ LogRel((DEVICE_NAME ":vboxNetFltSolarispIp6Timer failed to obtain mutex. rc=%Rrc\n", rc));
+ }
+ }
+
+ NOREF(iTick);
+}
+
+
+/**
+ * Setups up a kernel timer based on the driver property for attaching to IPv6 stream
+ * whenever the stream gets plumbed for the interface.
+ *
+ * @returns VBox status code.
+ * @param pThis The instance.
+ */
+static int vboxNetFltSolarisSetupIp6Polling(PVBOXNETFLTINS pThis)
+{
+ LogFunc((DEVICE_NAME ":vboxNetFltSolarisSetupIp6Polling pThis=%p\n", pThis));
+
+ int rc = VERR_GENERAL_FAILURE;
+ vboxnetflt_promisc_stream_t *pPromiscStream = ASMAtomicUoReadPtrT(&pThis->u.s.pPromiscStream, vboxnetflt_promisc_stream_t *);
+ if (RT_LIKELY(pPromiscStream))
+ {
+ if (RT_LIKELY(pPromiscStream->pIp6Timer == NULL))
+ {
+ /*
+ * Validate IPv6 polling interval.
+ */
+ int Interval = g_VBoxNetFltSolarisPollInterval;
+ if (Interval < 1 || Interval > 120)
+ {
+ LogRel((DEVICE_NAME ":vboxNetFltSolarisSetupIp6Polling: Invalid polling interval %d. Expected between"
+ " 1 and 120 secs.\n", Interval));
+ return VERR_INVALID_PARAMETER;
+ }
+
+ /*
+ * Setup kernel poll timer.
+ */
+ rc = RTTimerCreateEx(&pPromiscStream->pIp6Timer, Interval * (uint64_t)1000000000, RTTIMER_FLAGS_CPU_ANY,
+ vboxNetFltSolarispIp6Timer, (void *)pThis);
+ if (RT_SUCCESS(rc))
+ {
+ rc = RTTimerStart(pPromiscStream->pIp6Timer, 10 * (uint64_t)1000000000 /* 10 seconds to blastoff */);
+ Log((DEVICE_NAME ":vboxNetFltSolarisSetupIp6Polling: Ipv6 %d second timer begins firing in 10 seconds.\n",
+ Interval));
+ }
+ else
+ LogRel((DEVICE_NAME ":vboxNetFltSolarisSetupIp6Polling: Failed to create timer. rc=%d\n", rc));
+ }
+ else
+ {
+ LogRel((DEVICE_NAME ":vboxNetFltSolarisSetupIp6Polling: Polling already started.\n"));
+ rc = VINF_SUCCESS;
+ }
+ }
+ return rc;
+}
+#endif
+
+/**
+ * Wrapper for detaching ourselves from the interface.
+ *
+ * @returns VBox status code.
+ * @param pThis The instance.
+ * @remarks Owns the globals mutex, so re-requesting it anytime during this phase
+ * would panic the system (e.g. in vboxNetFltSolarisFindInstance).
+ */
+static int vboxNetFltSolarisDetachFromInterface(PVBOXNETFLTINS pThis)
+{
+ LogFunc((DEVICE_NAME ":vboxNetFltSolarisDetachFromInterface pThis=%p\n", pThis));
+
+ ASMAtomicWriteBool(&pThis->fDisconnectedFromHost, true);
+ vboxNetFltSolarisCloseStream(pThis);
+ int rc = VINF_SUCCESS;
+ if (pThis->u.s.pIp4Stream)
+ rc = vboxNetFltSolarisAttachIp4(pThis, false /* fAttach */);
+ if (pThis->u.s.pIp6Stream)
+ rc = vboxNetFltSolarisAttachIp6(pThis, false /* fAttach */);
+
+#ifdef VBOXNETFLT_SOLARIS_IPV6_POLLING
+ vboxnetflt_promisc_stream_t *pPromiscStream = ASMAtomicUoReadPtrT(&pThis->u.s.pPromiscStream, vboxnetflt_promisc_stream_t *);
+ if ( pPromiscStream
+ && pPromiscStream->pIp6Timer == NULL)
+ {
+ RTTimerStop(pPromiscStream->pIp6Timer);
+ RTTimerDestroy(pPromiscStream->pIp6Timer);
+ ASMAtomicUoWriteNullPtr(&pPromiscStream->pIp6Timer);
+ }
+#endif
+
+ return rc;
+}
+
+
+/**
+ * Wrapper for attaching ourselves to the interface.
+ *
+ * @returns VBox status code.
+ * @param pThis The instance.
+ */
+static int vboxNetFltSolarisAttachToInterface(PVBOXNETFLTINS pThis)
+{
+ LogFunc((DEVICE_NAME ":vboxNetFltSolarisAttachToInterface pThis=%p\n", pThis));
+
+ /*
+ * Since this is asynchronous streams injection, let the attach succeed before we can start
+ * processing the stream.
+ */
+ ASMAtomicWriteBool(&pThis->fDisconnectedFromHost, true);
+ int rc = vboxNetFltSolarisOpenStream(pThis);
+ if (RT_SUCCESS(rc))
+ {
+ rc = vboxNetFltSolarisAttachIp4(pThis, true /* fAttach */);
+ if (RT_SUCCESS(rc))
+ {
+ /*
+ * Ipv6 attaching is optional and can fail. We don't bother to bring down the whole
+ * attach process just if Ipv6 interface is unavailable.
+ */
+ int rc2 = vboxNetFltSolarisAttachIp6(pThis, true /* fAttach */);
+
+#ifdef VBOXNETFLT_SOLARIS_IPV6_POLLING
+ /*
+ * If Ip6 interface is not plumbed and an Ip6 polling interval is specified, we need
+ * to begin polling to attach on the Ip6 interface whenever it comes up.
+ */
+ if ( rc2 == VERR_INTNET_FLT_IF_NOT_FOUND
+ && g_VBoxNetFltSolarisPollInterval != -1)
+ {
+ int rc3 = vboxNetFltSolarisSetupIp6Polling(pThis);
+ if (RT_FAILURE(rc3))
+ {
+ /*
+ * If we failed to setup Ip6 polling, warn in the release log and continue.
+ */
+ LogRel((DEVICE_NAME ":vboxNetFltSolarisAttachToInterface IPv6 polling inactive. rc=%Rrc\n", rc3));
+ }
+ }
+#endif
+
+ /*
+ * Report promiscuousness and capabilities.
+ */
+ if (vboxNetFltTryRetainBusyNotDisconnected(pThis))
+ {
+ Assert(pThis->pSwitchPort);
+ /** @todo There is no easy way of obtaining the global host side promiscuous
+ * counter. Currently we just return false. */
+ pThis->pSwitchPort->pfnReportPromiscuousMode(pThis->pSwitchPort, false);
+ pThis->pSwitchPort->pfnReportGsoCapabilities(pThis->pSwitchPort, 0, INTNETTRUNKDIR_WIRE | INTNETTRUNKDIR_HOST);
+ pThis->pSwitchPort->pfnReportNoPreemptDsts(pThis->pSwitchPort, 0 /* none */);
+ vboxNetFltRelease(pThis, true /*fBusy*/);
+ }
+
+ /*
+ * Ipv4 is successful, and maybe Ipv6, we're ready for transfers.
+ */
+ ASMAtomicWriteBool(&pThis->fDisconnectedFromHost, false);
+
+ return VINF_SUCCESS;
+ }
+
+ vboxNetFltSolarisCloseStream(pThis);
+ }
+ else
+ LogRel((DEVICE_NAME ":vboxNetFltSolarisAttachToInterface vboxNetFltSolarisOpenStream failed rc=%Rrc\n", rc));
+
+ return rc;
+}
+
+
+/**
+ * Create a solaris message block from the SG list.
+ *
+ * @returns Solaris message block.
+ * @param pThis The instance.
+ * @param pSG Pointer to the scatter-gather list.
+ * @param fDst The destination mask, INTNETTRUNKDIR_XXX. Ignored.
+ */
+static mblk_t *vboxNetFltSolarisMBlkFromSG(PVBOXNETFLTINS pThis, PINTNETSG pSG, uint32_t fDst)
+{
+ LogFunc((DEVICE_NAME ":vboxNetFltSolarisMBlkFromSG pThis=%p pSG=%p\n", pThis, pSG));
+
+ mblk_t *pMsg = allocb(pSG->cbTotal, BPRI_MED);
+ if (RT_UNLIKELY(!pMsg))
+ {
+ LogRel((DEVICE_NAME ":vboxNetFltSolarisMBlkFromSG failed to alloc %d bytes for mblk_t.\n", pSG->cbTotal));
+ return NULL;
+ }
+
+ /*
+ * Single buffer copy. Maybe later explore the
+ * need/possibility for using a mblk_t chain rather.
+ */
+ for (unsigned i = 0; i < pSG->cSegsUsed; i++)
+ {
+ if (pSG->aSegs[i].pv)
+ {
+ bcopy(pSG->aSegs[i].pv, pMsg->b_wptr, pSG->aSegs[i].cb);
+ pMsg->b_wptr += pSG->aSegs[i].cb;
+ }
+ }
+ DB_TYPE(pMsg) = M_DATA;
+ return pMsg;
+}
+
+
+/**
+ * Calculate the number of segments required for this message block.
+ *
+ * @returns Number of segments.
+ * @param pThis The instance
+ * @param pMsg Pointer to the data message.
+ */
+static unsigned vboxNetFltSolarisMBlkCalcSGSegs(PVBOXNETFLTINS pThis, mblk_t *pMsg)
+{
+ unsigned cSegs = 0;
+ for (mblk_t *pCur = pMsg; pCur; pCur = pCur->b_cont)
+ if (MBLKL(pCur))
+ cSegs++;
+
+#ifdef PADD_RUNT_FRAMES_FROM_HOST
+ if (msgdsize(pMsg) < 60)
+ cSegs++;
+#endif
+
+ NOREF(pThis);
+ return RT_MAX(cSegs, 1);
+}
+
+
+/**
+ * Initializes an SG list from the given message block.
+ *
+ * @returns VBox status code.
+ * @param pThis The instance.
+ * @param pMsg Pointer to the data message.
+ The caller must ensure it's not a control message block.
+ * @param pSG Pointer to the SG.
+ * @param cSegs Number of segments in the SG.
+ * This should match the number in the message block exactly!
+ * @param fSrc The source of the message.
+ */
+static int vboxNetFltSolarisMBlkToSG(PVBOXNETFLTINS pThis, mblk_t *pMsg, PINTNETSG pSG, unsigned cSegs, uint32_t fSrc)
+{
+ LogFunc((DEVICE_NAME ":vboxNetFltSolarisMBlkToSG pThis=%p pMsg=%p pSG=%p cSegs=%d\n", pThis, pMsg, pSG, cSegs));
+
+ /*
+ * Convert the message block to segments. Work INTNETSG::cbTotal.
+ */
+ IntNetSgInitTempSegs(pSG, 0 /*cbTotal*/, cSegs, 0 /*cSegsUsed*/);
+ mblk_t *pCur = pMsg;
+ unsigned iSeg = 0;
+ while (pCur)
+ {
+ size_t cbSeg = MBLKL(pCur);
+ if (cbSeg)
+ {
+ void *pvSeg = pCur->b_rptr;
+ pSG->aSegs[iSeg].pv = pvSeg;
+ pSG->aSegs[iSeg].cb = cbSeg;
+ pSG->aSegs[iSeg].Phys = NIL_RTHCPHYS;
+ pSG->cbTotal += cbSeg;
+ iSeg++;
+ }
+ pCur = pCur->b_cont;
+ }
+ pSG->cSegsUsed = iSeg;
+
+#ifdef PADD_RUNT_FRAMES_FROM_HOST
+ if (pSG->cbTotal < 60 && (fSrc & INTNETTRUNKDIR_HOST))
+ {
+ Log((DEVICE_NAME ":vboxNetFltSolarisMBlkToSG pulling up to length.\n"));
+
+ static uint8_t const s_abZero[128] = {0};
+ pSG->aSegs[iSeg].Phys = NIL_RTHCPHYS;
+ pSG->aSegs[iSeg].pv = (void *)&s_abZero[0];
+ pSG->aSegs[iSeg].cb = 60 - pSG->cbTotal;
+ pSG->cbTotal = 60;
+ pSG->cSegsUsed++;
+ Assert(iSeg + 1 < cSegs);
+ }
+#endif
+
+ Log((DEVICE_NAME ":vboxNetFltSolarisMBlkToSG iSeg=%d pSG->cbTotal=%d msgdsize=%d\n", iSeg, pSG->cbTotal, msgdsize(pMsg)));
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * Converts raw mode M_DATA messages to M_PROTO DL_UNITDATA_IND format.
+ *
+ * @returns VBox status code.
+ * @param pMsg Pointer to the raw message.
+ * @param ppDlpiMsg Where to store the M_PROTO message.
+ *
+ * @remarks The original raw message would be no longer valid and will be
+ * linked as part of the new DLPI message. Callers must take care
+ * not to use the raw message if this routine is successful.
+ */
+static int vboxNetFltSolarisRawToUnitData(mblk_t *pMsg, mblk_t **ppDlpiMsg)
+{
+ LogFunc((DEVICE_NAME ":vboxNetFltSolarisRawToUnitData pMsg=%p\n", pMsg));
+
+ if (DB_TYPE(pMsg) != M_DATA)
+ return VERR_NO_MEMORY;
+
+ size_t cbMsg = sizeof(dl_unitdata_ind_t) + 2 * sizeof(vboxnetflt_dladdr_t);
+ mblk_t *pDlpiMsg = allocb(cbMsg, BPRI_MED);
+ if (RT_UNLIKELY(!pDlpiMsg))
+ return VERR_NO_MEMORY;
+
+ DB_TYPE(pDlpiMsg) = M_PROTO;
+ dl_unitdata_ind_t *pDlpiData = (dl_unitdata_ind_t *)pDlpiMsg->b_rptr;
+ pDlpiData->dl_primitive = DL_UNITDATA_IND;
+ pDlpiData->dl_dest_addr_length = VBOXNETFLT_DLADDRL;
+ pDlpiData->dl_dest_addr_offset = sizeof(dl_unitdata_ind_t);
+ pDlpiData->dl_src_addr_length = VBOXNETFLT_DLADDRL;
+ pDlpiData->dl_src_addr_offset = VBOXNETFLT_DLADDRL + sizeof(dl_unitdata_ind_t);
+
+ PRTNETETHERHDR pEthHdr = (PRTNETETHERHDR)pMsg->b_rptr;
+
+ vboxnetflt_dladdr_t *pDlAddr = (vboxnetflt_dladdr_t *)(pDlpiMsg->b_rptr + pDlpiData->dl_dest_addr_offset);
+ pDlAddr->SAP = RT_BE2H_U16(pEthHdr->EtherType);
+ bcopy(&pEthHdr->DstMac, &pDlAddr->Mac, sizeof(RTMAC));
+
+ pDlAddr = (vboxnetflt_dladdr_t *)(pDlpiMsg->b_rptr + pDlpiData->dl_src_addr_offset);
+ pDlAddr->SAP = RT_BE2H_U16(pEthHdr->EtherType);
+ bcopy(&pEthHdr->SrcMac, &pDlAddr->Mac, sizeof(RTMAC));
+
+ pDlpiMsg->b_wptr = pDlpiMsg->b_rptr + cbMsg;
+
+ /* Make the message point to the protocol header */
+ pMsg->b_rptr += sizeof(RTNETETHERHDR);
+
+ pDlpiMsg->b_cont = pMsg;
+ *ppDlpiMsg = pDlpiMsg;
+ return VINF_SUCCESS;
+}
+
+#if 0
+/**
+ * Converts DLPI M_PROTO messages to the raw mode M_DATA format.
+ *
+ * @returns VBox status code.
+ * @param pMsg Pointer to the M_PROTO message.
+ * @param ppRawMsg Where to store the converted message.
+ *
+ * @remarks If successful, the original pMsg is no longer valid, it will be deleted.
+ * Callers must take care not to continue to use pMsg after a successful
+ * call to this conversion routine.
+ */
+static int vboxNetFltSolarisUnitDataToRaw(PVBOXNETFLTINS pThis, mblk_t *pMsg, mblk_t **ppRawMsg)
+{
+ LogFunc((DEVICE_NAME ":vboxNetFltSolarisUnitDataToRaw pMsg=%p\n", pMsg));
+
+ if ( !pMsg->b_cont
+ || DB_TYPE(pMsg) != M_PROTO)
+ {
+ LogRel((DEVICE_NAME ":vboxNetFltSolarisUnitDataToRaw invalid input message.\n"));
+ return VERR_NET_PROTOCOL_ERROR;
+ }
+
+ /*
+ * Upstream consumers send/receive packets in the fast path mode.
+ * We of course need to convert them into raw ethernet frames.
+ */
+ RTNETETHERHDR EthHdr;
+ union DL_primitives *pPrim = (union DL_primitives *)pMsg->b_rptr;
+ switch (pPrim->dl_primitive)
+ {
+ case DL_UNITDATA_IND:
+ {
+ /*
+ * Receive side.
+ */
+ dl_unitdata_ind_t *pDlpiMsg = (dl_unitdata_ind_t *)pMsg->b_rptr;
+ bcopy(pMsg->b_rptr + pDlpiMsg->dl_dest_addr_offset, &EthHdr.DstMac, sizeof(EthHdr.DstMac));
+ bcopy(pMsg->b_rptr + pDlpiMsg->dl_src_addr_offset, &EthHdr.SrcMac, sizeof(EthHdr.SrcMac));
+
+ vboxnetflt_dladdr_t *pDLSapAddr = (vboxnetflt_dladdr_t *)(pMsg->b_rptr + pDlpiMsg->dl_dest_addr_offset);
+ EthHdr.EtherType = RT_H2BE_U16(pDLSapAddr->SAP);
+
+ break;
+ }
+
+ case DL_UNITDATA_REQ:
+ {
+ /*
+ * Send side.
+ */
+ dl_unitdata_req_t *pDlpiMsg = (dl_unitdata_req_t *)pMsg->b_rptr;
+
+ bcopy(pMsg->b_rptr + pDlpiMsg->dl_dest_addr_offset, &EthHdr.DstMac, sizeof(EthHdr.DstMac));
+ bcopy(&pThis->u.s.MacAddr, &EthHdr.SrcMac, sizeof(EthHdr.SrcMac));
+
+ vboxnetflt_dladdr_t *pDLSapAddr = (vboxnetflt_dladdr_t *)(pMsg->b_rptr + pDlpiMsg->dl_dest_addr_offset);
+ EthHdr.EtherType = RT_H2BE_U16(pDLSapAddr->SAP);
+
+ break;
+ }
+
+ default:
+ {
+ LogRel((DEVICE_NAME ":vboxNetFltSolarisUnitDataToRaw Unknown M_PROTO. This shouldn't be happening!!"));
+ return VERR_NET_PROTOCOL_ERROR;
+ }
+ }
+
+ /*
+ * Let us just link it as a mblk_t chain rather than re-copy the entire message.
+ * The vboxNetFltSolarisMBlkToSG function will handle chained mblk_t's.
+ */
+ size_t cbLen = sizeof(EthHdr);
+ mblk_t *pEtherMsg = allocb(cbLen, BPRI_MED);
+ if (RT_UNLIKELY(!pEtherMsg))
+ return VERR_NO_MEMORY;
+
+ DB_TYPE(pEtherMsg) = M_DATA;
+ bcopy(&EthHdr, pEtherMsg->b_wptr, sizeof(EthHdr));
+ pEtherMsg->b_wptr += cbLen;
+
+ pEtherMsg->b_cont = pMsg->b_cont;
+
+ /*
+ * Change the chained blocks to type M_DATA.
+ */
+ for (mblk_t *pTmp = pEtherMsg->b_cont; pTmp; pTmp = pTmp->b_cont)
+ DB_TYPE(pTmp) = M_DATA;
+
+ pMsg->b_cont = NULL;
+ freemsg(pMsg);
+
+ *ppRawMsg = pEtherMsg;
+ return VINF_SUCCESS;
+}
+#endif
+
+/**
+ * Initializes a packet identifier.
+ *
+ * @param pTag Pointer to the packed identifier.
+ * @param pMsg Pointer to the message to be identified.
+ *
+ * @remarks Warning!!! This function assumes 'pMsg' is an unchained message.
+ */
+static inline void vboxNetFltSolarisInitPacketId(PVBOXNETFLTPACKETID pTag, mblk_t *pMsg)
+{
+ PCRTNETETHERHDR pEthHdr = (PCRTNETETHERHDR)pMsg->b_rptr;
+ size_t cbMsg = MBLKL(pMsg);
+
+ pTag->cbPacket = cbMsg;
+ pTag->Checksum = RTCrc32(pMsg->b_rptr, cbMsg);
+ bcopy(&pEthHdr->SrcMac, &pTag->SrcMac, sizeof(RTMAC));
+ bcopy(&pEthHdr->DstMac, &pTag->DstMac, sizeof(RTMAC));
+}
+
+
+/**
+ * Queues a packet for loopback elimination.
+ *
+ * @returns VBox status code.
+ * @param pThis The instance.
+ * @param pPromiscStream Pointer to the promiscuous stream.
+ * @param pMsg Pointer to the message.
+ */
+static int vboxNetFltSolarisQueueLoopback(PVBOXNETFLTINS pThis, vboxnetflt_promisc_stream_t *pPromiscStream, mblk_t *pMsg)
+{
+ Assert(pThis);
+ Assert(pMsg);
+ Assert(DB_TYPE(pMsg) == M_DATA);
+ Assert(pPromiscStream);
+
+ LogFunc((DEVICE_NAME ":vboxNetFltSolarisQueueLoopback pThis=%p pPromiscStream=%p pMsg=%p\n", pThis, pPromiscStream, pMsg));
+
+ if (RT_UNLIKELY(pMsg->b_cont))
+ {
+ /*
+ * We don't currently make chained messages in on Xmit
+ * so this only needs to be supported when we do that.
+ */
+ return VERR_NOT_SUPPORTED;
+ }
+
+ size_t cbMsg = MBLKL(pMsg);
+ if (RT_UNLIKELY(cbMsg < sizeof(RTNETETHERHDR)))
+ return VERR_NET_MSG_SIZE;
+
+ int rc = VINF_SUCCESS;
+ mutex_enter(&pThis->u.s.hMtx);
+
+ PVBOXNETFLTPACKETID pCur = NULL;
+ if (pPromiscStream->cLoopback < VBOXNETFLT_LOOPBACK_SIZE
+ || ( pPromiscStream->pHead
+ && pPromiscStream->pHead->cbPacket == 0))
+ {
+ do
+ {
+ if (!pPromiscStream->pHead)
+ {
+ pCur = RTMemAlloc(sizeof(VBOXNETFLTPACKETID));
+ if (RT_UNLIKELY(!pCur))
+ {
+ rc = VERR_NO_MEMORY;
+ break;
+ }
+
+ vboxNetFltSolarisInitPacketId(pCur, pMsg);
+
+ pCur->pNext = NULL;
+ pPromiscStream->pHead = pCur;
+ pPromiscStream->pTail = pCur;
+ pPromiscStream->cLoopback++;
+
+ Log((DEVICE_NAME ":vboxNetFltSolarisQueueLoopback initialized head. checksum=%u.\n",
+ pPromiscStream->pHead->Checksum));
+ break;
+ }
+ else if ( pPromiscStream->pHead
+ && pPromiscStream->pHead->cbPacket == 0)
+ {
+ pCur = pPromiscStream->pHead;
+ vboxNetFltSolarisInitPacketId(pCur, pMsg);
+
+ Log((DEVICE_NAME ":vboxNetFltSolarisQueueLoopback re-used head checksum=%u cLoopback=%d.\n",
+ pCur->Checksum, pPromiscStream->cLoopback));
+ break;
+ }
+ else
+ {
+ pCur = RTMemAlloc(sizeof(VBOXNETFLTPACKETID));
+ if (RT_UNLIKELY(!pCur))
+ {
+ rc = VERR_NO_MEMORY;
+ break;
+ }
+
+ vboxNetFltSolarisInitPacketId(pCur, pMsg);
+
+ pCur->pNext = pPromiscStream->pHead;
+ pPromiscStream->pHead = pCur;
+ pPromiscStream->cLoopback++;
+
+ Log((DEVICE_NAME ":vboxNetFltSolarisQueueLoopback added head checksum=%u cLoopback=%d.\n", pCur->Checksum,
+ pPromiscStream->cLoopback));
+ break;
+ }
+ } while (0);
+ }
+ else
+ {
+ /*
+ * Maximum loopback queue size reached. Re-use tail as head.
+ */
+ Assert(pPromiscStream->pHead);
+ Assert(pPromiscStream->pTail);
+
+ /*
+ * Find tail's previous item.
+ */
+ PVBOXNETFLTPACKETID pPrev = NULL;
+ pCur = pPromiscStream->pHead;
+
+ /** @todo consider if this is worth switching to a double linked list... */
+ while (pCur != pPromiscStream->pTail)
+ {
+ pPrev = pCur;
+ pCur = pCur->pNext;
+ }
+
+ pPromiscStream->pTail = pPrev;
+ pPromiscStream->pTail->pNext = NULL;
+ pCur->pNext = pPromiscStream->pHead;
+ pPromiscStream->pHead = pCur;
+
+ vboxNetFltSolarisInitPacketId(pCur, pMsg);
+ Log((DEVICE_NAME ":vboxNetFltSolarisQueueLoopback recycled tail!! checksum=%u cLoopback=%d\n", pCur->Checksum,
+ pPromiscStream->cLoopback));
+ }
+
+ mutex_exit(&pThis->u.s.hMtx);
+
+ return rc;
+}
+
+
+/**
+ * Checks if the packet is enqueued for loopback as our own packet.
+ *
+ * @returns If it's our packet, returns true after dequeuing it, otherwise false.
+ * @param pThis The instance.
+ * @param pPromiscStream Pointer to the promiscuous stream.
+ * @param pMsg Pointer to the message.
+ */
+static bool vboxNetFltSolarisIsOurMBlk(PVBOXNETFLTINS pThis, vboxnetflt_promisc_stream_t *pPromiscStream, mblk_t *pMsg)
+{
+ Assert(pThis);
+ Assert(pPromiscStream);
+ Assert(pMsg);
+ Assert(DB_TYPE(pMsg) == M_DATA);
+
+ LogFunc((DEVICE_NAME ":vboxNetFltSolarisIsOurMBlk pThis=%p pMsg=%p\n", pThis, pMsg));
+
+ if (pMsg->b_cont)
+ {
+ /** Handle this when Xmit makes chained messages */
+ return false;
+ }
+
+ size_t cbMsg = MBLKL(pMsg);
+ if (cbMsg < sizeof(RTNETETHERHDR))
+ return false;
+
+ mutex_enter(&pThis->u.s.hMtx);
+
+ PVBOXNETFLTPACKETID pPrev = NULL;
+ PVBOXNETFLTPACKETID pCur = pPromiscStream->pHead;
+ bool fIsOurPacket = false;
+ while (pCur)
+ {
+ PCRTNETETHERHDR pEthHdr = (PCRTNETETHERHDR)pMsg->b_rptr;
+ if ( pCur->cbPacket != cbMsg
+ || pCur->SrcMac.au8[0] != pEthHdr->SrcMac.au8[0]
+ || pCur->SrcMac.au8[1] != pEthHdr->SrcMac.au8[1]
+ || pCur->SrcMac.au8[2] != pEthHdr->SrcMac.au8[2]
+ || pCur->SrcMac.au8[3] != pEthHdr->SrcMac.au8[3]
+ || pCur->SrcMac.au8[4] != pEthHdr->SrcMac.au8[4]
+ || pCur->SrcMac.au8[5] != pEthHdr->SrcMac.au8[5]
+ || pCur->DstMac.au8[0] != pEthHdr->DstMac.au8[0]
+ || pCur->DstMac.au8[1] != pEthHdr->DstMac.au8[1]
+ || pCur->DstMac.au8[2] != pEthHdr->DstMac.au8[2]
+ || pCur->DstMac.au8[3] != pEthHdr->DstMac.au8[3]
+ || pCur->DstMac.au8[4] != pEthHdr->DstMac.au8[4]
+ || pCur->DstMac.au8[5] != pEthHdr->DstMac.au8[5])
+ {
+ pPrev = pCur;
+ pCur = pCur->pNext;
+ continue;
+ }
+
+ uint16_t Checksum = RTCrc32(pMsg->b_rptr, cbMsg);
+ if (pCur->Checksum != Checksum)
+ {
+ pPrev = pCur;
+ pCur = pCur->pNext;
+ continue;
+ }
+
+ /*
+ * Yes, it really is our own packet, mark it as handled
+ * and move it as a "free slot" to the head and return success.
+ */
+ pCur->cbPacket = 0;
+ if (pPrev)
+ {
+ if (!pCur->pNext)
+ pPromiscStream->pTail = pPrev;
+
+ pPrev->pNext = pCur->pNext;
+ pCur->pNext = pPromiscStream->pHead;
+ pPromiscStream->pHead = pCur;
+ }
+ fIsOurPacket = true;
+
+ Log((DEVICE_NAME ":vboxNetFltSolarisIsOurMBlk found packet %p Checksum=%u cLoopback=%d\n", pMsg, Checksum,
+ pPromiscStream->cLoopback));
+ break;
+ }
+
+ Log((DEVICE_NAME ":vboxNetFltSolarisIsOurMBlk returns %d.\n", fIsOurPacket));
+ mutex_exit(&pThis->u.s.hMtx);
+ return fIsOurPacket;
+}
+
+
+/**
+ * Helper.
+ */
+DECLINLINE(bool) vboxNetFltPortSolarisIsHostMac(PVBOXNETFLTINS pThis, PCRTMAC pMac)
+{
+ /*
+ * MAC address change acknowledgements are intercepted on the read side
+ * hence theoretically we are always update to date with any changes.
+ */
+ return pThis->u.s.MacAddr.au16[0] == pMac->au16[0]
+ && pThis->u.s.MacAddr.au16[1] == pMac->au16[1]
+ && pThis->u.s.MacAddr.au16[2] == pMac->au16[2];
+}
+
+
+/**
+ * Worker for routing messages from the wire or from the host.
+ *
+ * @returns VBox status code.
+ * @param pThis The instance.
+ * @param pStream Pointer to the stream.
+ * @param pQueue Pointer to the read queue.
+ * @param pMsg Pointer to the message.
+ */
+static int vboxNetFltSolarisRecv(PVBOXNETFLTINS pThis, vboxnetflt_stream_t *pStream, queue_t *pQueue, mblk_t *pMsg)
+{
+ LogFunc((DEVICE_NAME ":vboxNetFltSolarisRecv pThis=%p pMsg=%p\n", pThis, pMsg));
+
+ AssertCompile(sizeof(struct ether_header) == sizeof(RTNETETHERHDR));
+ Assert(pStream->Type == kPromiscStream);
+
+ vboxnetflt_promisc_stream_t *pPromiscStream = ASMAtomicUoReadPtrT(&pThis->u.s.pPromiscStream, vboxnetflt_promisc_stream_t *);
+ if (RT_UNLIKELY(!pPromiscStream))
+ {
+ LogRel((DEVICE_NAME ":Promiscuous stream missing!! Failing to receive packet.\n"));
+ return VERR_INVALID_POINTER;
+ }
+
+ /*
+ * Paranoia...
+ */
+ if (RT_UNLIKELY(MBLKL(pMsg) < sizeof(RTNETETHERHDR)))
+ {
+ size_t cbMsg = msgdsize(pMsg);
+ if (cbMsg < sizeof(RTNETETHERHDR))
+ {
+ LogRel((DEVICE_NAME ":vboxNetFltSolarisRecv %s: packet too small. Dropping packet.\n", pThis->szName));
+ return VINF_SUCCESS;
+ }
+
+ mblk_t *pFullMsg = msgpullup(pMsg, -1 /* all data blocks */);
+ if (pFullMsg)
+ {
+ freemsg(pMsg);
+ pMsg = pFullMsg;
+ }
+ else
+ {
+ LogRel((DEVICE_NAME ":vboxNetFltSolarisRecv msgpullup failed.\n"));
+ return VERR_NO_MEMORY;
+ }
+ }
+
+ /*
+ * Don't loopback packets we transmit to the wire.
+ */
+ if (vboxNetFltSolarisIsOurMBlk(pThis, pPromiscStream, pMsg))
+ {
+ Log((DEVICE_NAME ":Avoiding packet loopback.\n"));
+ return VINF_SUCCESS;
+ }
+
+ /*
+ * Figure out the source of the packet based on the source Mac address.
+ */
+ uint32_t fSrc = INTNETTRUNKDIR_WIRE;
+ PRTNETETHERHDR pEthHdr = (PRTNETETHERHDR)pMsg->b_rptr;
+ if (vboxNetFltPortSolarisIsHostMac(pThis, &pEthHdr->SrcMac))
+ fSrc = INTNETTRUNKDIR_HOST;
+
+ /*
+ * Afaik; we no longer need to worry about incorrect checksums because we now use
+ * a dedicated stream and don't intercept packets under IP/ARP which might be doing
+ * checksum offloading.
+ */
+#if 0
+ if (fSrc & INTNETTRUNKDIR_HOST)
+ {
+ mblk_t *pCorrectedMsg = vboxNetFltSolarisFixChecksums(pMsg);
+ if (pCorrectedMsg)
+ pMsg = pCorrectedMsg;
+ }
+ vboxNetFltSolarisAnalyzeMBlk(pMsg);
+#endif
+
+ /*
+ * Solaris raw mode streams for priority-tagged VLAN does not strip the VLAN tag.
+ * It zero's the VLAN-Id but keeps the tag intact as part of the Ethernet header.
+ * We need to manually strip these tags out or the guests might get confused.
+ */
+ bool fCopied = false;
+ bool fTagged = false;
+ if ( pThis->u.s.fVLAN
+ && pPromiscStream->fRawMode)
+ {
+ if (pEthHdr->EtherType == RT_H2BE_U16(RTNET_ETHERTYPE_VLAN))
+ {
+ if (msgdsize(pMsg) > sizeof(RTNETETHERHDR) + sizeof(VLANHEADER))
+ {
+ if (pMsg->b_cont)
+ {
+ mblk_t *pFullMsg = msgpullup(pMsg, -1 /* all data blocks */);
+ if (pFullMsg)
+ {
+ /* Original pMsg will be freed by the caller */
+ pMsg = pFullMsg;
+ fCopied = true;
+ }
+ else
+ {
+ LogRel((DEVICE_NAME ":vboxNetFltSolarisRecv msgpullup failed.\n"));
+ return VERR_NO_MEMORY;
+ }
+ }
+
+ PVLANHEADER pVlanHdr = (PVLANHEADER)(pMsg->b_rptr + sizeof(RTNETETHERHDR) - sizeof(pEthHdr->EtherType));
+ Log((DEVICE_NAME ":Recv VLAN Pcp=%u Cfi=%u Id=%u\n", VLAN_PRI(RT_BE2H_U16(pVlanHdr->Data)),
+ VLAN_CFI(RT_BE2H_U16(pVlanHdr->Data)), VLAN_ID(RT_BE2H_U16(pVlanHdr->Data))));
+ if ( VLAN_PRI(RT_BE2H_U16(pVlanHdr->Data)) > 0
+ && VLAN_ID(RT_BE2H_U16(pVlanHdr->Data)) == 0)
+ {
+ /*
+ * Create new Ethernet header with stripped VLAN tag.
+ */
+ size_t cbEthPrefix = sizeof(RTNETETHERHDR) - sizeof(pEthHdr->EtherType);
+ mblk_t *pStrippedMsg = allocb(cbEthPrefix, BPRI_MED);
+ if (RT_LIKELY(pStrippedMsg))
+ {
+ fTagged = true;
+
+ /*
+ * Copy ethernet header excluding the ethertype.
+ */
+ bcopy(pMsg->b_rptr, pStrippedMsg->b_wptr, cbEthPrefix);
+ pStrippedMsg->b_wptr += cbEthPrefix;
+
+ /*
+ * Link the rest of the message (ethertype + data, skipping VLAN header).
+ */
+ pMsg->b_rptr += cbEthPrefix + sizeof(VLANHEADER);
+ pStrippedMsg->b_cont = pMsg;
+ pMsg = pStrippedMsg;
+ Log((DEVICE_NAME ":Stripped VLAN tag.\n"));
+ }
+ else
+ {
+ LogRel((DEVICE_NAME ":vboxNetFltSolarisRecv insufficient memory for creating VLAN stripped packet"
+ " cbMsg=%u.\n", cbEthPrefix));
+ if (fCopied)
+ freemsg(pMsg);
+ return VERR_NO_MEMORY;
+ }
+ }
+ }
+ }
+ }
+
+ /*
+ * Route all received packets into the internal network.
+ */
+ unsigned cSegs = vboxNetFltSolarisMBlkCalcSGSegs(pThis, pMsg);
+ PINTNETSG pSG = (PINTNETSG)alloca(RT_UOFFSETOF_DYN(INTNETSG, aSegs[cSegs]));
+ int rc = vboxNetFltSolarisMBlkToSG(pThis, pMsg, pSG, cSegs, fSrc);
+ if (RT_SUCCESS(rc))
+ pThis->pSwitchPort->pfnRecv(pThis->pSwitchPort, NULL /* pvIf */, pSG, fSrc);
+ else
+ LogRel((DEVICE_NAME ":vboxNetFltSolarisMBlkToSG failed. rc=%d\n", rc));
+
+ /*
+ * If we've allocated the prefix before the VLAN tag in a new message, free that.
+ */
+ if (fTagged)
+ {
+ mblk_t *pTagMsg = pMsg->b_cont;
+ pMsg->b_cont = NULL; /* b_cont could be the message from the caller or a copy we made (fCopied) */
+ freemsg(pMsg);
+ pMsg = pTagMsg;
+ }
+
+ /*
+ * If we made an extra copy for VLAN stripping, we need to free that ourselves.
+ */
+ if (fCopied)
+ freemsg(pMsg);
+
+ return VINF_SUCCESS;
+}
+
+#if 0
+/**
+ * Finalize the message to be fed into the internal network.
+ * Verifies and tries to fix checksums for TCP, UDP and IP.
+ *
+ * @returns Corrected message or NULL if no change was required.
+ * @param pMsg Pointer to the message block.
+ * This must not be DLPI linked messages, must be M_DATA.
+ *
+ * @remarks If this function returns a checksum adjusted message, the
+ * passed in input message has been freed and should not be
+ * referenced anymore by the caller.
+ */
+static mblk_t *vboxNetFltSolarisFixChecksums(mblk_t *pMsg)
+{
+ LogFunc((DEVICE_NAME ":vboxNetFltSolarisFixChecksums pMsg=%p\n"));
+
+ Assert(DB_TYPE(pMsg) == M_DATA);
+
+ if (MBLKL(pMsg) < sizeof(RTNETETHERHDR))
+ {
+ LogRel((DEVICE_NAME ":vboxNetFltSolarisFixChecksums Packet shorter than ethernet header size!\n"));
+ return NULL;
+ }
+
+ PRTNETETHERHDR pEthHdr = (PRTNETETHERHDR)pMsg->b_rptr;
+ if (pEthHdr->EtherType == RT_H2BE_U16(RTNET_ETHERTYPE_IPV4))
+ {
+ /*
+ * Check if we have a complete packet or being fed a chain.
+ */
+ size_t cbIpPacket = 0;
+ mblk_t *pFullMsg = NULL;
+ if (pMsg->b_cont)
+ {
+ Log((DEVICE_NAME ":Chained mblk_t.\n"));
+
+ /*
+ * Handle chain by making a packet copy to verify if the IP checksum is correct.
+ * Contributions to calculating IP checksums from a chained message block with
+ * odd/non-pulled up sizes are welcome.
+ */
+ size_t cbFullMsg = msgdsize(pMsg);
+ mblk_t *pFullMsg = allocb(cbFullMsg, BPRI_MED);
+ Log((DEVICE_NAME ":msgdsize returns %d\n", cbFullMsg));
+ if (RT_UNLIKELY(!pFullMsg))
+ {
+ LogRel((DEVICE_NAME ":vboxNetFltSolarisFixChecksums failed to alloc new message of %d bytes.\n", cbFullMsg));
+ return NULL;
+ }
+
+ for (mblk_t *pTmp = pMsg; pTmp; pTmp = pTmp->b_cont)
+ {
+ if (DB_TYPE(pTmp) == M_DATA)
+ {
+ bcopy(pTmp->b_rptr, pFullMsg->b_wptr, MBLKL(pTmp));
+ pFullMsg->b_wptr += MBLKL(pTmp);
+ }
+ }
+
+ DB_TYPE(pFullMsg) = M_DATA;
+ pEthHdr = (PRTNETETHERHDR)pFullMsg->b_rptr;
+ cbIpPacket = MBLKL(pFullMsg) - sizeof(RTNETETHERHDR);
+ }
+ else
+ cbIpPacket = MBLKL(pMsg) - sizeof(RTNETETHERHDR);
+
+ /*
+ * Check if the IP checksum is valid.
+ */
+ uint8_t *pbProtocol = (uint8_t *)(pEthHdr + 1);
+ PRTNETIPV4 pIpHdr = (PRTNETIPV4)pbProtocol;
+ size_t cbPayload = cbIpPacket - (pIpHdr->ip_hl << 2);
+ bool fChecksumAdjusted = false;
+ if (RTNetIPv4IsHdrValid(pIpHdr, cbPayload, cbPayload))
+ {
+ pbProtocol += (pIpHdr->ip_hl << 2);
+
+ /*
+ * Fix up TCP/UDP and IP checksums if they're incomplete/invalid.
+ */
+ if (pIpHdr->ip_p == RTNETIPV4_PROT_TCP)
+ {
+ PRTNETTCP pTcpHdr = (PRTNETTCP)pbProtocol;
+ uint16_t TcpChecksum = RTNetIPv4TCPChecksum(pIpHdr, pTcpHdr, NULL);
+ if (pTcpHdr->th_sum != TcpChecksum)
+ {
+ pTcpHdr->th_sum = TcpChecksum;
+ fChecksumAdjusted = true;
+ Log((DEVICE_NAME ":fixed TCP checksum.\n"));
+ }
+ }
+ else if (pIpHdr->ip_p == RTNETIPV4_PROT_UDP)
+ {
+ PRTNETUDP pUdpHdr = (PRTNETUDP)pbProtocol;
+ uint16_t UdpChecksum = RTNetIPv4UDPChecksum(pIpHdr, pUdpHdr, pUdpHdr + 1);
+
+ if (pUdpHdr->uh_sum != UdpChecksum)
+ {
+ pUdpHdr->uh_sum = UdpChecksum;
+ fChecksumAdjusted = true;
+ Log((DEVICE_NAME ":Fixed UDP checksum."));
+ }
+ }
+ }
+
+ if (fChecksumAdjusted)
+ {
+ /*
+ * If we made a copy and the checksum is corrected on the copy,
+ * free the original, return the checksum fixed copy.
+ */
+ if (pFullMsg)
+ {
+ freemsg(pMsg);
+ return pFullMsg;
+ }
+
+ return pMsg;
+ }
+
+ /*
+ * If we made a copy and the checksum is NOT corrected, free the copy,
+ * and return NULL.
+ */
+ if (pFullMsg)
+ freemsg(pFullMsg);
+
+ return NULL;
+ }
+
+ return NULL;
+}
+
+
+/**
+ * Simple packet dump, used for internal debugging.
+ *
+ * @param pMsg Pointer to the message to analyze and dump.
+ */
+static void vboxNetFltSolarisAnalyzeMBlk(mblk_t *pMsg)
+{
+ LogFunc((DEVICE_NAME ":vboxNetFltSolarisAnalyzeMBlk pMsg=%p\n", pMsg));
+
+ PCRTNETETHERHDR pEthHdr = (PCRTNETETHERHDR)pMsg->b_rptr;
+ uint8_t *pb = pMsg->b_rptr;
+ if (pEthHdr->EtherType == RT_H2BE_U16(RTNET_ETHERTYPE_IPV4))
+ {
+ PRTNETIPV4 pIpHdr = (PRTNETIPV4)(pEthHdr + 1);
+ size_t cbLen = MBLKL(pMsg) - sizeof(*pEthHdr);
+ if (!pMsg->b_cont)
+ {
+ if (pIpHdr->ip_p == RTNETIPV4_PROT_ICMP)
+ LogRel((DEVICE_NAME ":ICMP D=%.6Rhxs S=%.6Rhxs T=%04x\n", pb, pb + 6, RT_BE2H_U16(*(uint16_t *)(pb + 12))));
+ else if (pIpHdr->ip_p == RTNETIPV4_PROT_TCP)
+ LogRel((DEVICE_NAME ":TCP D=%.6Rhxs S=%.6Rhxs\n", pb, pb + 6));
+ else if (pIpHdr->ip_p == RTNETIPV4_PROT_UDP)
+ {
+ PCRTNETUDP pUdpHdr = (PCRTNETUDP)((uint32_t *)pIpHdr + pIpHdr->ip_hl);
+ if ( RT_BE2H_U16(pUdpHdr->uh_sport) == 67
+ && RT_BE2H_U16(pUdpHdr->uh_dport) == 68)
+ {
+ LogRel((DEVICE_NAME ":UDP bootp ack D=%.6Rhxs S=%.6Rhxs UDP_CheckSum=%04x Computex=%04x\n", pb, pb + 6,
+ RT_BE2H_U16(pUdpHdr->uh_sum), RT_BE2H_U16(RTNetIPv4UDPChecksum(pIpHdr, pUdpHdr, pUdpHdr + 1))));
+ }
+ }
+ }
+ else
+ {
+ Log((DEVICE_NAME ":Chained IP packet. Skipping validity check.\n"));
+ }
+ }
+ else if (pEthHdr->EtherType == RT_H2BE_U16(RTNET_ETHERTYPE_VLAN))
+ {
+ PVLANHEADER pVlanHdr = (PVLANHEADER)(pMsg->b_rptr + sizeof(RTNETETHERHDR) - sizeof(pEthHdr->EtherType));
+ LogRel((DEVICE_NAME ":VLAN Pcp=%u Cfi=%u Id=%u\n", VLAN_PRI(RT_BE2H_U16(pVlanHdr->Data)),
+ VLAN_CFI(RT_BE2H_U16(pVlanHdr->Data)), VLAN_ID(RT_BE2H_U16(pVlanHdr->Data))));
+ LogRel((DEVICE_NAME "%.*Rhxd\n", sizeof(VLANHEADER), pVlanHdr));
+ }
+ else if (pEthHdr->EtherType == RT_H2BE_U16(RTNET_ETHERTYPE_ARP))
+ {
+ PRTNETARPHDR pArpHdr = (PRTNETARPHDR)(pEthHdr + 1);
+ LogRel((DEVICE_NAME ":ARP Op=%d\n", pArpHdr->ar_oper));
+ }
+ else if (pEthHdr->EtherType == RT_H2BE_U16(RTNET_ETHERTYPE_IPV6))
+ {
+ LogRel((DEVICE_NAME ":IPv6 D=%.6Rhxs S=%.6Rhxs\n", pb, pb + 6));
+ }
+ else if (pEthHdr->EtherType == RT_H2BE_U16(RTNET_ETHERTYPE_IPX_1)
+ || pEthHdr->EtherType == RT_H2BE_U16(RTNET_ETHERTYPE_IPX_2)
+ || pEthHdr->EtherType == RT_H2BE_U16(RTNET_ETHERTYPE_IPX_3))
+ {
+ LogRel((DEVICE_NAME ":IPX packet.\n"));
+ }
+ else
+ {
+ LogRel((DEVICE_NAME ":Unknown EtherType=%x D=%.6Rhxs S=%.6Rhxs\n", RT_H2BE_U16(pEthHdr->EtherType), &pEthHdr->DstMac,
+ &pEthHdr->SrcMac));
+ /* Log((DEVICE_NAME ":%.*Rhxd\n", MBLKL(pMsg), pMsg->b_rptr)); */
+ }
+}
+#endif
+
+
+/* -=-=-=-=-=- Common Hooks -=-=-=-=-=- */
+
+
+
+void vboxNetFltPortOsSetActive(PVBOXNETFLTINS pThis, bool fActive)
+{
+ LogFunc((DEVICE_NAME ":vboxNetFltPortOsSetActive pThis=%p fActive=%d\n", pThis, fActive));
+
+ /*
+ * Enable/disable promiscuous mode.
+ */
+ vboxnetflt_promisc_params_t *pData = RTMemAllocZ(sizeof(vboxnetflt_promisc_params_t));
+ if (RT_LIKELY(pData))
+ {
+ /*
+ * See @bugref{5262} as to why we need to do all this qtimeout/qwriter tricks.
+ */
+ vboxnetflt_promisc_stream_t *pPromiscStream = ASMAtomicUoReadPtrT(&pThis->u.s.pPromiscStream,
+ vboxnetflt_promisc_stream_t *);
+ if ( pPromiscStream
+ && pPromiscStream->Stream.pReadQueue)
+ {
+ pData->pThis = pThis;
+ pData->fPromiscOn = fActive;
+ if (ASMAtomicReadPtr(&pPromiscStream->TimeoutId))
+ quntimeout(WR(pPromiscStream->Stream.pReadQueue), pPromiscStream->TimeoutId);
+ timeout_id_t TimeoutId = qtimeout(WR(pPromiscStream->Stream.pReadQueue), vboxNetFltSolarisPromiscReqWrap,
+ pData, 1 /* ticks */);
+ ASMAtomicWritePtr(&pPromiscStream->TimeoutId, TimeoutId);
+ return; /* pData will be freed by vboxNetFltSolarisPromiscReqWrap() */
+ }
+ else
+ LogRel((DEVICE_NAME ":vboxNetFltPortOsSetActive pThis=%p fActive=%d missing stream!\n", pThis, fActive));
+ RTMemFree(pData);
+ }
+ else
+ LogRel((DEVICE_NAME ":vboxNetFltPortOsSetActive out of memory!\n"));
+}
+
+
+int vboxNetFltOsDisconnectIt(PVBOXNETFLTINS pThis)
+{
+ LogFunc((DEVICE_NAME ":vboxNetFltOsDisconnectIt pThis=%p\n", pThis));
+
+ vboxNetFltSolarisDetachFromInterface(pThis);
+
+ return VINF_SUCCESS;
+}
+
+
+int vboxNetFltOsConnectIt(PVBOXNETFLTINS pThis)
+{
+ /* Nothing to do here. */
+ return VINF_SUCCESS;
+}
+
+
+void vboxNetFltOsDeleteInstance(PVBOXNETFLTINS pThis)
+{
+ LogFunc((DEVICE_NAME ":vboxNetFltOsDeleteInstance pThis=%p\n", pThis));
+
+ mutex_destroy(&pThis->u.s.hMtx);
+
+#ifdef VBOXNETFLT_SOLARIS_IPV6_POLLING
+ if (pThis->u.s.hPollMtx != NIL_RTSEMFASTMUTEX)
+ {
+ RTSemFastMutexDestroy(pThis->u.s.hPollMtx);
+ pThis->u.s.hPollMtx = NIL_RTSEMFASTMUTEX;
+ }
+#endif
+
+}
+
+
+int vboxNetFltOsInitInstance(PVBOXNETFLTINS pThis, void *pvContext)
+{
+ LogFunc((DEVICE_NAME ":vboxNetFltOsInitInstance pThis=%p\n"));
+
+ /*
+ * Mutex used for loopback lockouts.
+ */
+ int rc = VINF_SUCCESS;
+ mutex_init(&pThis->u.s.hMtx, NULL /* name */, MUTEX_DRIVER, NULL /* cookie */);
+#ifdef VBOXNETFLT_SOLARIS_IPV6_POLLING
+ rc = RTSemFastMutexCreate(&pThis->u.s.hPollMtx);
+ if (RT_SUCCESS(rc))
+ {
+#endif
+ rc = vboxNetFltSolarisAttachToInterface(pThis);
+ if (RT_SUCCESS(rc))
+ return rc;
+
+ LogRel((DEVICE_NAME ":vboxNetFltSolarisAttachToInterface failed. rc=%Rrc\n", rc));
+
+#ifdef VBOXNETFLT_SOLARIS_IPV6_POLLING
+ RTSemFastMutexDestroy(pThis->u.s.hPollMtx);
+ pThis->u.s.hPollMtx = NIL_RTSEMFASTMUTEX;
+ }
+ else
+ LogRel((DEVICE_NAME ":vboxNetFltOsInitInstance failed to create poll mutex. rc=%Rrc\n", rc));
+#endif
+
+ mutex_destroy(&pThis->u.s.hMtx);
+
+ NOREF(pvContext);
+ return rc;
+}
+
+
+int vboxNetFltOsPreInitInstance(PVBOXNETFLTINS pThis)
+{
+ /*
+ * Init. the solaris specific data.
+ */
+ pThis->u.s.hIface = NULL;
+ pThis->u.s.pIp4Stream = NULL;
+ pThis->u.s.pIp6Stream = NULL;
+ pThis->u.s.pArpStream = NULL;
+ pThis->u.s.pPromiscStream = NULL;
+ pThis->u.s.fAttaching = false;
+ pThis->u.s.fVLAN = false;
+#ifdef VBOXNETFLT_SOLARIS_IPV6_POLLING
+ pThis->u.s.hPollMtx = NIL_RTSEMFASTMUTEX;
+#endif
+ bzero(&pThis->u.s.MacAddr, sizeof(pThis->u.s.MacAddr));
+ return VINF_SUCCESS;
+}
+
+
+bool vboxNetFltOsMaybeRediscovered(PVBOXNETFLTINS pThis)
+{
+ /*
+ * We don't support interface rediscovery on Solaris hosts because the
+ * filter is very tightly bound to the stream.
+ */
+ return false;
+}
+
+
+void vboxNetFltPortOsNotifyMacAddress(PVBOXNETFLTINS pThis, void *pvIfData, PCRTMAC pMac)
+{
+ NOREF(pThis); NOREF(pvIfData); NOREF(pMac);
+}
+
+
+int vboxNetFltPortOsConnectInterface(PVBOXNETFLTINS pThis, void *pvIf, void **ppvIfData)
+{
+ /* Nothing to do */
+ NOREF(pThis); NOREF(pvIf); NOREF(ppvIfData);
+ return VINF_SUCCESS;
+}
+
+
+int vboxNetFltPortOsDisconnectInterface(PVBOXNETFLTINS pThis, void *pvIfData)
+{
+ /* Nothing to do */
+ NOREF(pThis); NOREF(pvIfData);
+ return VINF_SUCCESS;
+}
+
+
+int vboxNetFltPortOsXmit(PVBOXNETFLTINS pThis, void *pvIfData, PINTNETSG pSG, uint32_t fDst)
+{
+ NOREF(pvIfData);
+ LogFunc((DEVICE_NAME ":vboxNetFltPortOsXmit pThis=%p pSG=%p fDst=%d\n", pThis, pSG, fDst));
+
+ int rc = VINF_SUCCESS;
+ if (fDst & INTNETTRUNKDIR_WIRE)
+ {
+ vboxnetflt_promisc_stream_t *pPromiscStream = ASMAtomicUoReadPtrT(&pThis->u.s.pPromiscStream,
+ vboxnetflt_promisc_stream_t *);
+ if (RT_LIKELY(pPromiscStream))
+ {
+ mblk_t *pMsg = vboxNetFltSolarisMBlkFromSG(pThis, pSG, fDst);
+ if (RT_LIKELY(pMsg))
+ {
+ Log((DEVICE_NAME ":vboxNetFltPortOsXmit INTNETTRUNKDIR_WIRE\n"));
+
+ vboxNetFltSolarisQueueLoopback(pThis, pPromiscStream, pMsg);
+ putnext(WR(pPromiscStream->Stream.pReadQueue), pMsg);
+ }
+ else
+ {
+ LogRel((DEVICE_NAME ":vboxNetFltPortOsXmit vboxNetFltSolarisMBlkFromSG failed.\n"));
+ return VERR_NO_MEMORY;
+ }
+ }
+ }
+
+ if (fDst & INTNETTRUNKDIR_HOST)
+ {
+ /*
+ * For unplumbed interfaces we would not be bound to IP or ARP.
+ * We either bind to both or neither; so atomic reading one should be sufficient.
+ */
+ vboxnetflt_stream_t *pIp4Stream = ASMAtomicUoReadPtrT(&pThis->u.s.pIp4Stream, vboxnetflt_stream_t *);
+ if (!pIp4Stream)
+ return rc;
+
+ /*
+ * Create a message block and send it up the host stack (upstream).
+ */
+ mblk_t *pMsg = vboxNetFltSolarisMBlkFromSG(pThis, pSG, fDst);
+ if (RT_LIKELY(pMsg))
+ {
+ PCRTNETETHERHDR pEthHdr = (PCRTNETETHERHDR)pMsg->b_rptr;
+
+ /*
+ * Send message up ARP stream.
+ */
+ if (pEthHdr->EtherType == RT_H2BE_U16(RTNET_ETHERTYPE_ARP))
+ {
+ Log((DEVICE_NAME ":vboxNetFltPortOsXmit INTNETTRUNKDIR_HOST ARP\n"));
+
+ vboxnetflt_stream_t *pArpStream = ASMAtomicUoReadPtrT(&pThis->u.s.pArpStream, vboxnetflt_stream_t *);
+ if (pArpStream)
+ {
+ /*
+ * Construct a DL_UNITDATA_IND style message for ARP as it doesn't understand fast path.
+ */
+ mblk_t *pDlpiMsg;
+ rc = vboxNetFltSolarisRawToUnitData(pMsg, &pDlpiMsg);
+ if (RT_SUCCESS(rc))
+ {
+ pMsg = pDlpiMsg;
+
+ queue_t *pArpReadQueue = pArpStream->pReadQueue;
+ putnext(pArpReadQueue, pMsg);
+ }
+ else
+ {
+ LogRel((DEVICE_NAME ":vboxNetFltSolarisRawToUnitData failed!\n"));
+ freemsg(pMsg);
+ rc = VERR_NO_MEMORY;
+ }
+ }
+ else
+ freemsg(pMsg); /* Should really never happen... */
+ }
+ else
+ {
+ vboxnetflt_stream_t *pIp6Stream = ASMAtomicUoReadPtrT(&pThis->u.s.pIp6Stream, vboxnetflt_stream_t *);
+ if ( pEthHdr->EtherType == RT_H2BE_U16(RTNET_ETHERTYPE_IPV6)
+ && pIp6Stream)
+ {
+ /*
+ * Send messages up IPv6 stream.
+ */
+ Log((DEVICE_NAME ":vboxNetFltPortOsXmit INTNETTRUNKDIR_HOST IPv6\n"));
+
+ pMsg->b_rptr += sizeof(RTNETETHERHDR);
+ queue_t *pIp6ReadQueue = pIp6Stream->pReadQueue;
+ putnext(pIp6ReadQueue, pMsg);
+ }
+ else
+ {
+ /*
+ * Send messages up IPv4 stream.
+ */
+ Log((DEVICE_NAME ":vboxNetFltPortOsXmit INTNETTRUNKDIR_HOST IPv4\n"));
+
+ pMsg->b_rptr += sizeof(RTNETETHERHDR);
+ queue_t *pIp4ReadQueue = pIp4Stream->pReadQueue;
+ putnext(pIp4ReadQueue, pMsg);
+ }
+ }
+ }
+ else
+ {
+ LogRel((DEVICE_NAME ":vboxNetFltSolarisMBlkFromSG failed.\n"));
+ rc = VERR_NO_MEMORY;
+ }
+ }
+
+ return rc;
+}
+
diff --git a/src/VBox/HostDrivers/VBoxNetFlt/solaris/VBoxNetFltBow-solaris.c b/src/VBox/HostDrivers/VBoxNetFlt/solaris/VBoxNetFltBow-solaris.c
new file mode 100644
index 00000000..4a717253
--- /dev/null
+++ b/src/VBox/HostDrivers/VBoxNetFlt/solaris/VBoxNetFltBow-solaris.c
@@ -0,0 +1,1702 @@
+/* $Id: VBoxNetFltBow-solaris.c $ */
+/** @file
+ * VBoxNetFlt - Network Filter Driver (Host), Solaris Specific Code.
+ */
+
+/*
+ * Copyright (C) 2008-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#define LOG_GROUP LOG_GROUP_NET_FLT_DRV
+#include <VBox/log.h>
+#include <VBox/err.h>
+#include <VBox/intnetinline.h>
+#include <VBox/version.h>
+#include <iprt/initterm.h>
+#include <iprt/alloca.h>
+#include <iprt/assert.h>
+#include <iprt/err.h>
+#include <iprt/string.h>
+#include <iprt/rand.h>
+#include <iprt/net.h>
+#include <iprt/spinlock.h>
+#include <iprt/mem.h>
+
+#include <sys/types.h>
+#include <sys/modctl.h>
+#include <sys/conf.h>
+#include <sys/stat.h>
+#include <sys/ddi.h>
+#include <sys/gld.h>
+#include <sys/sunddi.h>
+#include <sys/strsubr.h>
+#include <sys/dlpi.h>
+#include <sys/dls_mgmt.h>
+#include <sys/mac.h>
+#include <sys/strsun.h>
+
+#include <sys/vnic_mgmt.h>
+#include <sys/mac_client.h>
+#include <sys/mac_provider.h>
+#include <sys/dls.h>
+#include <sys/dld.h>
+#include <sys/cred.h>
+
+
+#define VBOXNETFLT_OS_SPECFIC 1
+#include "../VBoxNetFltInternal.h"
+
+
+/*********************************************************************************************************************************
+* Defined Constants And Macros *
+*********************************************************************************************************************************/
+/** The module name. */
+#define DEVICE_NAME "vboxbow"
+/** The module descriptions as seen in 'modinfo'. */
+#define DEVICE_DESC_DRV "VirtualBox NetBow"
+/** The dynamically created VNIC name (hardcoded in NetIf-solaris.cpp).
+ * @todo move this define into a common header. */
+#define VBOXBOW_VNIC_NAME "vboxvnic"
+/** The VirtualBox VNIC template name (hardcoded in NetIf-solaris.cpp).
+ * @todo move this define into a common header. */
+#define VBOXBOW_VNIC_TEMPLATE_NAME "vboxvnic_template"
+/** Debugging switch for using symbols in kmdb */
+# define LOCAL static
+/** VBOXNETFLTVNIC::u32Magic */
+# define VBOXNETFLTVNIC_MAGIC 0x0ddfaced
+
+/** VLAN tag masking, should probably be in IPRT? */
+#define VLAN_ID(vlan) (((vlan) >> 0) & 0x0fffu)
+#define VLAN_CFI(vlan) (((vlan) >> 12) & 0x0001u)
+#define VLAN_PRI(vlan) (((vlan) >> 13) & 0x0007u)
+#define VLAN_TAG(pri,cfi,vid) (((pri) << 13) | ((cfi) << 12) | ((vid) << 0))
+
+typedef struct VLANHEADER
+{
+ uint16_t Type;
+ uint16_t Data;
+} VLANHEADER;
+typedef struct VLANHEADER *PVLANHEADER;
+
+/* Private: from sys/vlan.h */
+#ifndef VLAN_ID_NONE
+# define VLAN_ID_NONE 0
+#endif
+
+/* Private: from sys/param.h */
+#ifndef MAXLINKNAMESPECIFIER
+# define MAXLINKNAMESPECIFIER 96 /* MAXLINKNAMELEN + ZONENAME_MAX */
+#endif
+
+/* Private: from sys/mac_client_priv.h, mac client function prototypes. */
+extern uint16_t mac_client_vid(mac_client_handle_t hClient);
+extern void mac_client_get_resources(mac_client_handle_t hClient, mac_resource_props_t *pResources);
+extern int mac_client_set_resources(mac_client_handle_t hClient, mac_resource_props_t *pResources);
+
+
+/*********************************************************************************************************************************
+* Kernel Entry Hooks *
+*********************************************************************************************************************************/
+LOCAL int VBoxNetFltSolarisAttach(dev_info_t *pDip, ddi_attach_cmd_t enmCmd);
+LOCAL int VBoxNetFltSolarisDetach(dev_info_t *pDip, ddi_detach_cmd_t enmCmd);
+LOCAL int VBoxNetFltSolarisGetInfo(dev_info_t *pDip, ddi_info_cmd_t enmCmd, void *pArg, void **ppvResult);
+
+
+/*********************************************************************************************************************************
+* Structures and Typedefs *
+*********************************************************************************************************************************/
+/**
+ * cb_ops: for drivers that support char/block entry points
+ */
+static struct cb_ops g_VBoxNetFltSolarisCbOps =
+{
+ nulldev, /* c open */
+ nulldev, /* c close */
+ nodev, /* b strategy */
+ nodev, /* b dump */
+ nodev, /* b print */
+ nodev, /* c read */
+ nodev, /* c write*/
+ nodev, /* c ioctl*/
+ nodev, /* c devmap */
+ nodev, /* c mmap */
+ nodev, /* c segmap */
+ nochpoll, /* c poll */
+ ddi_prop_op, /* property ops */
+ NULL, /* streamtab */
+ D_NEW | D_MP, /* compat. flag */
+ CB_REV, /* revision */
+ nodev, /* c aread */
+ nodev /* c awrite */
+};
+
+/**
+ * dev_ops: for driver device operations
+ */
+static struct dev_ops g_VBoxNetFltSolarisDevOps =
+{
+ DEVO_REV, /* driver build revision */
+ 0, /* ref count */
+ VBoxNetFltSolarisGetInfo,
+ nulldev, /* identify */
+ nulldev, /* probe */
+ VBoxNetFltSolarisAttach,
+ VBoxNetFltSolarisDetach,
+ nodev, /* reset */
+ &g_VBoxNetFltSolarisCbOps,
+ NULL, /* bus ops */
+ nodev, /* power */
+ ddi_quiesce_not_needed
+};
+
+/**
+ * modldrv: export driver specifics to the kernel
+ */
+static struct modldrv g_VBoxNetFltSolarisModule =
+{
+ &mod_driverops, /* extern from kernel */
+ DEVICE_DESC_DRV " " VBOX_VERSION_STRING "r" RT_XSTR(VBOX_SVN_REV),
+ &g_VBoxNetFltSolarisDevOps
+};
+
+/**
+ * modlinkage: export install/remove/info to the kernel
+ */
+static struct modlinkage g_VBoxNetFltSolarisModLinkage =
+{
+ MODREV_1,
+ {
+ &g_VBoxNetFltSolarisModule,
+ NULL,
+ }
+};
+
+/*
+ * VBOXNETFLTVNICTEMPLATE: VNIC template information.
+ */
+typedef struct VBOXNETFLTVNICTEMPLATE
+{
+ /** The name of link on which the VNIC template is created on. */
+ char szLinkName[MAXNAMELEN];
+ /** The VLAN Id (can be VLAN_ID_NONE). */
+ uint16_t uVLANId;
+ /** Resources (bandwidth, CPU bindings, flow priority etc.) */
+ mac_resource_props_t Resources;
+} VBOXNETFLTVNICTEMPLATE;
+typedef struct VBOXNETFLTVNICTEMPLATE *PVBOXNETFLTVNICTEMPLATE;
+
+/**
+ * VBOXNETFLTVNIC: Per-VNIC instance data.
+ */
+typedef struct VBOXNETFLTVNIC
+{
+ /** Magic number (VBOXNETFLTVNIC_MAGIC). */
+ uint32_t u32Magic;
+ /** Whether we created the VNIC or not. */
+ bool fCreated;
+ /** Pointer to the VNIC template if any. */
+ PVBOXNETFLTVNICTEMPLATE pVNICTemplate;
+ /** Pointer to the VirtualBox interface instance. */
+ void *pvIf;
+ /** The MAC handle. */
+ mac_handle_t hInterface;
+ /** The VNIC link ID. */
+ datalink_id_t hLinkId;
+ /** The MAC client handle */
+ mac_client_handle_t hClient;
+ /** The unicast address handle. */
+ mac_unicast_handle_t hUnicast;
+ /** The promiscuous handle. */
+ mac_promisc_handle_t hPromisc;
+ /* The VNIC name. */
+ char szName[MAXLINKNAMESPECIFIER];
+ /** Handle to the next VNIC in the list. */
+ list_node_t hNode;
+} VBOXNETFLTVNIC;
+typedef struct VBOXNETFLTVNIC *PVBOXNETFLTVNIC;
+
+
+/*********************************************************************************************************************************
+* Global Variables *
+*********************************************************************************************************************************/
+/** Global Device handle we only support one instance. */
+static dev_info_t *g_pVBoxNetFltSolarisDip = NULL;
+/** The (common) global data. */
+static VBOXNETFLTGLOBALS g_VBoxNetFltSolarisGlobals;
+/** Global next-free VNIC Id (never decrements). */
+static volatile uint64_t g_VBoxNetFltSolarisVNICId;
+
+
+/*********************************************************************************************************************************
+* Internal Functions *
+*********************************************************************************************************************************/
+LOCAL mblk_t *vboxNetFltSolarisMBlkFromSG(PVBOXNETFLTINS pThis, PINTNETSG pSG, uint32_t fDst);
+LOCAL unsigned vboxNetFltSolarisMBlkCalcSGSegs(PVBOXNETFLTINS pThis, mblk_t *pMsg);
+LOCAL int vboxNetFltSolarisMBlkToSG(PVBOXNETFLTINS pThis, mblk_t *pMsg, PINTNETSG pSG, unsigned cSegs, uint32_t fSrc);
+LOCAL void vboxNetFltSolarisRecv(void *pvData, mac_resource_handle_t hResource, mblk_t *pMsg, boolean_t fLoopback);
+//LOCAL void vboxNetFltSolarisAnalyzeMBlk(mblk_t *pMsg);
+LOCAL int vboxNetFltSolarisReportInfo(PVBOXNETFLTINS pThis, mac_handle_t hInterface, bool fIsVNIC);
+LOCAL int vboxNetFltSolarisInitVNIC(PVBOXNETFLTINS pThis, PVBOXNETFLTVNIC pVNIC);
+LOCAL int vboxNetFltSolarisInitVNICTemplate(PVBOXNETFLTINS pThis, PVBOXNETFLTVNICTEMPLATE pVNICTemplate);
+LOCAL PVBOXNETFLTVNIC vboxNetFltSolarisAllocVNIC(void);
+LOCAL void vboxNetFltSolarisFreeVNIC(PVBOXNETFLTVNIC pVNIC);
+LOCAL void vboxNetFltSolarisDestroyVNIC(PVBOXNETFLTVNIC pVNIC);
+LOCAL int vboxNetFltSolarisCreateVNIC(PVBOXNETFLTINS pThis, PVBOXNETFLTVNIC *ppVNIC);
+DECLINLINE(int) vboxNetFltSolarisGetLinkId(const char *pszMacName, datalink_id_t *pLinkId);
+
+/**
+ * Kernel entry points
+ */
+int _init(void)
+{
+ Log((DEVICE_NAME ":_init\n"));
+
+ /*
+ * Prevent module autounloading.
+ */
+ modctl_t *pModCtl = mod_getctl(&g_VBoxNetFltSolarisModLinkage);
+ if (pModCtl)
+ pModCtl->mod_loadflags |= MOD_NOAUTOUNLOAD;
+ else
+ cmn_err(CE_NOTE, ":failed to disable autounloading!\n");
+
+ /*
+ * Initialize IPRT.
+ */
+ int rc = RTR0Init(0);
+ if (RT_SUCCESS(rc))
+ {
+ /*
+ * Initialize the globals and connect to the support driver.
+ *
+ * This will call back vboxNetFltOsOpenSupDrv (and maybe vboxNetFltOsCloseSupDrv)
+ * for establishing the connect to the support driver.
+ */
+ memset(&g_VBoxNetFltSolarisGlobals, 0, sizeof(g_VBoxNetFltSolarisGlobals));
+ rc = vboxNetFltInitGlobalsAndIdc(&g_VBoxNetFltSolarisGlobals);
+ if (RT_SUCCESS(rc))
+ {
+ rc = mod_install(&g_VBoxNetFltSolarisModLinkage);
+ if (!rc)
+ return rc;
+
+ LogRel((DEVICE_NAME ":mod_install failed. rc=%d\n", rc));
+ vboxNetFltTryDeleteIdcAndGlobals(&g_VBoxNetFltSolarisGlobals);
+ }
+ else
+ LogRel((DEVICE_NAME ":failed to initialize globals.\n"));
+
+ RTR0Term();
+ }
+ else
+ cmn_err(CE_NOTE, "failed to initialize IPRT (rc=%d)\n", rc);
+
+ memset(&g_VBoxNetFltSolarisGlobals, 0, sizeof(g_VBoxNetFltSolarisGlobals));
+ return RTErrConvertToErrno(rc);
+}
+
+
+int _fini(void)
+{
+ int rc;
+ Log((DEVICE_NAME ":_fini\n"));
+
+ /*
+ * Undo the work done during start (in reverse order).
+ */
+ rc = vboxNetFltTryDeleteIdcAndGlobals(&g_VBoxNetFltSolarisGlobals);
+ if (RT_FAILURE(rc))
+ {
+ LogRel((DEVICE_NAME ":_fini - busy! rc=%d\n", rc));
+ return EBUSY;
+ }
+
+ rc = mod_remove(&g_VBoxNetFltSolarisModLinkage);
+ if (!rc)
+ RTR0Term();
+
+ return rc;
+}
+
+
+int _info(struct modinfo *pModInfo)
+{
+ /* _info() can be called before _init() so RTR0Init() might not be called at this point. */
+ int rc = mod_info(&g_VBoxNetFltSolarisModLinkage, pModInfo);
+ return rc;
+}
+
+
+/**
+ * Attach entry point, to attach a device to the system or resume it.
+ *
+ * @param pDip The module structure instance.
+ * @param enmCmd Operation type (attach/resume).
+ *
+ * @returns corresponding solaris error code.
+ */
+LOCAL int VBoxNetFltSolarisAttach(dev_info_t *pDip, ddi_attach_cmd_t enmCmd)
+{
+ Log((DEVICE_NAME ":VBoxNetFltSolarisAttach pDip=%p enmCmd=%d\n", pDip, enmCmd));
+
+ switch (enmCmd)
+ {
+ case DDI_ATTACH:
+ {
+ g_pVBoxNetFltSolarisDip = pDip;
+ return DDI_SUCCESS;
+ }
+
+ case DDI_RESUME:
+ {
+ /* Nothing to do here... */
+ return DDI_SUCCESS;
+ }
+
+ /* case DDI_PM_RESUME: */
+ 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 Operation type (detach/suspend).
+ *
+ * @returns corresponding solaris error code.
+ */
+LOCAL int VBoxNetFltSolarisDetach(dev_info_t *pDip, ddi_detach_cmd_t enmCmd)
+{
+ Log((DEVICE_NAME ":VBoxNetFltSolarisDetach pDip=%p enmCmd=%d\n", pDip, enmCmd));
+
+ switch (enmCmd)
+ {
+ case DDI_DETACH:
+ {
+ return DDI_SUCCESS;
+ }
+
+ case DDI_RESUME:
+ {
+ /* Nothing to do here... */
+ return DDI_SUCCESS;
+ }
+
+ /* case DDI_PM_SUSPEND: */
+ /* case DDI_HOT_PLUG_DETACH: */
+ default:
+ return DDI_FAILURE;
+ }
+}
+
+
+/**
+ * Info entry point, called by solaris kernel for obtaining driver info.
+ *
+ * @param pDip The module structure instance (do not use).
+ * @param enmCmd Information request type.
+ * @param pvArg Type specific argument.
+ * @param ppvResult Where to store the requested info.
+ *
+ * @returns corresponding solaris error code.
+ */
+LOCAL int VBoxNetFltSolarisGetInfo(dev_info_t *pDip, ddi_info_cmd_t enmCmd, void *pvArg, void **ppvResult)
+{
+ Log((DEVICE_NAME ":VBoxNetFltSolarisGetInfo pDip=%p enmCmd=%d pArg=%p instance=%d\n", pDip, enmCmd, getminor((dev_t)pvArg)));
+
+ switch (enmCmd)
+ {
+ case DDI_INFO_DEVT2DEVINFO:
+ {
+ *ppvResult = g_pVBoxNetFltSolarisDip;
+ return *ppvResult ? DDI_SUCCESS : DDI_FAILURE;
+ }
+
+ case DDI_INFO_DEVT2INSTANCE:
+ {
+ /* There can only be a single-instance of this driver and thus its instance number is 0. */
+ *ppvResult = (void *)0;
+ break;
+ }
+ }
+
+ return DDI_FAILURE;
+}
+
+
+/**
+ * Create a solaris message block from the SG list.
+ *
+ * @param pThis The instance.
+ * @param pSG Pointer to the scatter-gather list.
+ * @param fDst INTNETTRUNKDIR_XXX.
+ *
+ * @returns Solaris message block.
+ */
+DECLINLINE(mblk_t *) vboxNetFltSolarisMBlkFromSG(PVBOXNETFLTINS pThis, PINTNETSG pSG, uint32_t fDst)
+{
+ Log((DEVICE_NAME ":vboxNetFltSolarisMBlkFromSG pThis=%p pSG=%p\n", pThis, pSG));
+
+ mblk_t *pMsg = allocb(pSG->cbTotal, BPRI_HI);
+ if (RT_UNLIKELY(!pMsg))
+ {
+ LogRel((DEVICE_NAME ":vboxNetFltSolarisMBlkFromSG failed to alloc %d bytes for mblk_t.\n", pSG->cbTotal));
+ return NULL;
+ }
+
+ /*
+ * Single buffer copy. Maybe later explore the
+ * need/possibility for using a mblk_t chain rather.
+ */
+ for (unsigned i = 0; i < pSG->cSegsUsed; i++)
+ {
+ if (pSG->aSegs[i].pv)
+ {
+ bcopy(pSG->aSegs[i].pv, pMsg->b_wptr, pSG->aSegs[i].cb);
+ pMsg->b_wptr += pSG->aSegs[i].cb;
+ }
+ }
+ return pMsg;
+}
+
+
+/**
+ * Calculate the number of segments required for this message block.
+ *
+ * @param pThis The instance
+ * @param pMsg Pointer to the data message.
+ *
+ * @returns Number of segments.
+ */
+LOCAL unsigned vboxNetFltSolarisMBlkCalcSGSegs(PVBOXNETFLTINS pThis, mblk_t *pMsg)
+{
+ unsigned cSegs = 0;
+ for (mblk_t *pCur = pMsg; pCur; pCur = pCur->b_cont)
+ if (MBLKL(pCur))
+ cSegs++;
+
+#ifdef PADD_RUNT_FRAMES_FROM_HOST
+ if (msgdsize(pMsg) < 60)
+ cSegs++;
+#endif
+
+ NOREF(pThis);
+ return RT_MAX(cSegs, 1);
+}
+
+
+/**
+ * Initializes an SG list from the given message block.
+ *
+ * @param pThis The instance.
+ * @param pMsg Pointer to the data message.
+ The caller must ensure it's not a control message block.
+ * @param pSG Pointer to the SG.
+ * @param cSegs Number of segments in the SG.
+ * This should match the number in the message block exactly!
+ * @param fSrc The source of the message.
+ *
+ * @returns VBox status code.
+ */
+LOCAL int vboxNetFltSolarisMBlkToSG(PVBOXNETFLTINS pThis, mblk_t *pMsg, PINTNETSG pSG, unsigned cSegs, uint32_t fSrc)
+{
+ Log((DEVICE_NAME ":vboxNetFltSolarisMBlkToSG pThis=%p pMsg=%p pSG=%p cSegs=%d\n", pThis, pMsg, pSG, cSegs));
+
+ /*
+ * Convert the message block to segments. Works cbTotal and sets cSegsUsed.
+ */
+ IntNetSgInitTempSegs(pSG, 0 /*cbTotal*/, cSegs, 0 /*cSegsUsed*/);
+ mblk_t *pCur = pMsg;
+ unsigned iSeg = 0;
+ while (pCur)
+ {
+ size_t cbSeg = MBLKL(pCur);
+ if (cbSeg)
+ {
+ void *pvSeg = pCur->b_rptr;
+ pSG->aSegs[iSeg].pv = pvSeg;
+ pSG->aSegs[iSeg].cb = cbSeg;
+ pSG->aSegs[iSeg].Phys = NIL_RTHCPHYS;
+ pSG->cbTotal += cbSeg;
+ iSeg++;
+ }
+ pCur = pCur->b_cont;
+ }
+ pSG->cSegsUsed = iSeg;
+
+#ifdef PADD_RUNT_FRAMES_FROM_HOST
+ if (pSG->cbTotal < 60 && (fSrc & INTNETTRUNKDIR_HOST))
+ {
+ Log((DEVICE_NAME ":vboxNetFltSolarisMBlkToSG pulling up to length.\n"));
+
+ static uint8_t const s_abZero[128] = {0};
+ pSG->aSegs[iSeg].Phys = NIL_RTHCPHYS;
+ pSG->aSegs[iSeg].pv = (void *)&s_abZero[0];
+ pSG->aSegs[iSeg].cb = 60 - pSG->cbTotal;
+ pSG->cbTotal = 60;
+ pSG->cSegsUsed++;
+ Assert(iSeg + 1 < cSegs);
+ }
+#endif
+
+ Log((DEVICE_NAME ":vboxNetFltSolarisMBlkToSG iSeg=%d pSG->cbTotal=%d msgdsize=%d\n", iSeg, pSG->cbTotal, msgdsize(pMsg)));
+ return VINF_SUCCESS;
+}
+
+
+#if 0
+/**
+ * Simple packet dump, used for internal debugging.
+ *
+ * @param pMsg Pointer to the message to analyze and dump.
+ */
+LOCAL void vboxNetFltSolarisAnalyzeMBlk(mblk_t *pMsg)
+{
+ LogFunc((DEVICE_NAME ":vboxNetFltSolarisAnalyzeMBlk pMsg=%p\n", pMsg));
+
+ PCRTNETETHERHDR pEthHdr = (PCRTNETETHERHDR)pMsg->b_rptr;
+ uint8_t *pb = pMsg->b_rptr;
+ if (pEthHdr->EtherType == RT_H2BE_U16(RTNET_ETHERTYPE_IPV4))
+ {
+ PRTNETIPV4 pIpHdr = (PRTNETIPV4)(pEthHdr + 1);
+ if (!pMsg->b_cont)
+ {
+ if (pIpHdr->ip_p == RTNETIPV4_PROT_ICMP)
+ LogRel((DEVICE_NAME ":ICMP D=%.6Rhxs S=%.6Rhxs T=%04x\n", pb, pb + 6, RT_BE2H_U16(*(uint16_t *)(pb + 12))));
+ else if (pIpHdr->ip_p == RTNETIPV4_PROT_TCP)
+ LogRel((DEVICE_NAME ":TCP D=%.6Rhxs S=%.6Rhxs\n", pb, pb + 6));
+ else if (pIpHdr->ip_p == RTNETIPV4_PROT_UDP)
+ {
+ PCRTNETUDP pUdpHdr = (PCRTNETUDP)((uint32_t *)pIpHdr + pIpHdr->ip_hl);
+ if ( RT_BE2H_U16(pUdpHdr->uh_sport) == 67
+ && RT_BE2H_U16(pUdpHdr->uh_dport) == 68)
+ {
+ LogRel((DEVICE_NAME ":UDP bootp ack D=%.6Rhxs S=%.6Rhxs UDP_CheckSum=%04x Computex=%04x\n", pb, pb + 6,
+ RT_BE2H_U16(pUdpHdr->uh_sum), RT_BE2H_U16(RTNetIPv4UDPChecksum(pIpHdr, pUdpHdr, pUdpHdr + 1))));
+ }
+ }
+ }
+ else
+ {
+ Log((DEVICE_NAME ":Chained IP packet. Skipping validity check.\n"));
+ }
+ }
+ else if (pEthHdr->EtherType == RT_H2BE_U16(RTNET_ETHERTYPE_VLAN))
+ {
+ PVLANHEADER pVlanHdr = (PVLANHEADER)(pMsg->b_rptr + sizeof(RTNETETHERHDR) - sizeof(pEthHdr->EtherType));
+ LogRel((DEVICE_NAME ":VLAN Pcp=%u Cfi=%u Id=%u\n", VLAN_PRI(RT_BE2H_U16(pVlanHdr->Data)),
+ VLAN_CFI(RT_BE2H_U16(pVlanHdr->Data)), VLAN_ID(RT_BE2H_U16(pVlanHdr->Data))));
+ LogRel((DEVICE_NAME "%.*Rhxd\n", sizeof(VLANHEADER), pVlanHdr));
+ }
+ else if (pEthHdr->EtherType == RT_H2BE_U16(RTNET_ETHERTYPE_ARP))
+ {
+ PRTNETARPHDR pArpHdr = (PRTNETARPHDR)(pEthHdr + 1);
+ LogRel((DEVICE_NAME ":ARP Op=%d\n", pArpHdr->ar_oper));
+ }
+ else if (pEthHdr->EtherType == RT_H2BE_U16(RTNET_ETHERTYPE_IPV6))
+ {
+ LogRel((DEVICE_NAME ":IPv6 D=%.6Rhxs S=%.6Rhxs\n", pb, pb + 6));
+ }
+ else if ( pEthHdr->EtherType == RT_H2BE_U16(RTNET_ETHERTYPE_IPX_1)
+ || pEthHdr->EtherType == RT_H2BE_U16(RTNET_ETHERTYPE_IPX_2)
+ || pEthHdr->EtherType == RT_H2BE_U16(RTNET_ETHERTYPE_IPX_3))
+ {
+ LogRel((DEVICE_NAME ":IPX packet.\n"));
+ }
+ else
+ {
+ LogRel((DEVICE_NAME ":Unknown EtherType=%x D=%.6Rhxs S=%.6Rhxs\n", RT_H2BE_U16(pEthHdr->EtherType), &pEthHdr->DstMac,
+ &pEthHdr->SrcMac));
+ /* Log((DEVICE_NAME ":%.*Rhxd\n", MBLKL(pMsg), pMsg->b_rptr)); */
+ }
+}
+#endif
+
+
+/**
+ * Helper.
+ */
+DECLINLINE(bool) vboxNetFltPortSolarisIsHostMac(PVBOXNETFLTINS pThis, PCRTMAC pMac)
+{
+ return pThis->u.s.MacAddr.au16[0] == pMac->au16[0]
+ && pThis->u.s.MacAddr.au16[1] == pMac->au16[1]
+ && pThis->u.s.MacAddr.au16[2] == pMac->au16[2];
+}
+
+
+/**
+ * Receive (rx) entry point.
+ *
+ * @param pvData Private data.
+ * @param hResource The resource handle.
+ * @param pMsg The packet.
+ * @param fLoopback Whether this is a loopback packet or not.
+ */
+LOCAL void vboxNetFltSolarisRecv(void *pvData, mac_resource_handle_t hResource, mblk_t *pMsg, boolean_t fLoopback)
+{
+ Log((DEVICE_NAME ":vboxNetFltSolarisRecv pvData=%p pMsg=%p fLoopback=%d cbData=%d\n", pvData, pMsg, fLoopback,
+ pMsg ? MBLKL(pMsg) : 0));
+
+ PVBOXNETFLTINS pThis = (PVBOXNETFLTINS)pvData;
+ AssertPtrReturnVoid(pThis);
+ AssertPtrReturnVoid(pMsg);
+
+ /*
+ * Active? Retain the instance and increment the busy counter.
+ */
+ if (!vboxNetFltTryRetainBusyActive(pThis))
+ {
+ freemsgchain(pMsg);
+ return;
+ }
+
+ uint32_t fSrc = INTNETTRUNKDIR_WIRE;
+ PRTNETETHERHDR pEthHdr = (PRTNETETHERHDR)pMsg->b_rptr;
+ if ( MBLKL(pMsg) >= sizeof(RTNETETHERHDR)
+ && vboxNetFltPortSolarisIsHostMac(pThis, &pEthHdr->SrcMac))
+ fSrc = INTNETTRUNKDIR_HOST;
+
+ /*
+ * Route all received packets into the internal network.
+ */
+ uint16_t cFailed = 0;
+ for (mblk_t *pCurMsg = pMsg; pCurMsg != NULL; pCurMsg = pCurMsg->b_next)
+ {
+ unsigned cSegs = vboxNetFltSolarisMBlkCalcSGSegs(pThis, pCurMsg);
+ PINTNETSG pSG = (PINTNETSG)alloca(RT_UOFFSETOF_DYN(INTNETSG, aSegs[cSegs]));
+ int rc = vboxNetFltSolarisMBlkToSG(pThis, pMsg, pSG, cSegs, fSrc);
+ if (RT_SUCCESS(rc))
+ pThis->pSwitchPort->pfnRecv(pThis->pSwitchPort, NULL, pSG, fSrc);
+ else
+ cFailed++;
+ }
+ vboxNetFltRelease(pThis, true /* fBusy */);
+
+ if (RT_UNLIKELY(cFailed))
+ LogRel((DEVICE_NAME ":vboxNetFltSolarisMBlkToSG failed for %u packets.\n", cFailed));
+
+ freemsgchain(pMsg);
+
+ NOREF(hResource);
+}
+
+
+#if 0
+/**
+ * MAC layer link notification hook.
+ *
+ * @param pvArg Opaque pointer to the instance.
+ * @param Type Notification Type.
+ *
+ * @remarks This hook will be invoked for various changes to the underlying
+ * interface even when VMs aren't running so don't do any funky stuff
+ * here.
+ */
+LOCAL void vboxNetFltSolarisLinkNotify(void *pvArg, mac_notify_type_t Type)
+{
+ LogRel((DEVICE_NAME ":vboxNetFltSolarisLinkNotify pvArg=%p Type=%d\n", pvArg, Type));
+
+ PVBOXNETFLTINS pThis = pvArg;
+ AssertPtrReturnVoid(pThis);
+ AssertPtrReturnVoid(pThis->u.s.hInterface);
+
+ switch (Type)
+ {
+ case MAC_NOTE_LINK:
+ {
+ LogRel((DEVICE_NAME ":vboxNetFltSolarisLinkNotify link state change\n"));
+ link_state_t hLinkState = mac_stat_get(pThis->u.s.hInterface, MAC_STAT_LINK_STATE);
+ bool fDisconnectedFromHost = hLinkState == LINK_STATE_UP ? false : true;
+ if (fDisconnectedFromHost != ASMAtomicUoReadBool(&pThis->fDisconnectedFromHost))
+ {
+ ASMAtomicUoWriteBool(&pThis->fDisconnectedFromHost, fDisconnectedFromHost);
+ LogRel((DEVICE_NAME ":vboxNetFltSolarisLinkNotify link state change: new state=%s\n",
+ fDisconnectedFromHost ? "DOWN" : "UP"));
+ }
+ break;
+ }
+
+ default:
+ return;
+ }
+}
+#endif
+
+
+/**
+ * Report capabilities and MAC address to IntNet after obtaining the MAC address
+ * of the underlying interface for a VNIC or the current interface if it's a
+ * physical/ether-stub interface.
+ *
+ * @param pThis The instance.
+ * @param hInterface The Interface handle.
+ * @param fIsVNIC Whether this interface handle corresponds to a VNIC
+ * or not.
+ *
+ * @remarks Retains the instance while doing it's job.
+ * @returns VBox status code.
+ */
+LOCAL int vboxNetFltSolarisReportInfo(PVBOXNETFLTINS pThis, mac_handle_t hInterface, bool fIsVNIC)
+{
+ mac_handle_t hLowerMac = NULL;
+ if (!fIsVNIC)
+ hLowerMac = hInterface;
+ else
+ {
+ hLowerMac = mac_get_lower_mac_handle(hInterface);
+ if (RT_UNLIKELY(!hLowerMac))
+ {
+ LogRel((DEVICE_NAME ":vboxNetFltSolarisReportInfo failed to get lower MAC handle for '%s'\n", pThis->szName));
+ return VERR_INVALID_HANDLE;
+ }
+ }
+
+ pThis->u.s.hInterface = hLowerMac;
+
+#if 0
+ /*
+ * Try setup link notification hooks, this might fail if mac_no_notification()
+ * doesn't support it. We won't bother using the private function since link notification
+ * isn't critical for us and ignore failures.
+ */
+ pThis->u.s.hNotify = mac_notify_add(hLowerMac, vboxNetFltSolarisLinkNotify, pThis);
+ if (!pThis->u.s.hNotify)
+ LogRel((DEVICE_NAME ":vboxNetFltSolarisReportInfo Warning! Failed to setup link notification hook.\n"));
+#endif
+
+ mac_unicast_primary_get(hLowerMac, (uint8_t *)pThis->u.s.MacAddr.au8);
+ if (vboxNetFltTryRetainBusyNotDisconnected(pThis))
+ {
+ Assert(pThis->pSwitchPort);
+ Log((DEVICE_NAME ":vboxNetFltSolarisReportInfo phys mac %.6Rhxs\n", &pThis->u.s.MacAddr));
+ pThis->pSwitchPort->pfnReportMacAddress(pThis->pSwitchPort, &pThis->u.s.MacAddr);
+ pThis->pSwitchPort->pfnReportPromiscuousMode(pThis->pSwitchPort, false); /** @todo Promisc */
+ pThis->pSwitchPort->pfnReportGsoCapabilities(pThis->pSwitchPort, 0, INTNETTRUNKDIR_WIRE | INTNETTRUNKDIR_HOST);
+ pThis->pSwitchPort->pfnReportNoPreemptDsts(pThis->pSwitchPort, 0 /* none */);
+ vboxNetFltRelease(pThis, true /*fBusy*/);
+ return VINF_SUCCESS;
+ }
+ else
+ LogRel((DEVICE_NAME ":vboxNetFltSolarisReportInfo failed to retain interface. pThis=%p\n", pThis));
+
+ return VERR_INTNET_FLT_IF_BUSY;
+}
+
+
+/**
+ * Initialize a VNIC, optionally from a template.
+ *
+ * @param pThis The instance.
+ * @param pVNIC Pointer to the VNIC.
+ *
+ * @returns VBox status code.
+ */
+LOCAL int vboxNetFltSolarisInitVNIC(PVBOXNETFLTINS pThis, PVBOXNETFLTVNIC pVNIC)
+{
+ /*
+ * Some paranoia.
+ */
+ AssertReturn(pThis, VERR_INVALID_PARAMETER);
+ AssertReturn(pVNIC, VERR_INVALID_PARAMETER);
+ AssertReturn(pVNIC->hInterface, VERR_INVALID_POINTER);
+ AssertReturn(pVNIC->hLinkId != DATALINK_INVALID_LINKID, VERR_INVALID_HANDLE);
+ AssertReturn(!pVNIC->hClient, VERR_INVALID_POINTER);
+
+ int rc = mac_client_open(pVNIC->hInterface, &pVNIC->hClient,
+ NULL, /* name of this client */
+ MAC_OPEN_FLAGS_USE_DATALINK_NAME | /* client name same as underlying NIC */
+ MAC_OPEN_FLAGS_MULTI_PRIMARY /* allow multiple primary unicasts */
+ );
+ if (RT_LIKELY(!rc))
+ {
+ if (pVNIC->pVNICTemplate)
+ rc = mac_client_set_resources(pVNIC->hClient, &pVNIC->pVNICTemplate->Resources);
+
+ if (RT_LIKELY(!rc))
+ {
+ Log((DEVICE_NAME ":vboxNetFltSolarisInitVNIC succesfully initialized VNIC.\n"));
+ return VINF_SUCCESS;
+ }
+ else
+ {
+ LogRel((DEVICE_NAME ":vboxNetFltSolarisInitVNIC mac_client_set_resources failed. rc=%d\n", rc));
+ rc = VERR_INTNET_FLT_VNIC_INIT_FAILED;
+ }
+
+ mac_client_close(pVNIC->hClient, 0 /* flags */);
+ pVNIC->hClient = NULL;
+ }
+ else
+ LogRel((DEVICE_NAME ":vboxNetFltSolarisInitVNIC failed to open mac client for '%s' rc=%d\n", pThis->szName, rc));
+
+ return VERR_INTNET_FLT_VNIC_OPEN_FAILED;
+}
+
+
+
+/**
+ * Get the underlying link name for a VNIC (template).
+ *
+ * @return VBox status code.
+ * @param hVNICMacHandle The handle to the VNIC.
+ * @param pszLowerLinkName Where to store the lower-mac linkname, must be
+ * at least MAXLINKNAMELEN in size.
+ */
+LOCAL int vboxNetFltSolarisGetLowerLinkName(mac_handle_t hVNICMacHandle, char *pszLowerLinkName)
+{
+ Assert(mac_is_vnic(hVNICMacHandle));
+ mac_handle_t hPhysLinkHandle = mac_get_lower_mac_handle(hVNICMacHandle);
+ if (RT_LIKELY(hPhysLinkHandle))
+ {
+ datalink_id_t PhysLinkId;
+ const char *pszMacName = mac_name(hPhysLinkHandle);
+ int rc = vboxNetFltSolarisGetLinkId(pszMacName, &PhysLinkId);
+ if (RT_SUCCESS(rc))
+ {
+ rc = dls_mgmt_get_linkinfo(PhysLinkId, pszLowerLinkName, NULL /*class*/, NULL /*media*/, NULL /*flags*/);
+ if (RT_LIKELY(!rc))
+ return VINF_SUCCESS;
+
+ LogRel((DEVICE_NAME ":vboxNetFltSolarisGetLowerLinkName failed to get link info. pszMacName=%s pszLowerLinkName=%s\n",
+ pszMacName, pszLowerLinkName));
+ return VERR_INTNET_FLT_LOWER_LINK_INFO_NOT_FOUND;
+ }
+
+ LogRel((DEVICE_NAME ":vboxNetFltSolarisGetLowerLinkName failed to get link id. pszMacName=%s pszLowerLinkName=%s\n",
+ pszMacName, pszLowerLinkName));
+ return VERR_INTNET_FLT_LOWER_LINK_ID_NOT_FOUND;
+ }
+
+ LogRel((DEVICE_NAME ":vboxNetFltSolarisGetLowerLinkName failed to get lower-mac. pszLowerLinkName=%s\n", pszLowerLinkName));
+ return VERR_INTNET_FLT_LOWER_LINK_OPEN_FAILED;
+}
+
+
+/**
+ * Initializes the VNIC template. This involves opening the template VNIC to
+ * retreive info. like the VLAN Id, underlying MAC address etc.
+ *
+ * @param pThis The VM connection instance.
+ * @param pVNICTemplate Pointer to a VNIC template to initialize.
+ *
+ * @returns VBox status code.
+ */
+LOCAL int vboxNetFltSolarisInitVNICTemplate(PVBOXNETFLTINS pThis, PVBOXNETFLTVNICTEMPLATE pVNICTemplate)
+{
+ Log((DEVICE_NAME ":vboxNetFltSolarisInitVNICTemplate pThis=%p pVNICTemplate=%p\n", pThis, pVNICTemplate));
+
+ AssertReturn(pVNICTemplate, VERR_INVALID_PARAMETER);
+ AssertReturn(pThis->u.s.fIsVNICTemplate == true, VERR_INVALID_STATE);
+
+ /*
+ * Get the VNIC template's datalink ID.
+ */
+ datalink_id_t VNICLinkId;
+ int rc = vboxNetFltSolarisGetLinkId(pThis->szName, &VNICLinkId);
+ if (RT_SUCCESS(rc))
+ {
+ /*
+ * Open the VNIC to obtain a MAC handle so as to retreive the VLAN ID.
+ */
+ mac_handle_t hInterface;
+ rc = mac_open_by_linkid(VNICLinkId, &hInterface);
+ if (!rc)
+ {
+ /*
+ * Get the underlying linkname.
+ */
+ AssertCompile(sizeof(pVNICTemplate->szLinkName) >= MAXLINKNAMELEN);
+ rc = vboxNetFltSolarisGetLowerLinkName(hInterface, pVNICTemplate->szLinkName);
+ if (RT_SUCCESS(rc))
+ {
+ /*
+ * Now open the VNIC template to retrieve the VLAN Id & resources.
+ */
+ mac_client_handle_t hClient;
+ rc = mac_client_open(hInterface, &hClient,
+ NULL, /* name of this client */
+ MAC_OPEN_FLAGS_USE_DATALINK_NAME | /* client name same as underlying NIC */
+ MAC_OPEN_FLAGS_MULTI_PRIMARY /* allow multiple primary unicasts */
+ );
+ if (RT_LIKELY(!rc))
+ {
+ pVNICTemplate->uVLANId = mac_client_vid(hClient);
+ mac_client_get_resources(hClient, &pVNICTemplate->Resources);
+ mac_client_close(hClient, 0 /* fFlags */);
+ mac_close(hInterface);
+
+ LogRel((DEVICE_NAME ":vboxNetFltSolarisInitVNICTemplate successfully init. VNIC template. szLinkName=%s "
+ "VLAN Id=%u\n", pVNICTemplate->szLinkName, pVNICTemplate->uVLANId));
+ return VINF_SUCCESS;
+ }
+ else
+ {
+ LogRel((DEVICE_NAME ":vboxNetFltSolarisInitVNICTemplate failed to open VNIC template. rc=%d\n", rc));
+ rc = VERR_INTNET_FLT_IF_FAILED;
+ }
+ }
+ else
+ LogRel((DEVICE_NAME ":vboxNetFltSolarisInitVNICTemplate failed to get lower linkname for VNIC template '%s'.\n",
+ pThis->szName));
+
+ mac_close(hInterface);
+ }
+ else
+ {
+ LogRel((DEVICE_NAME ":vboxNetFltSolarisInitVNICTemplate failed to open by link ID. rc=%d\n", rc));
+ rc = VERR_INTNET_FLT_IF_FAILED;
+ }
+ }
+ else
+ LogRel((DEVICE_NAME ":vboxNetFltSolarisInitVNICTemplate failed to get VNIC template link Id. rc=%d\n", rc));
+
+ return rc;
+}
+
+
+/**
+ * Allocate a VNIC structure.
+ *
+ * @returns An allocated VNIC structure or NULL in case of errors.
+ */
+LOCAL PVBOXNETFLTVNIC vboxNetFltSolarisAllocVNIC(void)
+{
+ PVBOXNETFLTVNIC pVNIC = RTMemAllocZ(sizeof(VBOXNETFLTVNIC));
+ if (RT_UNLIKELY(!pVNIC))
+ return NULL;
+
+ pVNIC->u32Magic = VBOXNETFLTVNIC_MAGIC;
+ pVNIC->fCreated = false;
+ pVNIC->pVNICTemplate = NULL;
+ pVNIC->pvIf = NULL;
+ pVNIC->hInterface = NULL;
+ pVNIC->hLinkId = DATALINK_INVALID_LINKID;
+ pVNIC->hClient = NULL;
+ pVNIC->hUnicast = NULL;
+ pVNIC->hPromisc = NULL;
+ RT_ZERO(pVNIC->szName);
+ list_link_init(&pVNIC->hNode);
+ return pVNIC;
+}
+
+
+/**
+ * Frees an allocated VNIC.
+ *
+ * @param pVNIC Pointer to the VNIC.
+ */
+DECLINLINE(void) vboxNetFltSolarisFreeVNIC(PVBOXNETFLTVNIC pVNIC)
+{
+ RTMemFree(pVNIC);
+}
+
+
+/**
+ * Destroy a created VNIC if it was created by us, or just
+ * de-initializes the VNIC freeing up resources handles.
+ *
+ * @param pVNIC Pointer to the VNIC.
+ */
+LOCAL void vboxNetFltSolarisDestroyVNIC(PVBOXNETFLTVNIC pVNIC)
+{
+ AssertPtrReturnVoid(pVNIC);
+ AssertMsgReturnVoid(pVNIC->u32Magic == VBOXNETFLTVNIC_MAGIC, ("pVNIC=%p u32Magic=%#x\n", pVNIC, pVNIC->u32Magic));
+ if (pVNIC)
+ {
+ if (pVNIC->hClient)
+ {
+#if 0
+ if (pVNIC->hUnicast)
+ {
+ mac_unicast_remove(pVNIC->hClient, pVNIC->hUnicast);
+ pVNIC->hUnicast = NULL;
+ }
+#endif
+
+ if (pVNIC->hPromisc)
+ {
+ mac_promisc_remove(pVNIC->hPromisc);
+ pVNIC->hPromisc = NULL;
+ }
+
+ mac_rx_clear(pVNIC->hClient);
+
+ mac_client_close(pVNIC->hClient, 0 /* fFlags */);
+ pVNIC->hClient = NULL;
+ }
+
+ if (pVNIC->hInterface)
+ {
+ mac_close(pVNIC->hInterface);
+ pVNIC->hInterface = NULL;
+ }
+
+ if (pVNIC->fCreated)
+ {
+ vnic_delete(pVNIC->hLinkId, 0 /* Flags */);
+ pVNIC->hLinkId = DATALINK_INVALID_LINKID;
+ pVNIC->fCreated = false;
+ }
+
+ if (pVNIC->pVNICTemplate)
+ {
+ RTMemFree(pVNIC->pVNICTemplate);
+ pVNIC->pVNICTemplate = NULL;
+ }
+ }
+}
+
+
+/**
+ * Create a non-persistent VNIC over the given interface.
+ *
+ * @param pThis The VM connection instance.
+ * @param ppVNIC Where to store the created VNIC.
+ *
+ * @returns VBox status code.
+ */
+LOCAL int vboxNetFltSolarisCreateVNIC(PVBOXNETFLTINS pThis, PVBOXNETFLTVNIC *ppVNIC)
+{
+ Log((DEVICE_NAME ":vboxNetFltSolarisCreateVNIC pThis=%p\n", pThis));
+
+ AssertReturn(pThis, VERR_INVALID_POINTER);
+ AssertReturn(ppVNIC, VERR_INVALID_POINTER);
+
+ int rc = VERR_INVALID_STATE;
+ PVBOXNETFLTVNIC pVNIC = vboxNetFltSolarisAllocVNIC();
+ if (RT_UNLIKELY(!pVNIC))
+ return VERR_NO_MEMORY;
+
+ /*
+ * Set a random MAC address for now. It will be changed to the VM interface's
+ * MAC address later, see vboxNetFltPortOsNotifyMacAddress().
+ */
+ RTMAC GuestMac;
+ GuestMac.au8[0] = 0x08;
+ GuestMac.au8[1] = 0x00;
+ GuestMac.au8[2] = 0x27;
+ RTRandBytes(&GuestMac.au8[3], 3);
+
+ AssertCompile(sizeof(RTMAC) <= MAXMACADDRLEN);
+
+ const char *pszLinkName = pThis->szName;
+ uint16_t uVLANId = VLAN_ID_NONE;
+ vnic_mac_addr_type_t AddrType = VNIC_MAC_ADDR_TYPE_FIXED;
+ vnic_ioc_diag_t Diag = VNIC_IOC_DIAG_NONE;
+ int MacSlot = 0;
+ int MacLen = sizeof(GuestMac);
+ uint32_t fFlags = 0;
+
+ if (pThis->u.s.fIsVNICTemplate)
+ {
+ pVNIC->pVNICTemplate = RTMemAllocZ(sizeof(VBOXNETFLTVNICTEMPLATE));
+ if (RT_UNLIKELY(!pVNIC->pVNICTemplate))
+ {
+ vboxNetFltSolarisFreeVNIC(pVNIC);
+ return VERR_NO_MEMORY;
+ }
+
+ /*
+ * Initialize the VNIC template.
+ */
+ rc = vboxNetFltSolarisInitVNICTemplate(pThis, pVNIC->pVNICTemplate);
+ if (RT_FAILURE(rc))
+ {
+ LogRel((DEVICE_NAME ":vboxNetFltSolarisCreateVNIC failed to initialize VNIC from VNIC template. rc=%Rrc\n", rc));
+ vboxNetFltSolarisFreeVNIC(pVNIC);
+ return rc;
+ }
+
+ pszLinkName = pVNIC->pVNICTemplate->szLinkName;
+ uVLANId = pVNIC->pVNICTemplate->uVLANId;
+#if 0
+ /*
+ * Required only if we're creating a VLAN interface & not a VNIC with a VLAN Id.
+ */
+ if (uVLANId != VLAN_ID_NONE)
+ fFlags |= MAC_VLAN;
+#endif
+ Log((DEVICE_NAME ":vboxNetFltSolarisCreateVNIC pThis=%p VLAN Id=%u\n", pThis, uVLANId));
+ }
+
+ /*
+ * Make sure the dynamic VNIC we're creating doesn't already exists, if so pick a new instance.
+ * This is to avoid conflicts with users manually creating VNICs whose name starts with VBOXBOW_VNIC_NAME.
+ */
+ do
+ {
+ AssertCompile(sizeof(pVNIC->szName) > sizeof(VBOXBOW_VNIC_NAME "18446744073709551615" /* UINT64_MAX */));
+ RTStrPrintf(pVNIC->szName, sizeof(pVNIC->szName), "%s%RU64", VBOXBOW_VNIC_NAME, g_VBoxNetFltSolarisVNICId);
+ mac_handle_t hTmpMacHandle;
+ rc = mac_open_by_linkname(pVNIC->szName, &hTmpMacHandle);
+ if (rc)
+ break;
+ mac_close(hTmpMacHandle);
+ ASMAtomicIncU64(&g_VBoxNetFltSolarisVNICId);
+ } while (1);
+
+ /*
+ * Create the VNIC under 'pszLinkName', which can be the one from the VNIC template or can
+ * be a physical interface.
+ */
+ rc = vnic_create(pVNIC->szName, pszLinkName, &AddrType, &MacLen, GuestMac.au8, &MacSlot, 0 /* Mac-Prefix Length */, uVLANId,
+ fFlags, &pVNIC->hLinkId, &Diag, NULL /* Reserved */);
+ if (!rc)
+ {
+ pVNIC->fCreated = true;
+ ASMAtomicIncU64(&g_VBoxNetFltSolarisVNICId);
+
+ /*
+ * Now try opening the created VNIC.
+ */
+ rc = mac_open_by_linkid(pVNIC->hLinkId, &pVNIC->hInterface);
+ if (!rc)
+ {
+ /*
+ * Initialize the VNIC from the physical interface or the VNIC template.
+ */
+ rc = vboxNetFltSolarisInitVNIC(pThis, pVNIC);
+ if (RT_SUCCESS(rc))
+ {
+ Log((DEVICE_NAME ":vboxNetFltSolarisCreateVNIC created VNIC '%s' over '%s' with random mac %.6Rhxs\n",
+ pVNIC->szName, pszLinkName, &GuestMac));
+ *ppVNIC = pVNIC;
+ return VINF_SUCCESS;
+ }
+
+ LogRel((DEVICE_NAME ":vboxNetFltSolarisCreateVNIC vboxNetFltSolarisInitVNIC failed. rc=%d\n", rc));
+ mac_close(pVNIC->hInterface);
+ pVNIC->hInterface = NULL;
+ }
+ else
+ {
+ LogRel((DEVICE_NAME ":vboxNetFltSolarisCreateVNIC logrel failed to open VNIC '%s' over '%s'. rc=%d\n", pVNIC->szName,
+ pThis->szName, rc));
+ rc = VERR_INTNET_FLT_VNIC_LINK_ID_NOT_FOUND;
+ }
+
+ vboxNetFltSolarisDestroyVNIC(pVNIC);
+ }
+ else
+ {
+ LogRel((DEVICE_NAME ":vboxNetFltSolarisCreateVNIC failed to create VNIC '%s' over '%s' rc=%d Diag=%d\n", pVNIC->szName,
+ pszLinkName, rc, Diag));
+ rc = VERR_INTNET_FLT_VNIC_CREATE_FAILED;
+ }
+
+ vboxNetFltSolarisFreeVNIC(pVNIC);
+
+ return rc;
+}
+
+
+/**
+ * Wrapper for getting the datalink ID given the MAC name.
+ *
+ * @param pszMacName The MAC name.
+ * @param pLinkId Where to store the datalink ID.
+ *
+ * @returns VBox status code.
+ */
+DECLINLINE(int) vboxNetFltSolarisGetLinkId(const char *pszMacName, datalink_id_t *pLinkId)
+{
+ /*
+ * dls_mgmt_get_linkid() requires to be in a state to answer upcalls. We should always use this
+ * first before resorting to other means to retrieve the MAC name.
+ */
+ int rc = dls_mgmt_get_linkid(pszMacName, pLinkId);
+ if (rc)
+ rc = dls_devnet_macname2linkid(pszMacName, pLinkId);
+
+ if (RT_LIKELY(!rc))
+ return VINF_SUCCESS;
+
+ LogRel((DEVICE_NAME ":vboxNetFltSolarisGetLinkId failed for '%s'. rc=%d\n", pszMacName, rc));
+ return RTErrConvertFromErrno(rc);
+}
+
+
+/**
+ * Set the promiscuous mode RX hook.
+ *
+ * @param pThis The VM connection instance.
+ * @param pVNIC Pointer to the VNIC.
+ *
+ * @returns VBox status code.
+ */
+DECLINLINE(int) vboxNetFltSolarisSetPromisc(PVBOXNETFLTINS pThis, PVBOXNETFLTVNIC pVNIC)
+{
+ int rc = VINF_SUCCESS;
+ if (!pVNIC->hPromisc)
+ {
+ rc = mac_promisc_add(pVNIC->hClient, MAC_CLIENT_PROMISC_FILTERED, vboxNetFltSolarisRecv, pThis, &pVNIC->hPromisc,
+ MAC_PROMISC_FLAGS_NO_TX_LOOP | MAC_PROMISC_FLAGS_VLAN_TAG_STRIP | MAC_PROMISC_FLAGS_NO_PHYS);
+ if (RT_UNLIKELY(rc))
+ LogRel((DEVICE_NAME ":vboxNetFltSolarisSetPromisc failed. rc=%d\n", rc));
+ rc = RTErrConvertFromErrno(rc);
+ }
+ return rc;
+}
+
+
+/**
+ * Clear the promiscuous mode RX hook.
+ *
+ * @param pThis The VM connection instance.
+ * @param pVNIC Pointer to the VNIC.
+ */
+DECLINLINE(void) vboxNetFltSolarisRemovePromisc(PVBOXNETFLTINS pThis, PVBOXNETFLTVNIC pVNIC)
+{
+ if (pVNIC->hPromisc)
+ {
+ mac_promisc_remove(pVNIC->hPromisc);
+ pVNIC->hPromisc = NULL;
+ }
+}
+
+
+/* -=-=-=-=-=- Common Hooks -=-=-=-=-=- */
+
+
+void vboxNetFltPortOsSetActive(PVBOXNETFLTINS pThis, bool fActive)
+{
+ Log((DEVICE_NAME ":vboxNetFltPortOsSetActive pThis=%p fActive=%d\n", pThis, fActive));
+
+ /*
+ * Reactivate/quiesce the interface.
+ */
+ PVBOXNETFLTVNIC pVNIC = list_head(&pThis->u.s.hVNICs);
+ if (fActive)
+ {
+ for (; pVNIC != NULL; pVNIC = list_next(&pThis->u.s.hVNICs, pVNIC))
+ if (pVNIC->hClient)
+ {
+#if 0
+ mac_rx_set(pVNIC->hClient, vboxNetFltSolarisRecv, pThis);
+#endif
+ vboxNetFltSolarisSetPromisc(pThis, pVNIC);
+ }
+ }
+ else
+ {
+ for (; pVNIC != NULL; pVNIC = list_next(&pThis->u.s.hVNICs, pVNIC))
+ if (pVNIC->hClient)
+ {
+#if 0
+ mac_rx_clear(pVNIC->hClient);
+#endif
+ vboxNetFltSolarisRemovePromisc(pThis, pVNIC);
+ }
+ }
+}
+
+
+int vboxNetFltOsDisconnectIt(PVBOXNETFLTINS pThis)
+{
+ Log((DEVICE_NAME ":vboxNetFltOsDisconnectIt pThis=%p\n", pThis));
+ return VINF_SUCCESS;
+}
+
+
+int vboxNetFltOsConnectIt(PVBOXNETFLTINS pThis)
+{
+ Log((DEVICE_NAME ":vboxNetFltOsConnectIt pThis=%p\n", pThis));
+ return VINF_SUCCESS;
+}
+
+
+void vboxNetFltOsDeleteInstance(PVBOXNETFLTINS pThis)
+{
+ Log((DEVICE_NAME ":vboxNetFltOsDeleteInstance pThis=%p\n", pThis));
+
+ if (pThis->u.s.hNotify)
+ mac_notify_remove(pThis->u.s.hNotify, B_TRUE /* Wait */);
+
+ /*
+ * Destroy all managed VNICs. If a VNIC was passed to us, there
+ * will be only 1 item in the list, otherwise as many interfaces
+ * that were somehow not destroyed using DisconnectInterface() will be
+ * present.
+ */
+ PVBOXNETFLTVNIC pVNIC = NULL;
+ while ((pVNIC = list_remove_head(&pThis->u.s.hVNICs)) != NULL)
+ {
+ vboxNetFltSolarisDestroyVNIC(pVNIC);
+ vboxNetFltSolarisFreeVNIC(pVNIC);
+ }
+
+ list_destroy(&pThis->u.s.hVNICs);
+}
+
+
+int vboxNetFltOsInitInstance(PVBOXNETFLTINS pThis, void *pvContext)
+{
+ Log((DEVICE_NAME ":vboxNetFltOsInitInstance pThis=%p pvContext=%p\n", pThis, pvContext));
+
+ /*
+ * Figure out if the interface is a VNIC or a physical/etherstub/whatever NIC, then
+ * do the actual VNIC creation if necessary in vboxNetFltPortOsConnectInterface().
+ */
+ mac_handle_t hInterface;
+ int rc = mac_open_by_linkname(pThis->szName, &hInterface);
+ if (RT_LIKELY(!rc))
+ {
+ rc = mac_is_vnic(hInterface);
+ if (!rc)
+ {
+ Log((DEVICE_NAME ":vboxNetFltOsInitInstance pThis=%p physical interface '%s' detected.\n", pThis, pThis->szName));
+ pThis->u.s.fIsVNIC = false;
+ }
+ else
+ {
+ pThis->u.s.fIsVNIC = true;
+ if (RTStrNCmp(pThis->szName, VBOXBOW_VNIC_TEMPLATE_NAME, sizeof(VBOXBOW_VNIC_TEMPLATE_NAME) - 1) == 0)
+ {
+ Log((DEVICE_NAME ":vboxNetFltOsInitInstance pThis=%p VNIC template '%s' detected.\n", pThis, pThis->szName));
+ pThis->u.s.fIsVNICTemplate = true;
+ }
+ }
+
+ if ( pThis->u.s.fIsVNIC
+ && !pThis->u.s.fIsVNICTemplate)
+ Log((DEVICE_NAME ":vboxNetFltOsInitInstance pThis=%p VNIC '%s' detected.\n", pThis, pThis->szName));
+
+ /*
+ * Report info. (host MAC address, promiscuous, GSO capabilities etc.) to IntNet.
+ */
+ rc = vboxNetFltSolarisReportInfo(pThis, hInterface, pThis->u.s.fIsVNIC);
+ if (RT_FAILURE(rc))
+ LogRel((DEVICE_NAME ":vboxNetFltOsInitInstance failed to report info. rc=%d\n", rc));
+
+ mac_close(hInterface);
+ }
+ else
+ {
+ LogRel((DEVICE_NAME ":vboxNetFltOsInitInstance failed to open link '%s'! rc=%d\n", pThis->szName, rc));
+ rc = VERR_INTNET_FLT_IF_FAILED;
+ }
+
+ return rc;
+}
+
+
+int vboxNetFltOsPreInitInstance(PVBOXNETFLTINS pThis)
+{
+ /*
+ * Init. the solaris specific data.
+ */
+ pThis->u.s.fIsVNIC = false;
+ pThis->u.s.fIsVNICTemplate = false;
+ list_create(&pThis->u.s.hVNICs, sizeof(VBOXNETFLTVNIC), offsetof(VBOXNETFLTVNIC, hNode));
+ pThis->u.s.hNotify = NULL;
+ RT_ZERO(pThis->u.s.MacAddr);
+ return VINF_SUCCESS;
+}
+
+
+bool vboxNetFltOsMaybeRediscovered(PVBOXNETFLTINS pThis)
+{
+ /*
+ * @todo Think about this.
+ */
+ return false;
+}
+
+
+int vboxNetFltPortOsXmit(PVBOXNETFLTINS pThis, void *pvIfData, PINTNETSG pSG, uint32_t fDst)
+{
+ /*
+ * Validate parameters.
+ */
+ PVBOXNETFLTVNIC pVNIC = pvIfData;
+ AssertPtrReturn(pVNIC, VERR_INVALID_POINTER);
+ AssertMsgReturn(pVNIC->u32Magic == VBOXNETFLTVNIC_MAGIC,
+ ("Invalid magic=%#x (expected %#x)\n", pVNIC->u32Magic, VBOXNETFLTVNIC_MAGIC),
+ VERR_INVALID_MAGIC);
+
+ /*
+ * Xmit the packet down the appropriate VNIC interface.
+ */
+ int rc = VINF_SUCCESS;
+ mblk_t *pMsg = vboxNetFltSolarisMBlkFromSG(pThis, pSG, fDst);
+ if (RT_LIKELY(pMsg))
+ {
+ Log((DEVICE_NAME ":vboxNetFltPortOsXmit pThis=%p cbData=%d\n", pThis, MBLKL(pMsg)));
+
+ mac_tx_cookie_t pXmitCookie = mac_tx(pVNIC->hClient, pMsg, 0 /* Hint */, MAC_DROP_ON_NO_DESC, NULL /* return message */);
+ if (RT_LIKELY(!pXmitCookie))
+ return VINF_SUCCESS;
+
+ pMsg = NULL;
+ rc = VERR_NET_IO_ERROR;
+ LogRel((DEVICE_NAME ":vboxNetFltPortOsXmit Xmit failed pVNIC=%p.\n", pVNIC));
+ }
+ else
+ {
+ LogRel((DEVICE_NAME ":vboxNetFltPortOsXmit no memory for allocating Xmit packet.\n"));
+ rc = VERR_NO_MEMORY;
+ }
+
+ return rc;
+}
+
+
+void vboxNetFltPortOsNotifyMacAddress(PVBOXNETFLTINS pThis, void *pvIfData, PCRTMAC pMac)
+{
+ Log((DEVICE_NAME ":vboxNetFltPortOSNotifyMacAddress pszIf=%s pszVNIC=%s MAC=%.6Rhxs\n", pThis->szName,
+ ((PVBOXNETFLTVNIC)pvIfData)->szName, pMac));
+
+ /*
+ * Validate parameters.
+ */
+ PVBOXNETFLTVNIC pVNIC = pvIfData;
+ AssertPtrReturnVoid(pVNIC);
+ AssertMsgReturnVoid(pVNIC->u32Magic == VBOXNETFLTVNIC_MAGIC,
+ ("Invalid pVNIC=%p magic=%#x (expected %#x)\n", pvIfData, pVNIC->u32Magic, VBOXNETFLTVNIC_MAGIC));
+ AssertMsgReturnVoid(pVNIC->hLinkId != DATALINK_INVALID_LINKID,
+ ("Invalid hLinkId pVNIC=%p magic=%#x\n", pVNIC, pVNIC->u32Magic));
+
+ /*
+ * Set the MAC address of the VNIC to the one used by the VM interface.
+ */
+ uchar_t au8GuestMac[MAXMACADDRLEN];
+ bcopy(pMac->au8, au8GuestMac, sizeof(RTMAC));
+
+ vnic_mac_addr_type_t AddrType = VNIC_MAC_ADDR_TYPE_FIXED;
+ vnic_ioc_diag_t Diag = VNIC_IOC_DIAG_NONE;
+ int MacSlot = 0;
+ int MacLen = sizeof(RTMAC);
+
+ int rc = vnic_modify_addr(pVNIC->hLinkId, &AddrType, &MacLen, au8GuestMac, &MacSlot, 0 /* Mac-Prefix Length */, &Diag);
+ if (RT_LIKELY(!rc))
+ {
+ /*
+ * Remove existing unicast address, promisc. and the RX hook.
+ */
+#if 0
+ if (pVNIC->hUnicast)
+ {
+ mac_rx_clear(pVNIC->hClient);
+ mac_unicast_remove(pVNIC->hClient, pVNIC->hUnicast);
+ pVNIC->hUnicast = NULL;
+ }
+#endif
+
+ if (pVNIC->hPromisc)
+ {
+ mac_promisc_remove(pVNIC->hPromisc);
+ pVNIC->hPromisc = NULL;
+ }
+
+ mac_diag_t MacDiag = MAC_DIAG_NONE;
+ /* uint16_t uVLANId = pVNIC->pVNICTemplate ? pVNIC->pVNICTemplate->uVLANId : 0; */
+#if 0
+ rc = mac_unicast_add(pVNIC->hClient, NULL, MAC_UNICAST_PRIMARY, &pVNIC->hUnicast, 0 /* VLAN Id */, &MacDiag);
+#endif
+ if (RT_LIKELY(!rc))
+ {
+ rc = vboxNetFltSolarisSetPromisc(pThis, pVNIC);
+#if 0
+ if (RT_SUCCESS(rc))
+ {
+ /*
+ * Set the RX receive function.
+ * This shouldn't be necessary as vboxNetFltPortOsSetActive() will be invoked after this, but in the future,
+ * if the guest NIC changes MAC address this may not be followed by a vboxNetFltPortOsSetActive() call,
+ * so set it here anyway.
+ */
+ mac_rx_set(pVNIC->hClient, vboxNetFltSolarisRecv, pThis);
+ Log((DEVICE_NAME ":vboxNetFltPortOsNotifyMacAddress successfully added unicast address %.6Rhxs\n", pMac));
+ }
+ else
+ LogRel((DEVICE_NAME ":vboxNetFltPortOsNotifyMacAddress failed to set promiscuous mode. rc=%d\n", rc));
+ mac_unicast_remove(pVNIC->hClient, pVNIC->hUnicast);
+ pVNIC->hUnicast = NULL;
+#endif
+ }
+ else
+ {
+ LogRel((DEVICE_NAME ":vboxNetFltPortOsNotifyMacAddress failed to add primary unicast address. rc=%d Diag=%d\n", rc,
+ MacDiag));
+ }
+ }
+ else
+ {
+ /*
+ * They really ought to use EEXIST, but I'm afraid this error comes from the VNIC device driver directly.
+ * Sequence: vnic_modify_addr()->mac_unicast_primary_set()->mac_update_macaddr() which uses a function pointer
+ * to the MAC driver (calls mac_vnic_unicast_set() in our case). Documented here if the error code should change we know
+ * where to look.
+ */
+ if (rc == ENOTSUP)
+ {
+ LogRel((DEVICE_NAME ":vboxNetFltPortOsNotifyMacAddress: failed! a VNIC with mac %.6Rhxs probably already exists.",
+ pMac, rc));
+ LogRel((DEVICE_NAME ":vboxNetFltPortOsNotifyMacAddress: This NIC cannot establish connection. szName=%s szVNIC=%s\n",
+ pThis->szName, pVNIC->szName));
+ }
+ else
+ LogRel((DEVICE_NAME ":vboxNetFltPortOsNotifyMacAddress failed! mac %.6Rhxs rc=%d Diag=%d\n", pMac, rc, Diag));
+ }
+}
+
+
+int vboxNetFltPortOsConnectInterface(PVBOXNETFLTINS pThis, void *pvIf, void **ppvIfData)
+{
+ Log((DEVICE_NAME ":vboxNetFltPortOsConnectInterface pThis=%p pvIf=%p\n", pThis, pvIf));
+
+ int rc = VINF_SUCCESS;
+
+ /*
+ * If the underlying interface is a physical interface or a VNIC template, we need to create
+ * a VNIC per guest NIC.
+ */
+ if ( !pThis->u.s.fIsVNIC
+ || pThis->u.s.fIsVNICTemplate)
+ {
+ PVBOXNETFLTVNIC pVNIC = NULL;
+ rc = vboxNetFltSolarisCreateVNIC(pThis, &pVNIC);
+ if (RT_SUCCESS(rc))
+ {
+ /*
+ * VM Interface<->VNIC association so that we can Xmit/Recv on the right ones.
+ */
+ pVNIC->pvIf = pvIf;
+ *ppvIfData = pVNIC;
+
+ /*
+ * Add the created VNIC to the list of VNICs we manage.
+ */
+ list_insert_tail(&pThis->u.s.hVNICs, pVNIC);
+ return VINF_SUCCESS;
+ }
+ else
+ LogRel((DEVICE_NAME ":vboxNetFltPortOsConnectInterface failed to create VNIC rc=%d\n", rc));
+ }
+ else
+ {
+ /*
+ * This is a VNIC passed to us, use it directly.
+ */
+ PVBOXNETFLTVNIC pVNIC = vboxNetFltSolarisAllocVNIC();
+ if (RT_LIKELY(pVNIC))
+ {
+ pVNIC->fCreated = false;
+
+ rc = mac_open_by_linkname(pThis->szName, &pVNIC->hInterface);
+ if (!rc)
+ {
+ /*
+ * Obtain the data link ID for this VNIC, it's needed for modifying the MAC address among other things.
+ */
+ rc = vboxNetFltSolarisGetLinkId(pThis->szName, &pVNIC->hLinkId);
+ if (RT_SUCCESS(rc))
+ {
+ /*
+ * Initialize the VNIC and add it to the list of managed VNICs.
+ */
+ RTStrPrintf(pVNIC->szName, sizeof(pVNIC->szName), "%s", pThis->szName);
+ rc = vboxNetFltSolarisInitVNIC(pThis, pVNIC);
+ if (!rc)
+ {
+ pVNIC->pvIf = pvIf;
+ *ppvIfData = pVNIC;
+ list_insert_head(&pThis->u.s.hVNICs, pVNIC);
+ return VINF_SUCCESS;
+ }
+ else
+ LogRel((DEVICE_NAME ":vboxNetFltPortOsConnectInterface failed to initialize VNIC. rc=%d\n", rc));
+ }
+ else
+ {
+ LogRel((DEVICE_NAME ":vboxNetFltPortOsConnectInterface failed to get link id for '%s'. rc=%d\n",
+ pThis->szName, rc));
+ }
+ }
+ else
+ {
+ LogRel((DEVICE_NAME ":vboxNetFltPortOsConnectInterface failed to open VNIC '%s'. rc=%d\n", pThis->szName, rc));
+ rc = VERR_OPEN_FAILED;
+ }
+
+ vboxNetFltSolarisFreeVNIC(pVNIC);
+ }
+ else
+ {
+ LogRel((DEVICE_NAME ":vboxNetFltOsInitInstance failed to allocate VNIC private data.\n"));
+ rc = VERR_NO_MEMORY;
+ }
+ }
+
+ return rc;
+}
+
+
+int vboxNetFltPortOsDisconnectInterface(PVBOXNETFLTINS pThis, void *pvIfData)
+{
+ Log((DEVICE_NAME ":vboxNetFltPortOsDisconnectInterface pThis=%p\n", pThis));
+
+ /*
+ * It is possible we get called when vboxNetFltPortOsConnectInterface() didn't succeed
+ * in which case pvIfData will be NULL. See intnetR0NetworkCreateIf() pfnConnectInterface call
+ * through reference counting in SUPR0ObjRelease() for the "pIf" object.
+ */
+ PVBOXNETFLTVNIC pVNIC = pvIfData;
+ if (RT_LIKELY(pVNIC))
+ {
+ AssertMsgReturn(pVNIC->u32Magic == VBOXNETFLTVNIC_MAGIC,
+ ("Invalid magic=%#x (expected %#x)\n", pVNIC->u32Magic, VBOXNETFLTVNIC_MAGIC), VERR_INVALID_POINTER);
+
+ /*
+ * If the underlying interface is a physical interface or a VNIC template, we need to delete the created VNIC.
+ */
+ if ( !pThis->u.s.fIsVNIC
+ || pThis->u.s.fIsVNICTemplate)
+ {
+ /*
+ * Remove the VNIC from the list, destroy and free it.
+ */
+ list_remove(&pThis->u.s.hVNICs, pVNIC);
+ Log((DEVICE_NAME ":vboxNetFltPortOsDisconnectInterface destroying pVNIC=%p\n", pVNIC));
+ vboxNetFltSolarisDestroyVNIC(pVNIC);
+ vboxNetFltSolarisFreeVNIC(pVNIC);
+ }
+ }
+
+ return VINF_SUCCESS;
+}
+
diff --git a/src/VBox/HostDrivers/VBoxNetFlt/solaris/vboxbow.conf b/src/VBox/HostDrivers/VBoxNetFlt/solaris/vboxbow.conf
new file mode 100644
index 00000000..8b4f716d
--- /dev/null
+++ b/src/VBox/HostDrivers/VBoxNetFlt/solaris/vboxbow.conf
@@ -0,0 +1,43 @@
+# $Id: vboxbow.conf $
+## @file
+# Solaris Host VBoxBow (Crossbow) Configuration
+#
+
+#
+# Copyright (C) 2008-2023 Oracle and/or its affiliates.
+#
+# This file is part of VirtualBox base platform packages, as
+# available from https://www.virtualbox.org.
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation, in version 3 of the
+# License.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, see <https://www.gnu.org/licenses>.
+#
+# The contents of this file may alternatively be used under the terms
+# of the Common Development and Distribution License Version 1.0
+# (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+# in the VirtualBox distribution, in which case the provisions of the
+# CDDL are applicable instead of those of the GPL.
+#
+# You may elect to license modified versions of this file under the
+# terms and conditions of either the GPL or the CDDL or both.
+#
+# SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+#
+
+# This needs to go into /platform/i86pc/kernel/drv,
+# while the 64-bit driver object goes into the amd64
+# subdirectory (32-bit drivers goes into the same
+# directory).
+#
+name="vboxbow" parent="pseudo" instance=0;
+
diff --git a/src/VBox/HostDrivers/VBoxNetFlt/solaris/vboxflt.conf b/src/VBox/HostDrivers/VBoxNetFlt/solaris/vboxflt.conf
new file mode 100644
index 00000000..dce8b795
--- /dev/null
+++ b/src/VBox/HostDrivers/VBoxNetFlt/solaris/vboxflt.conf
@@ -0,0 +1,56 @@
+# $Id: vboxflt.conf $
+## @file
+# Solaris Host VBoxFlt Configuration
+#
+
+#
+# Copyright (C) 2008-2023 Oracle and/or its affiliates.
+#
+# This file is part of VirtualBox base platform packages, as
+# available from https://www.virtualbox.org.
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation, in version 3 of the
+# License.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, see <https://www.gnu.org/licenses>.
+#
+# The contents of this file may alternatively be used under the terms
+# of the Common Development and Distribution License Version 1.0
+# (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+# in the VirtualBox distribution, in which case the provisions of the
+# CDDL are applicable instead of those of the GPL.
+#
+# You may elect to license modified versions of this file under the
+# terms and conditions of either the GPL or the CDDL or both.
+#
+# SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+#
+
+# This needs to go into /platform/i86pc/kernel/drv,
+# while the 64-bit driver object goes into the amd64
+# subdirectory (32-bit drivers goes into the same
+# directory).
+#
+name="vboxflt" parent="pseudo" instance=0;
+
+# If the interface being used does not have IPv6 plumbed and you want
+# vboxflt to automatically attach to it by checking for Ipv6 stream every
+# "ipv6-pollinterval" seconds.
+#
+# By default this is disabled meaning if the interface does not have Ipv6
+# plumbed when the virtual machine starts, guest<->host Ipv6 will not work
+# though guest<->remote Ipv6 would work.
+#
+# Enable if you have a dynamically plumbing/unplumbing Ipv6 interface for
+# which you want vboxflt to adjust accordingly, otherwise keep this disabled.
+# Enabling this option will have some minor performance penalty.
+#ipv6-pollinterval=3;
+
diff --git a/src/VBox/HostDrivers/VBoxNetFlt/win/Makefile.kup b/src/VBox/HostDrivers/VBoxNetFlt/win/Makefile.kup
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/src/VBox/HostDrivers/VBoxNetFlt/win/Makefile.kup
diff --git a/src/VBox/HostDrivers/VBoxNetFlt/win/cfg/Makefile.kup b/src/VBox/HostDrivers/VBoxNetFlt/win/cfg/Makefile.kup
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/src/VBox/HostDrivers/VBoxNetFlt/win/cfg/Makefile.kup
diff --git a/src/VBox/HostDrivers/VBoxNetFlt/win/cfg/VBoxNetCfg.cpp b/src/VBox/HostDrivers/VBoxNetFlt/win/cfg/VBoxNetCfg.cpp
new file mode 100644
index 00000000..a686196f
--- /dev/null
+++ b/src/VBox/HostDrivers/VBoxNetFlt/win/cfg/VBoxNetCfg.cpp
@@ -0,0 +1,3770 @@
+/* $Id: VBoxNetCfg.cpp $ */
+/** @file
+ * VBoxNetCfg.cpp - Network Configuration API.
+ */
+
+/*
+ * Copyright (C) 2011-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#define _WIN32_DCOM
+
+#include "VBox/VBoxNetCfg-win.h"
+#include "VBox/VBoxDrvCfg-win.h"
+
+#include <devguid.h>
+#include <regstr.h>
+#include <iprt/win/shlobj.h>
+#include <cfgmgr32.h>
+#include <iprt/win/objbase.h>
+
+#include <Wbemidl.h>
+
+#include <iprt/win/winsock2.h>
+#include <iprt/win/ws2tcpip.h>
+#include <ws2ipdef.h>
+#include <iprt/win/netioapi.h>
+#include <iprt/win/iphlpapi.h>
+
+#include <iprt/asm.h>
+#include <iprt/assertcompile.h>
+#include <iprt/mem.h>
+#include <iprt/list.h>
+#include <iprt/rand.h>
+#include <iprt/string.h>
+#include <iprt/utf16.h>
+#include <VBox/com/string.h>
+
+
+/*********************************************************************************************************************************
+* Defined Constants And Macros *
+*********************************************************************************************************************************/
+#ifndef Assert /** @todo r=bird: where would this be defined? */
+//# ifdef DEBUG
+//# define Assert(_expr) assert(_expr)
+//# else
+//# define Assert(_expr) do{ }while (0)
+//# endif
+# define Assert _ASSERT
+# define AssertMsg(expr, msg) do{}while (0)
+#endif
+
+#define NonStandardLog DoLogging
+#define NonStandardLogFlow(x) DoLogging x
+
+#define SetErrBreak(strAndArgs) \
+ if (1) { \
+ hrc = E_FAIL; \
+ NonStandardLog strAndArgs; \
+ bstrError.printfNoThrow strAndArgs; \
+ break; \
+ } else do {} while (0)
+
+
+#define VBOXNETCFGWIN_NETADP_ID_SZ "sun_VBoxNetAdp"
+#define VBOXNETCFGWIN_NETADP_ID_WSZ RT_CONCAT(L,VBOXNETCFGWIN_NETADP_ID_SZ)
+#define DRIVERHWID VBOXNETCFGWIN_NETADP_ID_WSZ
+
+/* We assume the following name matches the device description in vboxnetadp6.inf */
+#define HOSTONLY_ADAPTER_NAME_SZ "VirtualBox Host-Only Ethernet Adapter"
+#define HOSTONLY_ADAPTER_NAME_WSZ RT_CONCAT(L,HOSTONLY_ADAPTER_NAME_SZ)
+
+#define VBOX_CONNECTION_NAME_SZ "VirtualBox Host-Only Network"
+#define VBOX_CONNECTION_NAME_WSZ RT_CONCAT(L,VBOX_CONNECTION_NAME_SZ)
+
+#define VBOXNETCFGWIN_NETLWF_ID L"oracle_VBoxNetLwf"
+
+
+
+/*********************************************************************************************************************************
+* Global Variables *
+*********************************************************************************************************************************/
+static PFNVBOXNETCFGLOGGER volatile g_pfnLogger = NULL;
+
+/*
+ * Wrappers for HelpAPI functions
+ */
+typedef void FNINITIALIZEIPINTERFACEENTRY( _Inout_ PMIB_IPINTERFACE_ROW row);
+typedef FNINITIALIZEIPINTERFACEENTRY *PFNINITIALIZEIPINTERFACEENTRY;
+
+typedef NETIOAPI_API FNGETIPINTERFACEENTRY( _Inout_ PMIB_IPINTERFACE_ROW row);
+typedef FNGETIPINTERFACEENTRY *PFNGETIPINTERFACEENTRY;
+
+typedef NETIOAPI_API FNSETIPINTERFACEENTRY( _Inout_ PMIB_IPINTERFACE_ROW row);
+typedef FNSETIPINTERFACEENTRY *PFNSETIPINTERFACEENTRY;
+
+
+/*********************************************************************************************************************************
+* Internal Functions *
+*********************************************************************************************************************************/
+static PFNINITIALIZEIPINTERFACEENTRY g_pfnInitializeIpInterfaceEntry = NULL;
+static PFNGETIPINTERFACEENTRY g_pfnGetIpInterfaceEntry = NULL;
+static PFNSETIPINTERFACEENTRY g_pfnSetIpInterfaceEntry = NULL;
+
+static void DoLogging(const char *pszString, ...);
+
+/*
+ * Forward declaration for using vboxNetCfgWinSetupMetric()
+ */
+static HRESULT vboxNetCfgWinSetupMetric(IN NET_LUID *pLuid);
+static HRESULT vboxNetCfgWinGetInterfaceLUID(IN HKEY hKey, OUT NET_LUID *pLUID);
+
+
+
+static HRESULT vboxNetCfgWinINetCfgLock(IN INetCfg *pNetCfg,
+ IN LPCWSTR pszwClientDescription,
+ IN DWORD cmsTimeout,
+ OUT LPWSTR *ppszwClientDescription)
+{
+ INetCfgLock *pLock;
+ HRESULT hr = pNetCfg->QueryInterface(IID_INetCfgLock, (PVOID *)&pLock);
+ if (FAILED(hr))
+ {
+ NonStandardLogFlow(("QueryInterface failed: %Rhrc\n", hr));
+ return hr;
+ }
+
+ hr = pLock->AcquireWriteLock(cmsTimeout, pszwClientDescription, ppszwClientDescription);
+ if (hr == S_FALSE)
+ NonStandardLogFlow(("Write lock busy\n"));
+ else if (FAILED(hr))
+ NonStandardLogFlow(("AcquireWriteLock failed: %Rhrc\n", hr));
+
+ pLock->Release();
+ return hr;
+}
+
+static HRESULT vboxNetCfgWinINetCfgUnlock(IN INetCfg *pNetCfg)
+{
+ INetCfgLock *pLock;
+ HRESULT hr = pNetCfg->QueryInterface(IID_INetCfgLock, (PVOID *)&pLock);
+ if (FAILED(hr))
+ {
+ NonStandardLogFlow(("QueryInterface failed: %Rhrc\n", hr));
+ return hr;
+ }
+
+ hr = pLock->ReleaseWriteLock();
+ if (FAILED(hr))
+ NonStandardLogFlow(("ReleaseWriteLock failed: %Rhrc\n", hr));
+
+ pLock->Release();
+ return hr;
+}
+
+VBOXNETCFGWIN_DECL(HRESULT) VBoxNetCfgWinQueryINetCfg(OUT INetCfg **ppNetCfg,
+ IN BOOL fGetWriteLock,
+ IN LPCWSTR pszwClientDescription,
+ IN DWORD cmsTimeout,
+ OUT LPWSTR *ppszwClientDescription)
+{
+ INetCfg *pNetCfg = NULL;
+ HRESULT hr = CoCreateInstance(CLSID_CNetCfg, NULL, CLSCTX_INPROC_SERVER, IID_INetCfg, (PVOID *)&pNetCfg);
+ if (FAILED(hr))
+ {
+ NonStandardLogFlow(("CoCreateInstance failed: %Rhrc\n", hr));
+ return hr;
+ }
+
+ if (fGetWriteLock)
+ {
+ hr = vboxNetCfgWinINetCfgLock(pNetCfg, pszwClientDescription, cmsTimeout, ppszwClientDescription);
+ if (hr == S_FALSE)
+ {
+ NonStandardLogFlow(("Write lock is busy\n", hr));
+ hr = NETCFG_E_NO_WRITE_LOCK;
+ }
+ }
+
+ if (SUCCEEDED(hr))
+ {
+ hr = pNetCfg->Initialize(NULL);
+ if (SUCCEEDED(hr))
+ {
+ *ppNetCfg = pNetCfg;
+ return S_OK;
+ }
+ NonStandardLogFlow(("Initialize failed: %Rhrc\n", hr));
+ }
+
+ pNetCfg->Release();
+ return hr;
+}
+
+VBOXNETCFGWIN_DECL(HRESULT) VBoxNetCfgWinReleaseINetCfg(IN INetCfg *pNetCfg, IN BOOL fHasWriteLock)
+{
+ if (!pNetCfg) /* If network config has been released already, just bail out. */
+ {
+ NonStandardLogFlow(("Warning: No network config given but write lock is set to TRUE\n"));
+ return S_OK;
+ }
+
+ HRESULT hr = pNetCfg->Uninitialize();
+ if (FAILED(hr))
+ {
+ NonStandardLogFlow(("Uninitialize failed: %Rhrc\n", hr));
+ /* Try to release the write lock below. */
+ }
+
+ if (fHasWriteLock)
+ {
+ HRESULT hr2 = vboxNetCfgWinINetCfgUnlock(pNetCfg);
+ if (FAILED(hr2))
+ NonStandardLogFlow(("vboxNetCfgWinINetCfgUnlock failed: %Rhrc\n", hr2));
+ if (SUCCEEDED(hr))
+ hr = hr2;
+ }
+
+ pNetCfg->Release();
+ return hr;
+}
+
+static HRESULT vboxNetCfgWinGetComponentByGuidEnum(IEnumNetCfgComponent *pEnumNcc,
+ IN const GUID *pGuid,
+ OUT INetCfgComponent **ppNcc)
+{
+ HRESULT hr = pEnumNcc->Reset();
+ if (FAILED(hr))
+ {
+ NonStandardLogFlow(("Reset failed: %Rhrc\n", hr));
+ return hr;
+ }
+
+ INetCfgComponent *pNcc = NULL;
+ while ((hr = pEnumNcc->Next(1, &pNcc, NULL)) == S_OK)
+ {
+ ULONG uComponentStatus = 0;
+ hr = pNcc->GetDeviceStatus(&uComponentStatus);
+ if (SUCCEEDED(hr))
+ {
+ if (uComponentStatus == 0)
+ {
+ GUID NccGuid;
+ hr = pNcc->GetInstanceGuid(&NccGuid);
+
+ if (SUCCEEDED(hr))
+ {
+ if (NccGuid == *pGuid)
+ {
+ /* found the needed device */
+ *ppNcc = pNcc;
+ break;
+ }
+ }
+ else
+ NonStandardLogFlow(("GetInstanceGuid failed: %Rhrc\n", hr));
+ }
+ }
+
+ pNcc->Release();
+ }
+ return hr;
+}
+
+VBOXNETCFGWIN_DECL(HRESULT) VBoxNetCfgWinGetComponentByGuid(IN INetCfg *pNc,
+ IN const GUID *pguidClass,
+ IN const GUID * pComponentGuid,
+ OUT INetCfgComponent **ppncc)
+{
+ IEnumNetCfgComponent *pEnumNcc = NULL;
+ HRESULT hr = pNc->EnumComponents(pguidClass, &pEnumNcc);
+ if (SUCCEEDED(hr))
+ {
+ hr = vboxNetCfgWinGetComponentByGuidEnum(pEnumNcc, pComponentGuid, ppncc);
+ if (hr == S_FALSE)
+ NonStandardLogFlow(("Component not found\n"));
+ else if (FAILED(hr))
+ NonStandardLogFlow(("vboxNetCfgWinGetComponentByGuidEnum failed: %Rhrc\n", hr));
+ pEnumNcc->Release();
+ }
+ else
+ NonStandardLogFlow(("EnumComponents failed: %Rhrc\n", hr));
+ return hr;
+}
+
+static HRESULT vboxNetCfgWinQueryInstaller(IN INetCfg *pNetCfg, IN const GUID *pguidClass, INetCfgClassSetup **ppSetup)
+{
+ HRESULT hr = pNetCfg->QueryNetCfgClass(pguidClass, IID_INetCfgClassSetup, (void **)ppSetup);
+ if (FAILED(hr))
+ NonStandardLogFlow(("QueryNetCfgClass failed: %Rhrc\n", hr));
+ return hr;
+}
+
+VBOXNETCFGWIN_DECL(HRESULT) VBoxNetCfgWinInstallComponent(IN INetCfg *pNetCfg, IN LPCWSTR pszwComponentId,
+ IN const GUID *pguidClass, OUT INetCfgComponent **ppComponent)
+{
+ INetCfgClassSetup *pSetup;
+ HRESULT hr = vboxNetCfgWinQueryInstaller(pNetCfg, pguidClass, &pSetup);
+ if (FAILED(hr))
+ {
+ NonStandardLogFlow(("vboxNetCfgWinQueryInstaller failed: %Rhrc\n", hr));
+ return hr;
+ }
+
+ OBO_TOKEN Token;
+ RT_ZERO(Token);
+ Token.Type = OBO_USER;
+
+ INetCfgComponent *pTempComponent = NULL;
+ hr = pSetup->Install(pszwComponentId, &Token,
+ 0, /* IN DWORD dwSetupFlags */
+ 0, /* IN DWORD dwUpgradeFromBuildNo */
+ NULL, /* IN LPCWSTR pszwAnswerFile */
+ NULL, /* IN LPCWSTR pszwAnswerSections */
+ &pTempComponent);
+ if (SUCCEEDED(hr))
+ {
+ if (pTempComponent != NULL)
+ {
+ /*
+ * Set default metric value of interface to fix multicast issue
+ * See @bugref{6379} for details.
+ */
+ HKEY hKey = (HKEY)INVALID_HANDLE_VALUE;
+ HRESULT hrc2 = pTempComponent->OpenParamKey(&hKey);
+
+ /* Set default metric value for host-only interface only */
+ if ( SUCCEEDED(hrc2)
+ && hKey != (HKEY)INVALID_HANDLE_VALUE
+ /* Original was weird: && wcsnicmp(pszwComponentId, VBOXNETCFGWIN_NETADP_ID_WSZ, 256) == 0) */
+ && RTUtf16ICmpAscii(pszwComponentId, VBOXNETCFGWIN_NETADP_ID_SZ) == 0)
+ {
+ NET_LUID luid;
+ hrc2 = vboxNetCfgWinGetInterfaceLUID(hKey, &luid);
+
+ /* Close the key as soon as possible. See @bugref{7973}. */
+ RegCloseKey(hKey);
+ hKey = (HKEY)INVALID_HANDLE_VALUE;
+
+ if (FAILED(hrc2))
+ {
+ /*
+ * The setting of Metric is not very important functionality,
+ * So we will not break installation process due to this error.
+ */
+ NonStandardLogFlow(("VBoxNetCfgWinInstallComponent Warning! vboxNetCfgWinGetInterfaceLUID failed, default metric for new interface will not be set: %Rhrc\n", hrc2));
+ }
+ else
+ {
+ hrc2 = vboxNetCfgWinSetupMetric(&luid);
+ if (FAILED(hrc2))
+ {
+ /*
+ * The setting of Metric is not very important functionality,
+ * So we will not break installation process due to this error.
+ */
+ NonStandardLogFlow(("VBoxNetCfgWinInstallComponent Warning! vboxNetCfgWinSetupMetric failed, default metric for new interface will not be set: %Rhrc\n", hrc2));
+ }
+ }
+ }
+ if (hKey != (HKEY)INVALID_HANDLE_VALUE)
+ RegCloseKey(hKey);
+ if (ppComponent != NULL)
+ *ppComponent = pTempComponent;
+ else
+ pTempComponent->Release();
+ }
+
+ /* ignore the apply failure */
+ HRESULT hrc3 = pNetCfg->Apply();
+ Assert(hrc3 == S_OK);
+ if (hrc3 != S_OK)
+ NonStandardLogFlow(("Apply failed: %Rhrc\n", hrc3));
+ }
+ else
+ NonStandardLogFlow(("Install failed: %Rhrc\n", hr));
+
+ pSetup->Release();
+ return hr;
+}
+
+static HRESULT vboxNetCfgWinInstallInfAndComponent(IN INetCfg *pNetCfg, IN LPCWSTR pszwComponentId, IN const GUID *pguidClass,
+ IN LPCWSTR const *apwszInfPaths, IN UINT cInfPaths,
+ OUT INetCfgComponent **ppComponent)
+{
+ NonStandardLogFlow(("Installing %u INF files ...\n", cInfPaths));
+
+ HRESULT hr = S_OK;
+ UINT cFilesProcessed = 0;
+ for (; cFilesProcessed < cInfPaths; cFilesProcessed++)
+ {
+ NonStandardLogFlow(("Installing INF file \"%ls\" ...\n", apwszInfPaths[cFilesProcessed]));
+ hr = VBoxDrvCfgInfInstall(apwszInfPaths[cFilesProcessed]);
+ if (FAILED(hr))
+ {
+ NonStandardLogFlow(("VBoxNetCfgWinInfInstall failed: %Rhrc\n", hr));
+ break;
+ }
+ }
+
+ if (SUCCEEDED(hr))
+ {
+ hr = VBoxNetCfgWinInstallComponent(pNetCfg, pszwComponentId, pguidClass, ppComponent);
+ if (FAILED(hr))
+ NonStandardLogFlow(("VBoxNetCfgWinInstallComponent failed: %Rhrc\n", hr));
+ }
+
+ if (FAILED(hr))
+ {
+ NonStandardLogFlow(("Installation failed, rolling back installation set ...\n"));
+
+ do
+ {
+ HRESULT hr2 = VBoxDrvCfgInfUninstall(apwszInfPaths[cFilesProcessed], 0);
+ if (FAILED(hr2))
+ NonStandardLogFlow(("VBoxDrvCfgInfUninstall failed: %Rhrc\n", hr2));
+ /* Keep going. */
+ if (!cFilesProcessed)
+ break;
+ } while (cFilesProcessed--);
+
+ NonStandardLogFlow(("Rollback complete\n"));
+ }
+
+ return hr;
+}
+
+VBOXNETCFGWIN_DECL(HRESULT) VBoxNetCfgWinUninstallComponent(IN INetCfg *pNetCfg, IN INetCfgComponent *pComponent)
+{
+ GUID GuidClass;
+ HRESULT hr = pComponent->GetClassGuid(&GuidClass);
+ if (FAILED(hr))
+ {
+ NonStandardLogFlow(("GetClassGuid failed: %Rhrc\n", hr));
+ return hr;
+ }
+
+ INetCfgClassSetup *pSetup = NULL;
+ hr = vboxNetCfgWinQueryInstaller(pNetCfg, &GuidClass, &pSetup);
+ if (FAILED(hr))
+ {
+ NonStandardLogFlow(("vboxNetCfgWinQueryInstaller failed: %Rhrc\n", hr));
+ return hr;
+ }
+
+ OBO_TOKEN Token;
+ RT_ZERO(Token);
+ Token.Type = OBO_USER;
+
+ hr = pSetup->DeInstall(pComponent, &Token, NULL /* OUT LPWSTR *pmszwRefs */);
+ if (SUCCEEDED(hr))
+ {
+ hr = pNetCfg->Apply();
+ if (FAILED(hr))
+ NonStandardLogFlow(("Apply failed: %Rhrc\n", hr));
+ }
+ else
+ NonStandardLogFlow(("DeInstall failed: %Rhrc\n", hr));
+
+ if (pSetup)
+ pSetup->Release();
+ return hr;
+}
+
+typedef BOOL (*PFN_VBOXNETCFGWIN_NETCFGENUM_CALLBACK_T)(IN INetCfg *pNetCfg, IN INetCfgComponent *pNetCfgComponent,
+ PVOID pvContext);
+
+static HRESULT vboxNetCfgWinEnumNetCfgComponents(IN INetCfg *pNetCfg,
+ IN const GUID *pguidClass,
+ PFN_VBOXNETCFGWIN_NETCFGENUM_CALLBACK_T pfnCallback,
+ PVOID pContext)
+{
+ IEnumNetCfgComponent *pEnumComponent = NULL;
+ HRESULT hr = pNetCfg->EnumComponents(pguidClass, &pEnumComponent);
+ if (SUCCEEDED(hr))
+ {
+ INetCfgComponent *pNetCfgComponent;
+ hr = pEnumComponent->Reset();
+ for (;;)
+ {
+ hr = pEnumComponent->Next(1, &pNetCfgComponent, NULL);
+ if (hr == S_OK)
+ {
+// ULONG uComponentStatus;
+// hr = pNcc->GetDeviceStatus(&uComponentStatus);
+// if (SUCCEEDED(hr))
+ BOOL fResult = FALSE;
+ if (pNetCfgComponent)
+ {
+ fResult = pfnCallback(pNetCfg, pNetCfgComponent, pContext);
+ pNetCfgComponent->Release();
+ }
+
+ if (!fResult)
+ break;
+ }
+ else
+ {
+ if (hr == S_FALSE)
+ hr = S_OK; /* no more components */
+ else
+ NonStandardLogFlow(("Next failed: %Rhrc\n", hr));
+ break;
+ }
+ }
+ pEnumComponent->Release();
+ }
+ return hr;
+}
+
+/** PFNVBOXNETCFGWINNETENUMCALLBACK */
+static BOOL vboxNetCfgWinRemoveAllNetDevicesOfIdCallback(HDEVINFO hDevInfo, PSP_DEVINFO_DATA pDev, PVOID pvContext)
+{
+ RT_NOREF1(pvContext);
+
+ SP_REMOVEDEVICE_PARAMS rmdParams;
+ RT_ZERO(rmdParams);
+ rmdParams.ClassInstallHeader.cbSize = sizeof(SP_CLASSINSTALL_HEADER);
+ rmdParams.ClassInstallHeader.InstallFunction = DIF_REMOVE;
+ rmdParams.Scope = DI_REMOVEDEVICE_GLOBAL;
+
+ if (SetupDiSetClassInstallParams(hDevInfo,pDev,
+ &rmdParams.ClassInstallHeader, sizeof(rmdParams)))
+ {
+ if (SetupDiSetSelectedDevice(hDevInfo, pDev))
+ {
+#ifndef VBOXNETCFG_DELAYEDRENAME
+ /* Figure out NetCfgInstanceId. */
+ HKEY hKey = SetupDiOpenDevRegKey(hDevInfo,
+ pDev,
+ DICS_FLAG_GLOBAL,
+ 0,
+ DIREG_DRV,
+ KEY_READ);
+ if (hKey == INVALID_HANDLE_VALUE)
+ NonStandardLogFlow(("vboxNetCfgWinRemoveAllNetDevicesOfIdCallback: SetupDiOpenDevRegKey failed with error %u\n",
+ GetLastError()));
+ else
+ {
+ WCHAR wszCfgGuidString[50] = { L'' };
+ DWORD cbSize = sizeof(wszCfgGuidString) - sizeof(WCHAR); /* make sure we get a terminated string back */
+ DWORD dwValueType = 0;
+ LSTATUS lrc = RegQueryValueExW(hKey, L"NetCfgInstanceId", NULL, &dwValueType, (LPBYTE)wszCfgGuidString, &cbSize);
+ if (lrc == ERROR_SUCCESS)
+ {
+ /** @todo r=bird: original didn't check the type here, just assumed it was a
+ * valid zero terminated string. (zero term handled by -sizeof(WHCAR) above now). */
+ if (dwValueType == REG_SZ || dwValueType == REG_EXPAND_SZ || dwValueType == REG_EXPAND_SZ)
+ {
+ NonStandardLogFlow(("vboxNetCfgWinRemoveAllNetDevicesOfIdCallback: Processing device ID \"%ls\"\n",
+ wszCfgGuidString));
+
+ /* Figure out device name. */
+ WCHAR wszDevName[256 + 1] = {0};
+ if (SetupDiGetDeviceRegistryPropertyW(hDevInfo, pDev, SPDRP_FRIENDLYNAME, NULL, (PBYTE)wszDevName,
+ sizeof(wszDevName) - sizeof(WCHAR) /* yes, in bytes */, NULL))
+ {
+ /*
+ * Rename the connection before removing the device. This will
+ * hopefully prevent an error when we will be attempting
+ * to rename a newly created connection (see @bugref{6740}).
+ */
+ WCHAR wszNewName[RT_ELEMENTS(wszDevName) + 128 /* ensure sufficient buffer */];
+ HRESULT hr = VBoxNetCfgWinGenHostonlyConnectionName(wszDevName, wszNewName,
+ RT_ELEMENTS(wszNewName) - 10 /*removed++*/,
+ NULL);
+ RTUtf16CatAscii(wszNewName, sizeof(wszNewName), " removed");
+ if (SUCCEEDED(hr))
+ hr = VBoxNetCfgWinRenameConnection(wszCfgGuidString, wszNewName);
+ //NonStandardLogFlow(("VBoxNetCfgWinRenameConnection(%S,%S) => 0x%x\n", wszCfgGuidString, TempName, hr_tmp));
+ }
+ else
+ NonStandardLogFlow(("vboxNetCfgWinRemoveAllNetDevicesOfIdCallback: Failed to get friendly name for device \"%ls\"\n",
+ wszCfgGuidString));
+ }
+ else
+ NonStandardLogFlow(("vboxNetCfgWinRemoveAllNetDevicesOfIdCallback: Friendly name for \"%S\" isn't a string: %d\n",
+ wszCfgGuidString, dwValueType
+ }
+ else
+ NonStandardLogFlow(("vboxNetCfgWinRemoveAllNetDevicesOfIdCallback: Querying instance ID failed with %u (%#x)\n",
+ lrc, lrc));
+
+ RegCloseKey(hKey);
+ }
+#endif /* VBOXNETCFG_DELAYEDRENAME */
+
+ if (SetupDiCallClassInstaller(DIF_REMOVE, hDevInfo, pDev))
+ {
+ SP_DEVINSTALL_PARAMS_W DevParams = { sizeof(DevParams) };
+ if (SetupDiGetDeviceInstallParams(hDevInfo, pDev, &DevParams))
+ {
+ if ( (DevParams.Flags & DI_NEEDRESTART)
+ || (DevParams.Flags & DI_NEEDREBOOT))
+ NonStandardLog(("vboxNetCfgWinRemoveAllNetDevicesOfIdCallback: A reboot is required\n"));
+ }
+ else
+ NonStandardLogFlow(("vboxNetCfgWinRemoveAllNetDevicesOfIdCallback: SetupDiGetDeviceInstallParams failed with %u\n",
+ GetLastError()));
+ }
+ else
+ NonStandardLogFlow(("vboxNetCfgWinRemoveAllNetDevicesOfIdCallback: SetupDiCallClassInstaller failed with %u\n",
+ GetLastError()));
+ }
+ else
+ NonStandardLogFlow(("vboxNetCfgWinRemoveAllNetDevicesOfIdCallback: SetupDiSetSelectedDevice failed with %u\n",
+ GetLastError()));
+ }
+ else
+ NonStandardLogFlow(("vboxNetCfgWinRemoveAllNetDevicesOfIdCallback: SetupDiSetClassInstallParams failed with %u\n",
+ GetLastError()));
+
+ /* Continue enumeration. */
+ return TRUE;
+}
+
+typedef struct VBOXNECTFGWINPROPCHANGE
+{
+ VBOXNECTFGWINPROPCHANGE_TYPE_T enmPcType;
+ HRESULT hr;
+} VBOXNECTFGWINPROPCHANGE, *PVBOXNECTFGWINPROPCHANGE;
+
+static BOOL vboxNetCfgWinPropChangeAllNetDevicesOfIdCallback(HDEVINFO hDevInfo, PSP_DEVINFO_DATA pDev, PVOID pContext)
+{
+ PVBOXNECTFGWINPROPCHANGE pPc = (PVBOXNECTFGWINPROPCHANGE)pContext;
+
+ SP_PROPCHANGE_PARAMS PcParams;
+ RT_ZERO(PcParams);
+ PcParams.ClassInstallHeader.cbSize = sizeof(SP_CLASSINSTALL_HEADER);
+ PcParams.ClassInstallHeader.InstallFunction = DIF_PROPERTYCHANGE;
+ PcParams.Scope = DICS_FLAG_GLOBAL;
+
+ switch (pPc->enmPcType)
+ {
+ case VBOXNECTFGWINPROPCHANGE_TYPE_DISABLE:
+ PcParams.StateChange = DICS_DISABLE;
+ NonStandardLogFlow(("vboxNetCfgWinPropChangeAllNetDevicesOfIdCallback: Change type (DICS_DISABLE): %d\n", pPc->enmPcType));
+ break;
+ case VBOXNECTFGWINPROPCHANGE_TYPE_ENABLE:
+ PcParams.StateChange = DICS_ENABLE;
+ NonStandardLogFlow(("vboxNetCfgWinPropChangeAllNetDevicesOfIdCallback: Change type (DICS_ENABLE): %d\n", pPc->enmPcType));
+ break;
+ default:
+ NonStandardLogFlow(("vboxNetCfgWinPropChangeAllNetDevicesOfIdCallback: Unexpected prop change type: %d\n", pPc->enmPcType));
+ pPc->hr = E_INVALIDARG;
+ return FALSE;
+ }
+
+ if (SetupDiSetClassInstallParamsW(hDevInfo, pDev, &PcParams.ClassInstallHeader, sizeof(PcParams)))
+ {
+ if (SetupDiSetSelectedDevice(hDevInfo, pDev))
+ {
+ if (SetupDiCallClassInstaller(DIF_PROPERTYCHANGE, hDevInfo, pDev))
+ {
+ SP_DEVINSTALL_PARAMS_W DevParams = { sizeof(DevParams) };
+ if (SetupDiGetDeviceInstallParamsW(hDevInfo, pDev, &DevParams))
+ {
+ if ( (DevParams.Flags & DI_NEEDRESTART)
+ || (DevParams.Flags & DI_NEEDREBOOT))
+ NonStandardLog(("vboxNetCfgWinPropChangeAllNetDevicesOfIdCallback: A reboot is required\n"));
+ }
+ else
+ NonStandardLogFlow(("vboxNetCfgWinPropChangeAllNetDevicesOfIdCallback: SetupDiGetDeviceInstallParams failed with %u\n",
+ GetLastError()));
+ }
+ else
+ NonStandardLogFlow(("vboxNetCfgWinPropChangeAllNetDevicesOfIdCallback: SetupDiCallClassInstaller failed with %u\n",
+ GetLastError()));
+ }
+ else
+ NonStandardLogFlow(("SetupDiSetSelectedDevice failed with %u\n", GetLastError()));
+ }
+ else
+ NonStandardLogFlow(("SetupDiSetClassInstallParams failed with %u\n", GetLastError()));
+
+ /* Continue enumeration. */
+ return TRUE;
+}
+
+typedef BOOL (*PFNVBOXNETCFGWINNETENUMCALLBACK)(HDEVINFO hDevInfo, PSP_DEVINFO_DATA pDev, PVOID pContext);
+
+static HRESULT vboxNetCfgWinEnumNetDevices(LPCWSTR pwszPnPId, PFNVBOXNETCFGWINNETENUMCALLBACK pfnCallback, PVOID pvContext)
+{
+ NonStandardLogFlow(("VBoxNetCfgWinEnumNetDevices: Searching for: %ls\n", pwszPnPId));
+
+ HRESULT hr;
+ HDEVINFO hDevInfo = SetupDiGetClassDevsExW(&GUID_DEVCLASS_NET,
+ NULL, /* IN PCTSTR Enumerator, OPTIONAL */
+ NULL, /* IN HWND hwndParent, OPTIONAL */
+ DIGCF_PRESENT, /* IN DWORD Flags,*/
+ NULL, /* IN HDEVINFO DeviceInfoSet, OPTIONAL */
+ NULL, /* IN PCTSTR MachineName, OPTIONAL */
+ NULL /* IN PVOID Reserved */);
+ if (hDevInfo != INVALID_HANDLE_VALUE)
+ {
+ size_t const cwcPnPId = RTUtf16Len(pwszPnPId);
+ DWORD winEr = NO_ERROR;
+ DWORD dwDevId = 0;
+ DWORD cbBuffer = 0;
+ PBYTE pbBuffer = NULL;
+ for (;;)
+ {
+ SP_DEVINFO_DATA Dev;
+ memset(&Dev, 0, sizeof(SP_DEVINFO_DATA));
+ Dev.cbSize = sizeof(SP_DEVINFO_DATA);
+
+ if (!SetupDiEnumDeviceInfo(hDevInfo, dwDevId, &Dev))
+ {
+ winEr = GetLastError();
+ if (winEr == ERROR_NO_MORE_ITEMS)
+ winEr = NO_ERROR;
+ break;
+ }
+
+ NonStandardLogFlow(("VBoxNetCfgWinEnumNetDevices: Enumerating device %u ... \n", dwDevId));
+ dwDevId++;
+
+ DWORD cbRequired = 0;
+ SetLastError(0);
+ if (!SetupDiGetDeviceRegistryPropertyW(hDevInfo, &Dev,
+ SPDRP_HARDWAREID, /* IN DWORD Property */
+ NULL, /* OUT PDWORD PropertyRegDataType OPTIONAL */
+ pbBuffer, /* OUT PBYTE PropertyBuffer */
+ cbBuffer, /* IN DWORD PropertyBufferSize */
+ &cbRequired /* OUT PDWORD RequiredSize OPTIONAL */))
+ {
+ winEr = GetLastError();
+ if (winEr != ERROR_INSUFFICIENT_BUFFER)
+ {
+ if (winEr == ERROR_INVALID_DATA)
+ {
+ NonStandardLogFlow(("VBoxNetCfgWinEnumNetDevices: SetupDiGetDeviceRegistryPropertyW (1) failed with ERROR_INVALID_DATA - ignoring, skipping to next device\n"));
+ continue;
+ }
+ NonStandardLogFlow(("VBoxNetCfgWinEnumNetDevices: SetupDiGetDeviceRegistryPropertyW (1) failed with %u\n", winEr));
+ break;
+ }
+ winEr = NO_ERROR;
+
+ cbBuffer = RT_ALIGN_32(cbRequired, 64);
+ void *pvNew = RTMemRealloc(pbBuffer, cbBuffer);
+ if (pvNew)
+ pbBuffer = (PBYTE)pvNew;
+ else
+ {
+ NonStandardLogFlow(("VBoxNetCfgWinEnumNetDevices: Out of memory allocating %u bytes\n", cbBuffer));
+ winEr = ERROR_OUTOFMEMORY;
+ break;
+ }
+
+ if (!SetupDiGetDeviceRegistryPropertyW(hDevInfo, &Dev,
+ SPDRP_HARDWAREID, /* IN DWORD Property */
+ NULL, /* OUT PDWORD PropertyRegDataType, OPTIONAL */
+ pbBuffer, /* OUT PBYTE PropertyBuffer */
+ cbBuffer, /* IN DWORD PropertyBufferSize */
+ &cbRequired /* OUT PDWORD RequiredSize OPTIONAL */))
+ {
+ winEr = GetLastError();
+ NonStandardLogFlow(("VBoxNetCfgWinEnumNetDevices: SetupDiGetDeviceRegistryPropertyW (2) failed with %u\n",
+ winEr));
+ break;
+ }
+ }
+
+ PWSTR pwszCurId = (PWSTR)pbBuffer;
+ size_t cwcCurId = RTUtf16Len(pwszCurId);
+
+ NonStandardLogFlow(("VBoxNetCfgWinEnumNetDevices: Device %u: %ls\n", dwDevId, pwszCurId));
+
+ if (cwcCurId >= cwcPnPId)
+ {
+ NonStandardLogFlow(("!RTUtf16NICmp(pwszCurId = (%ls), pwszPnPId = (%ls), cwcPnPId = (%d))\n", pwszCurId, pwszPnPId, cwcPnPId));
+
+ pwszCurId += cwcCurId - cwcPnPId;
+ if (!RTUtf16NICmp(pwszCurId, pwszPnPId, cwcPnPId))
+ {
+ if (!pfnCallback(hDevInfo, &Dev, pvContext))
+ break;
+ }
+ }
+ }
+
+ NonStandardLogFlow(("VBoxNetCfgWinEnumNetDevices: Found %u devices total\n", dwDevId));
+
+ if (pbBuffer)
+ RTMemFree(pbBuffer);
+
+ hr = HRESULT_FROM_WIN32(winEr);
+
+ SetupDiDestroyDeviceInfoList(hDevInfo);
+ }
+ else
+ {
+ DWORD winEr = GetLastError();
+ NonStandardLogFlow(("VBoxNetCfgWinEnumNetDevices: SetupDiGetClassDevsExW failed with %u\n", winEr));
+ hr = HRESULT_FROM_WIN32(winEr);
+ }
+
+ NonStandardLogFlow(("VBoxNetCfgWinEnumNetDevices: Ended with hr (0x%x)\n", hr));
+ return hr;
+}
+
+VBOXNETCFGWIN_DECL(HRESULT) VBoxNetCfgWinRemoveAllNetDevicesOfId(IN LPCWSTR pwszPnPId)
+{
+ return vboxNetCfgWinEnumNetDevices(pwszPnPId, vboxNetCfgWinRemoveAllNetDevicesOfIdCallback, NULL);
+}
+
+VBOXNETCFGWIN_DECL(HRESULT) VBoxNetCfgWinPropChangeAllNetDevicesOfId(IN LPCWSTR pwszPnPId, VBOXNECTFGWINPROPCHANGE_TYPE_T enmPcType)
+{
+ VBOXNECTFGWINPROPCHANGE Pc;
+ Pc.enmPcType = enmPcType;
+ Pc.hr = S_OK;
+ NonStandardLogFlow(("Calling VBoxNetCfgWinEnumNetDevices with pwszPnPId (= %ls) and vboxNetCfgWinPropChangeAllNetDevicesOfIdCallback\n", pwszPnPId));
+
+ HRESULT hr = vboxNetCfgWinEnumNetDevices(pwszPnPId, vboxNetCfgWinPropChangeAllNetDevicesOfIdCallback, &Pc);
+ if (!SUCCEEDED(hr))
+ {
+ NonStandardLogFlow(("VBoxNetCfgWinEnumNetDevices failed 0x%x\n", hr));
+ return hr;
+ }
+
+ if (!SUCCEEDED(Pc.hr))
+ {
+ NonStandardLogFlow(("vboxNetCfgWinPropChangeAllNetDevicesOfIdCallback failed 0x%x\n", Pc.hr));
+ return Pc.hr;
+ }
+
+ return S_OK;
+}
+
+
+
+/*********************************************************************************************************************************
+* Logging *
+*********************************************************************************************************************************/
+
+static void DoLogging(const char *pszString, ...)
+{
+ PFNVBOXNETCFGLOGGER pfnLogger = g_pfnLogger;
+ if (pfnLogger)
+ {
+ char szBuffer[4096];
+ va_list va;
+ va_start(va, pszString);
+ RTStrPrintfV(szBuffer, RT_ELEMENTS(szBuffer), pszString, va);
+ va_end(va);
+
+ pfnLogger(szBuffer);
+ }
+}
+
+VBOXNETCFGWIN_DECL(void) VBoxNetCfgWinSetLogging(IN PFNVBOXNETCFGLOGGER pfnLogger)
+{
+ g_pfnLogger = pfnLogger;
+}
+
+
+
+/*********************************************************************************************************************************
+* IP configuration API *
+*********************************************************************************************************************************/
+/* network settings config */
+#if 1 /** @todo r=bird: Can't we replace this with VBox/com/ptr.h? */
+/**
+ * Strong referencing operators. Used as a second argument to ComPtr<>/ComObjPtr<>.
+ */
+template<class C>
+class ComStrongRef
+{
+protected:
+
+ static void addref(C *p) { p->AddRef(); }
+ static void release(C *p) { p->Release(); }
+};
+
+
+/**
+ * Base template for smart COM pointers. Not intended to be used directly.
+ */
+template<class C, template<class> class RefOps = ComStrongRef>
+class ComPtrBase : protected RefOps<C>
+{
+public:
+
+ /* special template to disable AddRef()/Release() */
+ template<class I>
+ class NoAddRefRelease : public I
+ {
+ public:
+ virtual ~NoAddRefRelease() { /* Make VC++ 19.2 happy. */ }
+ private:
+#ifndef VBOX_WITH_XPCOM
+ STDMETHOD_(ULONG, AddRef)() = 0;
+ STDMETHOD_(ULONG, Release)() = 0;
+#else
+ NS_IMETHOD_(nsrefcnt) AddRef(void) = 0;
+ NS_IMETHOD_(nsrefcnt) Release(void) = 0;
+#endif
+ };
+
+protected:
+
+ ComPtrBase() : p(NULL) {}
+ ComPtrBase(const ComPtrBase &that) : p(that.p) { addref(); }
+ ComPtrBase(C *that_p) : p(that_p) { addref(); }
+
+ ~ComPtrBase() { release(); }
+
+ ComPtrBase &operator=(const ComPtrBase &that)
+ {
+ safe_assign(that.p);
+ return *this;
+ }
+
+ ComPtrBase &operator=(C *that_p)
+ {
+ safe_assign(that_p);
+ return *this;
+ }
+
+public:
+
+ void setNull()
+ {
+ release();
+ p = NULL;
+ }
+
+ bool isNull() const
+ {
+ return (p == NULL);
+ }
+
+ bool operator!() const { return isNull(); }
+
+ bool operator<(C* that_p) const { return p < that_p; }
+ bool operator==(C* that_p) const { return p == that_p; }
+
+ template<class I>
+ bool equalsTo(I *aThat) const
+ {
+ return ComPtrEquals(p, aThat);
+ }
+
+ template<class OC>
+ bool equalsTo(const ComPtrBase<OC> &oc) const
+ {
+ return equalsTo((OC *) oc);
+ }
+
+ /** Intended to pass instances as in parameters to interface methods */
+ operator C *() const { return p; }
+
+ /**
+ * Dereferences the instance (redirects the -> operator to the managed
+ * pointer).
+ */
+ NoAddRefRelease<C> *operator->() const
+ {
+ AssertMsg (p, ("Managed pointer must not be null\n"));
+ return (NoAddRefRelease <C> *) p;
+ }
+
+ template<class I>
+ HRESULT queryInterfaceTo(I **pp) const
+ {
+ if (pp)
+ {
+ if (p)
+ return p->QueryInterface(COM_IIDOF(I), (void **)pp);
+ *pp = NULL;
+ return S_OK;
+ }
+ return E_INVALIDARG;
+ }
+
+ /** Intended to pass instances as out parameters to interface methods */
+ C **asOutParam()
+ {
+ setNull();
+ return &p;
+ }
+
+private:
+
+ void addref()
+ {
+ if (p)
+ RefOps<C>::addref(p);
+ }
+
+ void release()
+ {
+ if (p)
+ RefOps<C>::release(p);
+ }
+
+ void safe_assign(C *that_p)
+ {
+ /* be aware of self-assignment */
+ if (that_p)
+ RefOps<C>::addref(that_p);
+ release();
+ p = that_p;
+ }
+
+ C *p;
+};
+
+/**
+ * Smart COM pointer wrapper that automatically manages refcounting of
+ * interface pointers.
+ *
+ * @param I COM interface class
+ */
+template<class I, template<class> class RefOps = ComStrongRef>
+class ComPtr : public ComPtrBase<I, RefOps>
+{
+ typedef ComPtrBase<I, RefOps> Base;
+
+public:
+
+ ComPtr() : Base() {}
+ ComPtr(const ComPtr &that) : Base(that) {}
+ ComPtr&operator= (const ComPtr &that)
+ {
+ Base::operator=(that);
+ return *this;
+ }
+
+ template<class OI>
+ ComPtr(OI *that_p) : Base () { operator=(that_p); }
+
+ /* specialization for I */
+ ComPtr(I *that_p) : Base (that_p) {}
+
+ template <class OC>
+ ComPtr(const ComPtr<OC, RefOps> &oc) : Base() { operator=((OC *) oc); }
+
+ template<class OI>
+ ComPtr &operator=(OI *that_p)
+ {
+ if (that_p)
+ that_p->QueryInterface(COM_IIDOF(I), (void **)Base::asOutParam());
+ else
+ Base::setNull();
+ return *this;
+ }
+
+ /* specialization for I */
+ ComPtr &operator=(I *that_p)
+ {
+ Base::operator=(that_p);
+ return *this;
+ }
+
+ template<class OC>
+ ComPtr &operator=(const ComPtr<OC, RefOps> &oc)
+ {
+ return operator=((OC *) oc);
+ }
+};
+#endif
+
+static HRESULT netIfWinFindAdapterClassById(IWbemServices *pSvc, const GUID *pGuid, IWbemClassObject **pAdapterConfig)
+{
+ HRESULT hr;
+
+ WCHAR wszGuid[50];
+ int cwcGuid = StringFromGUID2(*pGuid, wszGuid, RT_ELEMENTS(wszGuid));
+ if (cwcGuid)
+ {
+ com::BstrFmt bstrQuery("SELECT * FROM Win32_NetworkAdapterConfiguration WHERE SettingID = \"%ls\"", wszGuid);
+ IEnumWbemClassObject* pEnumerator = NULL;
+ hr = pSvc->ExecQuery(com::Bstr("WQL").raw(), bstrQuery.raw(), WBEM_FLAG_FORWARD_ONLY | WBEM_FLAG_RETURN_IMMEDIATELY,
+ NULL, &pEnumerator);
+ if (SUCCEEDED(hr))
+ {
+ if (pEnumerator)
+ {
+ IWbemClassObject *pclsObj = NULL;
+ ULONG uReturn = 0;
+ hr = pEnumerator->Next(WBEM_INFINITE, 1, &pclsObj, &uReturn);
+ NonStandardLogFlow(("netIfWinFindAdapterClassById: IEnumWbemClassObject::Next -> hr=0x%x pclsObj=%p uReturn=%u 42=%u\n",
+ hr, (void *)pclsObj, uReturn, 42));
+ if (SUCCEEDED(hr))
+ {
+ if (uReturn && pclsObj != NULL)
+ {
+ *pAdapterConfig = pclsObj;
+ pEnumerator->Release();
+ NonStandardLogFlow(("netIfWinFindAdapterClassById: S_OK and %p\n", *pAdapterConfig));
+ return S_OK;
+ }
+ hr = E_FAIL;
+ }
+ pEnumerator->Release();
+ }
+ else
+ {
+ NonStandardLogFlow(("ExecQuery returned no enumerator\n"));
+ hr = E_FAIL;
+ }
+ }
+ else
+ NonStandardLogFlow(("ExecQuery failed (0x%x)\n", hr));
+ }
+ else
+ {
+ DWORD winEr = GetLastError();
+ hr = HRESULT_FROM_WIN32(winEr);
+ if (SUCCEEDED(hr))
+ hr = E_FAIL;
+ NonStandardLogFlow(("StringFromGUID2 failed winEr=%u, hr=0x%x\n", winEr, hr));
+ }
+
+ NonStandardLogFlow(("netIfWinFindAdapterClassById: 0x%x and %p\n", hr, *pAdapterConfig));
+ return hr;
+}
+
+static HRESULT netIfWinIsHostOnly(IWbemClassObject *pAdapterConfig, BOOL *pfIsHostOnly)
+{
+ VARIANT vtServiceName;
+ VariantInit(&vtServiceName);
+
+ HRESULT hr = pAdapterConfig->Get(L"ServiceName", 0 /*lFlags*/, &vtServiceName, NULL /*pvtType*/, NULL /*plFlavor*/);
+ if (SUCCEEDED(hr))
+ {
+ *pfIsHostOnly = RTUtf16CmpAscii(vtServiceName.bstrVal, "VBoxNetAdp") == 0;
+
+ VariantClear(&vtServiceName);
+ }
+
+ return hr;
+}
+
+static HRESULT netIfWinGetIpSettings(IWbemClassObject * pAdapterConfig, ULONG *pIpv4, ULONG *pMaskv4)
+{
+ *pIpv4 = 0;
+ *pMaskv4 = 0;
+
+ VARIANT vtIp;
+ VariantInit(&vtIp);
+ HRESULT hr = pAdapterConfig->Get(L"IPAddress", 0, &vtIp, 0, 0);
+ if (SUCCEEDED(hr))
+ {
+ if (vtIp.vt == (VT_ARRAY | VT_BSTR))
+ {
+ VARIANT vtMask;
+ VariantInit(&vtMask);
+ hr = pAdapterConfig->Get(L"IPSubnet", 0, &vtMask, 0, 0);
+ if (SUCCEEDED(hr))
+ {
+ if (vtMask.vt == (VT_ARRAY | VT_BSTR))
+ {
+ SAFEARRAY *pIpArray = vtIp.parray;
+ SAFEARRAY *pMaskArray = vtMask.parray;
+ if (pIpArray && pMaskArray)
+ {
+ BSTR pBstrCurIp;
+ BSTR pBstrCurMask;
+ for (LONG i = 0;
+ SafeArrayGetElement(pIpArray, &i, (PVOID)&pBstrCurIp) == S_OK
+ && SafeArrayGetElement(pMaskArray, &i, (PVOID)&pBstrCurMask) == S_OK;
+ i++)
+ {
+ com::Utf8Str strIp(pBstrCurIp);
+ ULONG Ipv4 = inet_addr(strIp.c_str());
+ if (Ipv4 != INADDR_NONE)
+ {
+ *pIpv4 = Ipv4;
+
+ com::Utf8Str strMask(pBstrCurMask);
+ *pMaskv4 = inet_addr(strMask.c_str());
+ break;
+ }
+ }
+ }
+ }
+ VariantClear(&vtMask);
+ }
+ }
+ VariantClear(&vtIp);
+ }
+
+ return hr;
+}
+
+#if 0 /* unused */
+
+static HRESULT netIfWinHasIpSettings(IWbemClassObject * pAdapterConfig, SAFEARRAY * pCheckIp, SAFEARRAY * pCheckMask, bool *pFound)
+{
+ VARIANT vtIp;
+ HRESULT hr;
+ VariantInit(&vtIp);
+
+ *pFound = false;
+
+ hr = pAdapterConfig->Get(L"IPAddress", 0, &vtIp, 0, 0);
+ if (SUCCEEDED(hr))
+ {
+ VARIANT vtMask;
+ VariantInit(&vtMask);
+ hr = pAdapterConfig->Get(L"IPSubnet", 0, &vtMask, 0, 0);
+ if (SUCCEEDED(hr))
+ {
+ SAFEARRAY * pIpArray = vtIp.parray;
+ SAFEARRAY * pMaskArray = vtMask.parray;
+ if (pIpArray && pMaskArray)
+ {
+ BSTR pIp, pMask;
+ for (LONG k = 0;
+ SafeArrayGetElement(pCheckIp, &k, (PVOID)&pIp) == S_OK
+ && SafeArrayGetElement(pCheckMask, &k, (PVOID)&pMask) == S_OK;
+ k++)
+ {
+ BSTR pCurIp;
+ BSTR pCurMask;
+ for (LONG i = 0;
+ SafeArrayGetElement(pIpArray, &i, (PVOID)&pCurIp) == S_OK
+ && SafeArrayGetElement(pMaskArray, &i, (PVOID)&pCurMask) == S_OK;
+ i++)
+ {
+ if (!wcsicmp(pCurIp, pIp))
+ {
+ if (!wcsicmp(pCurMask, pMask))
+ *pFound = true;
+ break;
+ }
+ }
+ }
+ }
+
+
+ VariantClear(&vtMask);
+ }
+
+ VariantClear(&vtIp);
+ }
+
+ return hr;
+}
+
+static HRESULT netIfWinWaitIpSettings(IWbemServices *pSvc, const GUID * pGuid, SAFEARRAY * pCheckIp, SAFEARRAY * pCheckMask, ULONG sec2Wait, bool *pFound)
+{
+ /* on Vista we need to wait for the address to get applied */
+ /* wait for the address to appear in the list */
+ HRESULT hr = S_OK;
+ ULONG i;
+ *pFound = false;
+ ComPtr<IWbemClassObject> pAdapterConfig;
+ for (i = 0;
+ (hr = netIfWinFindAdapterClassById(pSvc, pGuid, pAdapterConfig.asOutParam())) == S_OK
+ && (hr = netIfWinHasIpSettings(pAdapterConfig, pCheckIp, pCheckMask, pFound)) == S_OK
+ && !(*pFound)
+ && i < sec2Wait/6;
+ i++)
+ {
+ Sleep(6000);
+ }
+
+ return hr;
+}
+
+#endif /* unused */
+
+static HRESULT netIfWinCreateIWbemServices(IWbemServices **ppSvc)
+{
+ IWbemLocator *pLoc = NULL;
+ HRESULT hr = CoCreateInstance(CLSID_WbemLocator, 0, CLSCTX_INPROC_SERVER, IID_IWbemLocator, (LPVOID *)&pLoc);
+ if (SUCCEEDED(hr))
+ {
+ IWbemServices *pSvc = NULL;
+ hr = pLoc->ConnectServer(com::Bstr(L"ROOT\\CIMV2").raw(), /* [in] const BSTR strNetworkResource */
+ NULL, /* [in] const BSTR strUser */
+ NULL, /* [in] const BSTR strPassword */
+ 0, /* [in] const BSTR strLocale */
+ NULL, /* [in] LONG lSecurityFlags */
+ 0, /* [in] const BSTR strAuthority */
+ 0, /* [in] IWbemContext* pCtx */
+ &pSvc /* [out] IWbemServices** ppNamespace */);
+ if (SUCCEEDED(hr))
+ {
+ hr = CoSetProxyBlanket(pSvc, /* IUnknown * pProxy */
+ RPC_C_AUTHN_WINNT, /* DWORD dwAuthnSvc */
+ RPC_C_AUTHZ_NONE, /* DWORD dwAuthzSvc */
+ NULL, /* WCHAR * pServerPrincName */
+ RPC_C_AUTHN_LEVEL_CALL, /* DWORD dwAuthnLevel */
+ RPC_C_IMP_LEVEL_IMPERSONATE, /* DWORD dwImpLevel */
+ NULL, /* RPC_AUTH_IDENTITY_HANDLE pAuthInfo */
+ EOAC_NONE /* DWORD dwCapabilities */
+ );
+ if (SUCCEEDED(hr))
+ {
+ *ppSvc = pSvc;
+ /* do not need it any more */
+ pLoc->Release();
+ return hr;
+ }
+
+ NonStandardLogFlow(("CoSetProxyBlanket failed: %Rhrc\n", hr));
+ pSvc->Release();
+ }
+ else
+ NonStandardLogFlow(("ConnectServer failed: %Rhrc\n", hr));
+ pLoc->Release();
+ }
+ else
+ NonStandardLogFlow(("CoCreateInstance failed: %Rhrc\n", hr));
+ return hr;
+}
+
+static HRESULT netIfWinAdapterConfigPath(IWbemClassObject *pObj, com::Bstr *pRet)
+{
+ VARIANT index;
+ VariantInit(&index);
+ HRESULT hr = pObj->Get(L"Index", 0, &index, 0, 0);
+ if (SUCCEEDED(hr))
+ hr = pRet->printfNoThrow("Win32_NetworkAdapterConfiguration.Index='%u'", index.uintVal);
+ else
+ {
+ pRet->setNull();
+ NonStandardLogFlow(("Get failed: %Rhrc\n", hr));
+ }
+ return hr;
+}
+
+static HRESULT netIfExecMethod(IWbemServices * pSvc, IWbemClassObject *pClass, com::Bstr const &rObjPath,
+ const char *pszMethodName, LPWSTR *papwszArgNames, LPVARIANT *pArgs, UINT cArgs,
+ IWbemClassObject **ppOutParams)
+{
+ *ppOutParams = NULL;
+ com::Bstr bstrMethodName;
+ HRESULT hr = bstrMethodName.assignEx(pszMethodName);
+ if (SUCCEEDED(hr))
+ {
+ ComPtr<IWbemClassObject> pInParamsDefinition;
+ ComPtr<IWbemClassObject> pClassInstance;
+ if (cArgs)
+ {
+ hr = pClass->GetMethod(bstrMethodName.raw(), 0, pInParamsDefinition.asOutParam(), NULL);
+ if (SUCCEEDED(hr))
+ {
+ hr = pInParamsDefinition->SpawnInstance(0, pClassInstance.asOutParam());
+ if (SUCCEEDED(hr))
+ {
+ for (UINT i = 0; i < cArgs; i++)
+ {
+ hr = pClassInstance->Put(papwszArgNames[i], 0, pArgs[i], 0);
+ if (FAILED(hr))
+ break;
+ }
+ }
+ }
+ }
+
+ if (SUCCEEDED(hr))
+ {
+ IWbemClassObject *pOutParams = NULL;
+ hr = pSvc->ExecMethod(rObjPath.raw(), bstrMethodName.raw(), 0, NULL, pClassInstance, &pOutParams, NULL);
+ if (SUCCEEDED(hr))
+ *ppOutParams = pOutParams;
+ }
+ }
+
+ return hr;
+}
+
+static HRESULT netIfWinCreateIpArray(SAFEARRAY **ppArray, in_addr const *paIps, UINT cIps)
+{
+ HRESULT hr = S_OK;
+ SAFEARRAY *pIpArray = SafeArrayCreateVector(VT_BSTR, 0, cIps);
+ if (pIpArray)
+ {
+ for (UINT i = 0; i < cIps; i++)
+ {
+ com::Bstr bstrVal;
+ hr = bstrVal.printfNoThrow("%RTnaipv4", paIps[i].s_addr);
+ if (SUCCEEDED(hr))
+ {
+ Assert(bstrVal.equals(inet_ntoa(paIps[i])));
+
+ BSTR pRawVal;
+ hr = bstrVal.detachToEx(&pRawVal);
+ if (SUCCEEDED(hr))
+ {
+ LONG aIndex[1] = { (LONG)i };
+ hr = SafeArrayPutElement(pIpArray, aIndex, pRawVal);
+ if (SUCCEEDED(hr))
+ continue;
+ SysFreeString(pRawVal);
+ }
+ }
+ break;
+ }
+
+ if (SUCCEEDED(hr))
+ *ppArray = pIpArray;
+ else
+ SafeArrayDestroy(pIpArray);
+ }
+ else
+ hr = HRESULT_FROM_WIN32(GetLastError());
+ return hr;
+}
+
+#if 0 /* unused */
+static HRESULT netIfWinCreateIpArrayV4V6(SAFEARRAY **ppArray, BSTR Ip)
+{
+ HRESULT hr;
+ SAFEARRAY *pIpArray = SafeArrayCreateVector(VT_BSTR, 0, 1);
+ if (pIpArray)
+ {
+ BSTR val = com::Bstr(Ip, false).copy();
+ long aIndex[1];
+ aIndex[0] = 0;
+ hr = SafeArrayPutElement(pIpArray, aIndex, val);
+ if (FAILED(hr))
+ {
+ SysFreeString(val);
+ SafeArrayDestroy(pIpArray);
+ }
+
+ if (SUCCEEDED(hr))
+ {
+ *ppArray = pIpArray;
+ }
+ }
+ else
+ hr = HRESULT_FROM_WIN32(GetLastError());
+
+ return hr;
+}
+#endif
+
+
+static HRESULT netIfWinCreateIpArrayVariantV4(VARIANT *pIpAddresses, in_addr const *paIps, UINT cIps)
+{
+ VariantInit(pIpAddresses);
+ pIpAddresses->vt = VT_ARRAY | VT_BSTR;
+
+ SAFEARRAY *pIpArray;
+ HRESULT hr = netIfWinCreateIpArray(&pIpArray, paIps, cIps);
+ if (SUCCEEDED(hr))
+ pIpAddresses->parray = pIpArray;
+ return hr;
+}
+
+#if 0 /* unused */
+static HRESULT netIfWinCreateIpArrayVariantV4V6(VARIANT * pIpAddresses, BSTR Ip)
+{
+ HRESULT hr;
+ VariantInit(pIpAddresses);
+ pIpAddresses->vt = VT_ARRAY | VT_BSTR;
+ SAFEARRAY *pIpArray;
+ hr = netIfWinCreateIpArrayV4V6(&pIpArray, Ip);
+ if (SUCCEEDED(hr))
+ {
+ pIpAddresses->parray = pIpArray;
+ }
+ return hr;
+}
+#endif
+
+static HRESULT netIfWinEnableStatic(IWbemServices *pSvc, const GUID *pGuid, com::Bstr &rObjPath, VARIANT *pIp, VARIANT *pMask)
+{
+ com::Bstr bstrClassName;
+ HRESULT hr = bstrClassName.assignEx("Win32_NetworkAdapterConfiguration");
+ if (SUCCEEDED(hr))
+ {
+ ComPtr<IWbemClassObject> pClass;
+ hr = pSvc->GetObject(bstrClassName.raw(), 0, NULL, pClass.asOutParam(), NULL);
+ if (SUCCEEDED(hr))
+ {
+ LPWSTR argNames[] = {L"IPAddress", L"SubnetMask"};
+ LPVARIANT args[] = { pIp, pMask };
+
+ ComPtr<IWbemClassObject> pOutParams;
+ hr = netIfExecMethod(pSvc, pClass, rObjPath.raw(), "EnableStatic", argNames, args,
+ 2, pOutParams.asOutParam());
+ if (SUCCEEDED(hr))
+ {
+ com::Bstr bstrReturnValue;
+ hr = bstrReturnValue.assignEx("ReturnValue");
+ if (SUCCEEDED(hr))
+ {
+ VARIANT varReturnValue;
+ VariantInit(&varReturnValue);
+ hr = pOutParams->Get(bstrReturnValue.raw(), 0, &varReturnValue, NULL, 0);
+ Assert(SUCCEEDED(hr));
+ if (SUCCEEDED(hr))
+ {
+ //Assert(varReturnValue.vt == VT_UINT);
+ int winEr = varReturnValue.uintVal;
+ switch (winEr)
+ {
+ case 0:
+ {
+ hr = S_OK;
+ //bool bFound;
+ //HRESULT tmpHr = netIfWinWaitIpSettings(pSvc, pGuid, pIp->parray, pMask->parray, 180, &bFound);
+ NOREF(pGuid);
+ break;
+ }
+ default:
+ hr = HRESULT_FROM_WIN32( winEr );
+ break;
+ }
+ }
+ }
+ }
+ }
+ }
+ return hr;
+}
+
+
+static HRESULT netIfWinEnableStaticV4(IWbemServices *pSvc, const GUID *pGuid, com::Bstr &rObjPath,
+ in_addr const *paIps, in_addr const *paMasks, UINT cIpAndMasks)
+{
+ VARIANT ipAddresses;
+ HRESULT hr = netIfWinCreateIpArrayVariantV4(&ipAddresses, paIps, cIpAndMasks);
+ if (SUCCEEDED(hr))
+ {
+ VARIANT ipMasks;
+ hr = netIfWinCreateIpArrayVariantV4(&ipMasks, paMasks, cIpAndMasks);
+ if (SUCCEEDED(hr))
+ {
+ hr = netIfWinEnableStatic(pSvc, pGuid, rObjPath, &ipAddresses, &ipMasks);
+ VariantClear(&ipMasks);
+ }
+ VariantClear(&ipAddresses);
+ }
+ return hr;
+}
+
+#if 0 /* unused */
+
+static HRESULT netIfWinEnableStaticV4V6(IWbemServices * pSvc, const GUID * pGuid, BSTR ObjPath, BSTR Ip, BSTR Mask)
+{
+ VARIANT ipAddresses;
+ HRESULT hr = netIfWinCreateIpArrayVariantV4V6(&ipAddresses, Ip);
+ if (SUCCEEDED(hr))
+ {
+ VARIANT ipMasks;
+ hr = netIfWinCreateIpArrayVariantV4V6(&ipMasks, Mask);
+ if (SUCCEEDED(hr))
+ {
+ hr = netIfWinEnableStatic(pSvc, pGuid, ObjPath, &ipAddresses, &ipMasks);
+ VariantClear(&ipMasks);
+ }
+ VariantClear(&ipAddresses);
+ }
+ return hr;
+}
+
+/* win API allows to set gw metrics as well, we are not setting them */
+static HRESULT netIfWinSetGateways(IWbemServices * pSvc, BSTR ObjPath, VARIANT * pGw)
+{
+ ComPtr<IWbemClassObject> pClass;
+ BSTR ClassName = SysAllocString(L"Win32_NetworkAdapterConfiguration");
+ HRESULT hr;
+ if (ClassName)
+ {
+ hr = pSvc->GetObject(ClassName, 0, NULL, pClass.asOutParam(), NULL);
+ if (SUCCEEDED(hr))
+ {
+ LPWSTR argNames[] = {L"DefaultIPGateway"};
+ LPVARIANT args[] = {pGw};
+ ComPtr<IWbemClassObject> pOutParams;
+
+ hr = netIfExecMethod(pSvc, pClass, ObjPath, com::Bstr(L"SetGateways"), argNames, args, 1, pOutParams.asOutParam());
+ if (SUCCEEDED(hr))
+ {
+ VARIANT varReturnValue;
+ hr = pOutParams->Get(com::Bstr(L"ReturnValue"), 0, &varReturnValue, NULL, 0);
+ Assert(SUCCEEDED(hr));
+ if (SUCCEEDED(hr))
+ {
+// Assert(varReturnValue.vt == VT_UINT);
+ int winEr = varReturnValue.uintVal;
+ switch (winEr)
+ {
+ case 0:
+ hr = S_OK;
+ break;
+ default:
+ hr = HRESULT_FROM_WIN32( winEr );
+ break;
+ }
+ }
+ }
+ }
+ SysFreeString(ClassName);
+ }
+ else
+ hr = HRESULT_FROM_WIN32(GetLastError());
+
+ return hr;
+}
+
+/* win API allows to set gw metrics as well, we are not setting them */
+static HRESULT netIfWinSetGatewaysV4(IWbemServices * pSvc, BSTR ObjPath, in_addr* aGw, UINT cGw)
+{
+ VARIANT gwais;
+ HRESULT hr = netIfWinCreateIpArrayVariantV4(&gwais, aGw, cGw);
+ if (SUCCEEDED(hr))
+ {
+ netIfWinSetGateways(pSvc, ObjPath, &gwais);
+ VariantClear(&gwais);
+ }
+ return hr;
+}
+
+/* win API allows to set gw metrics as well, we are not setting them */
+static HRESULT netIfWinSetGatewaysV4V6(IWbemServices * pSvc, BSTR ObjPath, BSTR Gw)
+{
+ VARIANT vGw;
+ HRESULT hr = netIfWinCreateIpArrayVariantV4V6(&vGw, Gw);
+ if (SUCCEEDED(hr))
+ {
+ netIfWinSetGateways(pSvc, ObjPath, &vGw);
+ VariantClear(&vGw);
+ }
+ return hr;
+}
+
+#endif /* unused */
+
+static HRESULT netIfWinEnableDHCP(IWbemServices * pSvc, const com::Bstr &rObjPath)
+{
+ com::Bstr bstrClassName;
+ HRESULT hr = bstrClassName.assignEx("Win32_NetworkAdapterConfiguration");
+ if (SUCCEEDED(hr))
+ {
+ ComPtr<IWbemClassObject> pClass;
+ hr = pSvc->GetObject(bstrClassName.raw(), 0, NULL, pClass.asOutParam(), NULL);
+ if (SUCCEEDED(hr))
+ {
+ ComPtr<IWbemClassObject> pOutParams;
+ hr = netIfExecMethod(pSvc, pClass, rObjPath, "EnableDHCP", NULL, NULL, 0, pOutParams.asOutParam());
+ if (SUCCEEDED(hr))
+ {
+ com::Bstr bstrReturnValue;
+ hr = bstrReturnValue.assignEx("ReturnValue");
+ if (SUCCEEDED(hr))
+ {
+ VARIANT varReturnValue;
+ VariantInit(&varReturnValue);
+ hr = pOutParams->Get(bstrReturnValue.raw(), 0, &varReturnValue, NULL, 0);
+ Assert(SUCCEEDED(hr));
+ if (SUCCEEDED(hr))
+ {
+ //Assert(varReturnValue.vt == VT_UINT);
+ int winEr = varReturnValue.uintVal;
+ switch (winEr)
+ {
+ case 0:
+ hr = S_OK;
+ break;
+ default:
+ hr = HRESULT_FROM_WIN32( winEr );
+ break;
+ }
+ }
+ }
+ }
+ }
+ }
+ return hr;
+}
+
+static HRESULT netIfWinDhcpRediscover(IWbemServices * pSvc, const com::Bstr &rObjPath)
+{
+ com::Bstr bstrClassName;
+ HRESULT hr = bstrClassName.assignEx("Win32_NetworkAdapterConfiguration");
+ if (SUCCEEDED(hr))
+ {
+ ComPtr<IWbemClassObject> pClass;
+ hr = pSvc->GetObject(bstrClassName.raw(), 0, NULL, pClass.asOutParam(), NULL);
+ if (SUCCEEDED(hr))
+ {
+ ComPtr<IWbemClassObject> pOutParams;
+ hr = netIfExecMethod(pSvc, pClass, rObjPath, "ReleaseDHCPLease", NULL, NULL, 0, pOutParams.asOutParam());
+ if (SUCCEEDED(hr))
+ {
+ com::Bstr bstrReturnValue;
+ hr = bstrReturnValue.assignEx("ReturnValue");
+ if (SUCCEEDED(hr))
+ {
+ VARIANT varReturnValue;
+ VariantInit(&varReturnValue);
+ hr = pOutParams->Get(bstrReturnValue.raw(), 0, &varReturnValue, NULL, 0);
+ Assert(SUCCEEDED(hr));
+ if (SUCCEEDED(hr))
+ {
+ //Assert(varReturnValue.vt == VT_UINT);
+ int winEr = varReturnValue.uintVal;
+ if (winEr == 0)
+ {
+ hr = netIfExecMethod(pSvc, pClass, rObjPath, "RenewDHCPLease", NULL, NULL, 0, pOutParams.asOutParam());
+ if (SUCCEEDED(hr))
+ {
+ hr = pOutParams->Get(bstrReturnValue.raw(), 0, &varReturnValue, NULL, 0);
+ Assert(SUCCEEDED(hr));
+ if (SUCCEEDED(hr))
+ {
+ //Assert(varReturnValue.vt == VT_UINT);
+ winEr = varReturnValue.uintVal;
+ if (winEr == 0)
+ hr = S_OK;
+ else
+ hr = HRESULT_FROM_WIN32( winEr );
+ }
+ }
+ }
+ else
+ hr = HRESULT_FROM_WIN32( winEr );
+ }
+ }
+ }
+ }
+ }
+
+ return hr;
+}
+
+static HRESULT vboxNetCfgWinIsDhcpEnabled(IWbemClassObject *pAdapterConfig, BOOL *pfEnabled)
+{
+ VARIANT vtEnabled;
+ VariantInit(&vtEnabled);
+ HRESULT hr = pAdapterConfig->Get(L"DHCPEnabled", 0, &vtEnabled, 0, 0);
+ if (SUCCEEDED(hr))
+ *pfEnabled = vtEnabled.boolVal;
+ else
+ *pfEnabled = FALSE;
+ return hr;
+}
+
+VBOXNETCFGWIN_DECL(HRESULT) VBoxNetCfgWinGetAdapterSettings(IN const GUID *pGuid, OUT PADAPTER_SETTINGS pSettings)
+{
+ ComPtr<IWbemServices> pSvc;
+ HRESULT hr = netIfWinCreateIWbemServices(pSvc.asOutParam());
+ if (SUCCEEDED(hr))
+ {
+ ComPtr<IWbemClassObject> pAdapterConfig;
+ hr = netIfWinFindAdapterClassById(pSvc, pGuid, pAdapterConfig.asOutParam());
+ if (SUCCEEDED(hr))
+ {
+ hr = vboxNetCfgWinIsDhcpEnabled(pAdapterConfig, &pSettings->bDhcp);
+ if (SUCCEEDED(hr))
+ hr = netIfWinGetIpSettings(pAdapterConfig, &pSettings->ip, &pSettings->mask);
+ }
+ }
+
+ return hr;
+}
+
+VBOXNETCFGWIN_DECL(HRESULT) VBoxNetCfgWinIsDhcpEnabled(const GUID * pGuid, BOOL *pEnabled)
+{
+ ComPtr<IWbemServices> pSvc;
+ HRESULT hr = netIfWinCreateIWbemServices(pSvc.asOutParam());
+ if (SUCCEEDED(hr))
+ {
+ ComPtr<IWbemClassObject> pAdapterConfig;
+ hr = netIfWinFindAdapterClassById(pSvc, pGuid, pAdapterConfig.asOutParam());
+ if (SUCCEEDED(hr))
+ {
+ VARIANT vtEnabled;
+ hr = pAdapterConfig->Get(L"DHCPEnabled", 0, &vtEnabled, 0, 0);
+ if (SUCCEEDED(hr))
+ *pEnabled = vtEnabled.boolVal;
+ }
+ }
+
+ return hr;
+}
+
+VBOXNETCFGWIN_DECL(HRESULT) VBoxNetCfgWinEnableStaticIpConfig(IN const GUID *pGuid, IN ULONG ip, IN ULONG mask)
+{
+ NonStandardLogFlow(("VBoxNetCfgWinEnableStaticIpConfig: ip=0x%x mask=0x%x\n", ip, mask));
+ ComPtr<IWbemServices> pSvc;
+ HRESULT hr = netIfWinCreateIWbemServices(pSvc.asOutParam());
+ if (SUCCEEDED(hr))
+ {
+ ComPtr<IWbemClassObject> pAdapterConfig;
+ hr = netIfWinFindAdapterClassById(pSvc, pGuid, pAdapterConfig.asOutParam());
+ if (SUCCEEDED(hr))
+ {
+ BOOL fIsHostOnly;
+ hr = netIfWinIsHostOnly(pAdapterConfig, &fIsHostOnly);
+ if (SUCCEEDED(hr))
+ {
+ if (fIsHostOnly)
+ {
+ in_addr aIp[1];
+ in_addr aMask[1];
+ aIp[0].S_un.S_addr = ip;
+ aMask[0].S_un.S_addr = mask;
+
+ com::Bstr bstrObjPath;
+ hr = netIfWinAdapterConfigPath(pAdapterConfig, &bstrObjPath);
+ if (SUCCEEDED(hr))
+ {
+ hr = netIfWinEnableStaticV4(pSvc, pGuid, bstrObjPath, aIp, aMask, ip != 0 ? 1 : 0);
+ if (SUCCEEDED(hr))
+ {
+#if 0
+ in_addr aGw[1];
+ aGw[0].S_un.S_addr = gw;
+ hr = netIfWinSetGatewaysV4(pSvc, bstrObjPath, aGw, 1);
+ if (SUCCEEDED(hr))
+#endif
+ {
+ }
+ }
+ }
+ }
+ else
+ {
+ hr = E_FAIL;
+ }
+ }
+ }
+ }
+
+ NonStandardLogFlow(("VBoxNetCfgWinEnableStaticIpConfig: returns %Rhrc\n", hr));
+ return hr;
+}
+
+#if 0
+static HRESULT netIfEnableStaticIpConfigV6(const GUID *pGuid, IN_BSTR aIPV6Address, IN_BSTR aIPV6Mask, IN_BSTR aIPV6DefaultGateway)
+{
+ HRESULT hr;
+ ComPtr<IWbemServices> pSvc;
+ hr = netIfWinCreateIWbemServices(pSvc.asOutParam());
+ if (SUCCEEDED(hr))
+ {
+ ComPtr<IWbemClassObject> pAdapterConfig;
+ hr = netIfWinFindAdapterClassById(pSvc, pGuid, pAdapterConfig.asOutParam());
+ if (SUCCEEDED(hr))
+ {
+ BSTR ObjPath;
+ hr = netIfWinAdapterConfigPath(pAdapterConfig, &ObjPath);
+ if (SUCCEEDED(hr))
+ {
+ hr = netIfWinEnableStaticV4V6(pSvc, pAdapterConfig, ObjPath, aIPV6Address, aIPV6Mask);
+ if (SUCCEEDED(hr))
+ {
+ if (aIPV6DefaultGateway)
+ {
+ hr = netIfWinSetGatewaysV4V6(pSvc, ObjPath, aIPV6DefaultGateway);
+ }
+ if (SUCCEEDED(hr))
+ {
+// hr = netIfWinUpdateConfig(pIf);
+ }
+ }
+ SysFreeString(ObjPath);
+ }
+ }
+ }
+
+ return SUCCEEDED(hr) ? VINF_SUCCESS : VERR_GENERAL_FAILURE;
+}
+
+static HRESULT netIfEnableStaticIpConfigV6(const GUID *pGuid, IN_BSTR aIPV6Address, ULONG aIPV6MaskPrefixLength)
+{
+ RTNETADDRIPV6 Mask;
+ int rc = RTNetPrefixToMaskIPv6(aIPV6MaskPrefixLength, &Mask);
+ if (RT_SUCCESS(rc))
+ {
+ Bstr maskStr = composeIPv6Address(&Mask);
+ rc = netIfEnableStaticIpConfigV6(pGuid, aIPV6Address, maskStr, NULL);
+ }
+ return rc;
+}
+#endif
+
+VBOXNETCFGWIN_DECL(HRESULT) VBoxNetCfgWinEnableDynamicIpConfig(IN const GUID *pGuid)
+{
+ ComPtr<IWbemServices> pSvc;
+ HRESULT hr = netIfWinCreateIWbemServices(pSvc.asOutParam());
+ if (SUCCEEDED(hr))
+ {
+ ComPtr<IWbemClassObject> pAdapterConfig;
+ hr = netIfWinFindAdapterClassById(pSvc, pGuid, pAdapterConfig.asOutParam());
+ if (SUCCEEDED(hr))
+ {
+ BOOL fIsHostOnly;
+ hr = netIfWinIsHostOnly(pAdapterConfig, &fIsHostOnly);
+ if (SUCCEEDED(hr))
+ {
+ if (fIsHostOnly)
+ {
+ com::Bstr bstrObjPath;
+ hr = netIfWinAdapterConfigPath(pAdapterConfig, &bstrObjPath);
+ if (SUCCEEDED(hr))
+ {
+ hr = netIfWinEnableDHCP(pSvc, bstrObjPath);
+ if (SUCCEEDED(hr))
+ {
+ //hr = netIfWinUpdateConfig(pIf);
+ }
+ }
+ }
+ else
+ hr = E_FAIL;
+ }
+ }
+ }
+ return hr;
+}
+
+VBOXNETCFGWIN_DECL(HRESULT) VBoxNetCfgWinDhcpRediscover(IN const GUID *pGuid)
+{
+ ComPtr<IWbemServices> pSvc;
+ HRESULT hr = netIfWinCreateIWbemServices(pSvc.asOutParam());
+ if (SUCCEEDED(hr))
+ {
+ ComPtr<IWbemClassObject> pAdapterConfig;
+ hr = netIfWinFindAdapterClassById(pSvc, pGuid, pAdapterConfig.asOutParam());
+ if (SUCCEEDED(hr))
+ {
+ BOOL fIsHostOnly;
+ hr = netIfWinIsHostOnly(pAdapterConfig, &fIsHostOnly);
+ if (SUCCEEDED(hr))
+ {
+ if (fIsHostOnly)
+ {
+ com::Bstr bstrObjPath;
+ hr = netIfWinAdapterConfigPath(pAdapterConfig, &bstrObjPath);
+ if (SUCCEEDED(hr))
+ {
+ hr = netIfWinDhcpRediscover(pSvc, bstrObjPath);
+ if (SUCCEEDED(hr))
+ {
+ //hr = netIfWinUpdateConfig(pIf);
+ }
+ }
+ }
+ else
+ hr = E_FAIL;
+ }
+ }
+ }
+
+
+ return hr;
+}
+
+static const char *vboxNetCfgWinAddrToStr(char *pszBuf, size_t cbBuf, LPSOCKADDR pAddr)
+{
+ switch (pAddr->sa_family)
+ {
+ case AF_INET:
+ RTStrPrintf(pszBuf, cbBuf, "%d.%d.%d.%d",
+ ((PSOCKADDR_IN)pAddr)->sin_addr.S_un.S_un_b.s_b1,
+ ((PSOCKADDR_IN)pAddr)->sin_addr.S_un.S_un_b.s_b2,
+ ((PSOCKADDR_IN)pAddr)->sin_addr.S_un.S_un_b.s_b3,
+ ((PSOCKADDR_IN)pAddr)->sin_addr.S_un.S_un_b.s_b4);
+ break;
+ case AF_INET6:
+ RTStrPrintf(pszBuf, cbBuf, "%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x",
+ ((PSOCKADDR_IN6)pAddr)->sin6_addr.s6_addr[0], ((PSOCKADDR_IN6)pAddr)->sin6_addr.s6_addr[1],
+ ((PSOCKADDR_IN6)pAddr)->sin6_addr.s6_addr[2], ((PSOCKADDR_IN6)pAddr)->sin6_addr.s6_addr[3],
+ ((PSOCKADDR_IN6)pAddr)->sin6_addr.s6_addr[4], ((PSOCKADDR_IN6)pAddr)->sin6_addr.s6_addr[5],
+ ((PSOCKADDR_IN6)pAddr)->sin6_addr.s6_addr[6], ((PSOCKADDR_IN6)pAddr)->sin6_addr.s6_addr[7],
+ ((PSOCKADDR_IN6)pAddr)->sin6_addr.s6_addr[8], ((PSOCKADDR_IN6)pAddr)->sin6_addr.s6_addr[9],
+ ((PSOCKADDR_IN6)pAddr)->sin6_addr.s6_addr[10], ((PSOCKADDR_IN6)pAddr)->sin6_addr.s6_addr[11],
+ ((PSOCKADDR_IN6)pAddr)->sin6_addr.s6_addr[12], ((PSOCKADDR_IN6)pAddr)->sin6_addr.s6_addr[13],
+ ((PSOCKADDR_IN6)pAddr)->sin6_addr.s6_addr[14], ((PSOCKADDR_IN6)pAddr)->sin6_addr.s6_addr[15]);
+ break;
+ default:
+ RTStrCopy(pszBuf, cbBuf, "unknown");
+ break;
+ }
+ return pszBuf;
+}
+
+typedef bool (*PFNVBOXNETCFG_IPSETTINGS_CALLBACK) (ULONG ip, ULONG mask, PVOID pContext);
+
+static void vboxNetCfgWinEnumIpConfig(PIP_ADAPTER_ADDRESSES pAddresses, PFNVBOXNETCFG_IPSETTINGS_CALLBACK pfnCallback, PVOID pContext)
+{
+ PIP_ADAPTER_ADDRESSES pAdapter;
+ for (pAdapter = pAddresses; pAdapter; pAdapter = pAdapter->Next)
+ {
+ NonStandardLogFlow(("+- Enumerating adapter '%ls' %s\n", pAdapter->FriendlyName, pAdapter->AdapterName));
+ for (PIP_ADAPTER_PREFIX pPrefix = pAdapter->FirstPrefix; pPrefix; pPrefix = pPrefix->Next)
+ {
+ char szBuf[80];
+ const char *pcszAddress = vboxNetCfgWinAddrToStr(szBuf, sizeof(szBuf), pPrefix->Address.lpSockaddr);
+
+ /* We are concerned with IPv4 only, ignore the rest. */
+ if (pPrefix->Address.lpSockaddr->sa_family != AF_INET)
+ {
+ NonStandardLogFlow(("| +- %s %d: not IPv4, ignoring\n", pcszAddress, pPrefix->PrefixLength));
+ continue;
+ }
+
+ /* Ignore invalid prefixes as well as host addresses. */
+ if (pPrefix->PrefixLength < 1 || pPrefix->PrefixLength > 31)
+ {
+ NonStandardLogFlow(("| +- %s %d: host or broadcast, ignoring\n", pcszAddress, pPrefix->PrefixLength));
+ continue;
+ }
+
+ /* Ignore multicast and beyond. */
+ ULONG ip = ((struct sockaddr_in *)pPrefix->Address.lpSockaddr)->sin_addr.s_addr;
+ if ((ip & 0xF0) > 224)
+ {
+ NonStandardLogFlow(("| +- %s %d: multicast, ignoring\n", pcszAddress, pPrefix->PrefixLength));
+ continue;
+ }
+
+ ULONG mask = htonl((~(((ULONG)~0) >> pPrefix->PrefixLength)));
+ bool fContinue = pfnCallback(ip, mask, pContext);
+ if (!fContinue)
+ {
+ NonStandardLogFlow(("| +- %s %d: CONFLICT!\n", pcszAddress, pPrefix->PrefixLength));
+ return;
+ }
+
+ NonStandardLogFlow(("| +- %s %d: no conflict, moving on\n", pcszAddress, pPrefix->PrefixLength));
+ }
+ }
+}
+
+typedef struct _IPPROBE_CONTEXT
+{
+ ULONG Prefix;
+ bool fConflict;
+}IPPROBE_CONTEXT, *PIPPROBE_CONTEXT;
+
+#define IPPROBE_INIT(a_pContext, a_addr) \
+ do { (a_pContext)->fConflict = false; (a_pContext)->Prefix = (a_addr); } while (0)
+
+#define IPPROBE_INIT_STR(a_pContext, a_straddr) \
+ IPROBE_INIT(a_pContext, inet_addr(_straddr))
+
+static bool vboxNetCfgWinIpProbeCallback (ULONG ip, ULONG mask, PVOID pContext)
+{
+ PIPPROBE_CONTEXT pProbe = (PIPPROBE_CONTEXT)pContext;
+
+ if ((ip & mask) == (pProbe->Prefix & mask))
+ {
+ pProbe->fConflict = true;
+ return false;
+ }
+
+ return true;
+}
+
+VBOXNETCFGWIN_DECL(HRESULT) VBoxNetCfgWinGenHostOnlyNetworkNetworkIp(OUT PULONG pNetIp, OUT PULONG pNetMask)
+{
+ HRESULT hr = S_OK;
+
+ *pNetIp = 0;
+ *pNetMask = 0;
+
+ /*
+ * MSDN recommends to pre-allocate a 15KB buffer.
+ */
+ ULONG cbBuf = 15 * _1K;
+ PIP_ADAPTER_ADDRESSES paAddresses = (PIP_ADAPTER_ADDRESSES)RTMemAllocZ(cbBuf);
+ if (!paAddresses)
+ return E_OUTOFMEMORY;
+ DWORD dwRc = GetAdaptersAddresses(AF_UNSPEC, GAA_FLAG_INCLUDE_PREFIX, NULL, paAddresses, &cbBuf);
+ if (dwRc == ERROR_BUFFER_OVERFLOW)
+ {
+ /* Impressive! More than 10 adapters! Get more memory and try again. */
+ RTMemFree(paAddresses);
+ paAddresses = (PIP_ADAPTER_ADDRESSES)RTMemAllocZ(cbBuf);
+ if (!paAddresses)
+ return E_OUTOFMEMORY;
+ dwRc = GetAdaptersAddresses(AF_UNSPEC, GAA_FLAG_INCLUDE_PREFIX, NULL, paAddresses, &cbBuf);
+ }
+ if (dwRc == NO_ERROR)
+ {
+ const ULONG ip192168 = inet_addr("192.168.0.0");
+ for (int i = 0; i < 384; i++)
+ {
+#if 0
+ ULONG ipProbe = rand()*255 / RAND_MAX;
+#else
+ uint32_t ipProbe = RTRandU32Ex(0, 255);
+#endif
+ ipProbe = ip192168 | (ipProbe << 16);
+ NonStandardLogFlow(("probing %RTnaipv4\n", ipProbe));
+
+ IPPROBE_CONTEXT Context;
+ IPPROBE_INIT(&Context, ipProbe);
+ vboxNetCfgWinEnumIpConfig(paAddresses, vboxNetCfgWinIpProbeCallback, &Context);
+ if (!Context.fConflict)
+ {
+ NonStandardLogFlow(("found unused net %RTnaipv4\n", ipProbe));
+ *pNetIp = ipProbe;
+ *pNetMask = inet_addr("255.255.255.0");
+ break;
+ }
+ }
+ if (*pNetIp == 0)
+ dwRc = ERROR_DHCP_ADDRESS_CONFLICT;
+ }
+ else
+ NonStandardLogFlow(("GetAdaptersAddresses err (%u)\n", dwRc));
+
+ RTMemFree(paAddresses);
+
+ if (dwRc != NO_ERROR)
+ hr = HRESULT_FROM_WIN32(dwRc);
+ return hr;
+}
+
+/*
+ * convenience functions to perform netflt/adp manipulations
+ */
+#define VBOXNETCFGWIN_NETFLT_ID L"sun_VBoxNetFlt"
+#define VBOXNETCFGWIN_NETFLT_MP_ID L"sun_VBoxNetFltmp"
+
+static HRESULT vboxNetCfgWinNetFltUninstall(IN INetCfg *pNc, DWORD InfRmFlags)
+{
+ INetCfgComponent *pNcc = NULL;
+ HRESULT hr = pNc->FindComponent(VBOXNETCFGWIN_NETFLT_ID, &pNcc);
+ if (hr == S_OK)
+ {
+ NonStandardLog("NetFlt is installed currently, uninstalling ...\n");
+
+ hr = VBoxNetCfgWinUninstallComponent(pNc, pNcc);
+ NonStandardLogFlow(("NetFlt component uninstallation ended with hr (%Rhrc)\n", hr));
+
+ pNcc->Release();
+ }
+ else if (hr == S_FALSE)
+ NonStandardLog("NetFlt is not installed currently\n");
+ else
+ NonStandardLogFlow(("FindComponent failed: %Rhrc\n", hr));
+
+ VBoxDrvCfgInfUninstallAllF(L"NetService", VBOXNETCFGWIN_NETFLT_ID, InfRmFlags);
+ VBoxDrvCfgInfUninstallAllF(L"Net", VBOXNETCFGWIN_NETFLT_MP_ID, InfRmFlags);
+
+ return hr;
+}
+
+VBOXNETCFGWIN_DECL(HRESULT) VBoxNetCfgWinNetFltUninstall(IN INetCfg *pNc)
+{
+ return vboxNetCfgWinNetFltUninstall(pNc, 0);
+}
+
+VBOXNETCFGWIN_DECL(HRESULT) VBoxNetCfgWinNetFltInstall(IN INetCfg *pNc, IN LPCWSTR const *pwszInfFullPath, IN UINT cInfFullPaths)
+{
+ HRESULT hr = vboxNetCfgWinNetFltUninstall(pNc, SUOI_FORCEDELETE);
+ if (SUCCEEDED(hr))
+ {
+ NonStandardLog("NetFlt will be installed ...\n");
+ hr = vboxNetCfgWinInstallInfAndComponent(pNc, VBOXNETCFGWIN_NETFLT_ID,
+ &GUID_DEVCLASS_NETSERVICE,
+ pwszInfFullPath,
+ cInfFullPaths,
+ NULL);
+ }
+ return hr;
+}
+
+static HRESULT vboxNetCfgWinNetAdpUninstall(IN INetCfg *pNc, LPCWSTR pwszId, DWORD InfRmFlags)
+{
+ NOREF(pNc);
+ NonStandardLog("Finding NetAdp driver package and trying to uninstall it ...\n");
+
+ VBoxDrvCfgInfUninstallAllF(L"Net", pwszId, InfRmFlags);
+ NonStandardLog("NetAdp is not installed currently\n");
+ return S_OK;
+}
+
+VBOXNETCFGWIN_DECL(HRESULT) VBoxNetCfgWinNetAdpUninstall(IN INetCfg *pNc, IN LPCWSTR pwszId)
+{
+ return vboxNetCfgWinNetAdpUninstall(pNc, pwszId, SUOI_FORCEDELETE);
+}
+
+VBOXNETCFGWIN_DECL(HRESULT) VBoxNetCfgWinNetAdpInstall(IN INetCfg *pNc,
+ IN LPCWSTR const pwszInfFullPath)
+{
+ NonStandardLog("NetAdp will be installed ...\n");
+ HRESULT hr = vboxNetCfgWinInstallInfAndComponent(pNc, VBOXNETCFGWIN_NETADP_ID_WSZ,
+ &GUID_DEVCLASS_NET,
+ &pwszInfFullPath,
+ 1,
+ NULL);
+ return hr;
+}
+
+
+static HRESULT vboxNetCfgWinNetLwfUninstall(IN INetCfg *pNc, DWORD InfRmFlags)
+{
+ INetCfgComponent * pNcc = NULL;
+ HRESULT hr = pNc->FindComponent(VBOXNETCFGWIN_NETLWF_ID, &pNcc);
+ if (hr == S_OK)
+ {
+ NonStandardLog("NetLwf is installed currently, uninstalling ...\n");
+
+ hr = VBoxNetCfgWinUninstallComponent(pNc, pNcc);
+
+ pNcc->Release();
+ }
+ else if (hr == S_FALSE)
+ {
+ NonStandardLog("NetLwf is not installed currently\n");
+ hr = S_OK;
+ }
+ else
+ {
+ NonStandardLogFlow(("FindComponent failed: %Rhrc\n", hr));
+ hr = S_OK;
+ }
+
+ VBoxDrvCfgInfUninstallAllF(L"NetService", VBOXNETCFGWIN_NETLWF_ID, InfRmFlags);
+
+ return hr;
+}
+
+VBOXNETCFGWIN_DECL(HRESULT) VBoxNetCfgWinNetLwfUninstall(IN INetCfg *pNc)
+{
+ return vboxNetCfgWinNetLwfUninstall(pNc, 0);
+}
+
+static void VBoxNetCfgWinFilterLimitWorkaround(void)
+{
+ /*
+ * Need to check if the system has a limit of installed filter drivers. If it
+ * has, bump the limit to 14, which the maximum value supported by Windows 7.
+ * Note that we only touch the limit if it is set to the default value (8).
+ * See @bugref{7899}.
+ */
+ /** @todo r=bird: This code was mixing HRESULT and LSTATUS, checking the return
+ * codes using SUCCEEDED(lrc) instead of lrc == ERROR_SUCCESS. So, it might
+ * have misbehaved on bogus registry content, but worked fine on sane values. */
+ HKEY hKeyNet = NULL;
+ LSTATUS lrc = RegOpenKeyExW(HKEY_LOCAL_MACHINE, L"SYSTEM\\CurrentControlSet\\Control\\Network", 0,
+ KEY_QUERY_VALUE | KEY_SET_VALUE, &hKeyNet);
+ if (lrc == ERROR_SUCCESS)
+ {
+ DWORD dwMaxNumFilters = 0;
+ DWORD cbMaxNumFilters = sizeof(dwMaxNumFilters);
+ lrc = RegQueryValueExW(hKeyNet, L"MaxNumFilters", NULL, NULL, (LPBYTE)&dwMaxNumFilters, &cbMaxNumFilters);
+ if (lrc == ERROR_SUCCESS && cbMaxNumFilters == sizeof(dwMaxNumFilters) && dwMaxNumFilters == 8)
+ {
+ dwMaxNumFilters = 14;
+ lrc = RegSetValueExW(hKeyNet, L"MaxNumFilters", 0, REG_DWORD, (LPBYTE)&dwMaxNumFilters, sizeof(dwMaxNumFilters));
+ if (lrc == ERROR_SUCCESS)
+ NonStandardLog("Adjusted the installed filter limit to 14...\n");
+ else
+ NonStandardLog("Failed to set MaxNumFilters, error code %d\n", lrc);
+ }
+ RegCloseKey(hKeyNet);
+ }
+ else
+ NonStandardLog("Failed to open network key, error code %d\n", lrc);
+
+}
+
+VBOXNETCFGWIN_DECL(HRESULT) VBoxNetCfgWinNetLwfInstall(IN INetCfg *pNc, IN LPCWSTR const pwszInfFullPath)
+{
+ HRESULT hr = vboxNetCfgWinNetLwfUninstall(pNc, SUOI_FORCEDELETE);
+ if (SUCCEEDED(hr))
+ {
+ VBoxNetCfgWinFilterLimitWorkaround();
+ NonStandardLog("NetLwf will be installed ...\n");
+ hr = vboxNetCfgWinInstallInfAndComponent(pNc, VBOXNETCFGWIN_NETLWF_ID,
+ &GUID_DEVCLASS_NETSERVICE,
+ &pwszInfFullPath,
+ 1,
+ NULL);
+ }
+ return hr;
+}
+
+VBOXNETCFGWIN_DECL(HRESULT) VBoxNetCfgWinGenHostonlyConnectionName(IN PCWSTR pwszDevName, OUT WCHAR *pwszBuf,
+ IN ULONG cwcBuf, OUT PULONG pcwcNeeded)
+{
+ /* Look for a suffix that we need to preserve. */
+ size_t const cwcDevName = RTUtf16Len(pwszDevName);
+ size_t offSuffix = cwcDevName;
+ while (offSuffix > 0 && pwszDevName[offSuffix - 1] != '#')
+ offSuffix--;
+ size_t const cwcSuffix = pwszDevName[offSuffix] != '#' ? 0 : cwcDevName - offSuffix;
+
+ /* Calculate required buffer size: */
+ size_t cwcNeeded = sizeof(VBOX_CONNECTION_NAME_WSZ) / sizeof(wchar_t) /* includes terminator */
+ + !!cwcSuffix /*space*/ + cwcSuffix;
+ if (pcwcNeeded)
+ *pcwcNeeded = (ULONG)cwcNeeded;
+
+ if (cwcNeeded <= cwcBuf)
+ {
+ memcpy(pwszBuf, VBOX_CONNECTION_NAME_WSZ, sizeof(VBOX_CONNECTION_NAME_WSZ));
+ if (cwcSuffix > 0)
+ {
+ size_t offDst = sizeof(VBOX_CONNECTION_NAME_WSZ) / sizeof(wchar_t) - 1;
+ pwszBuf[offDst++] = ' ';
+ memcpy(&pwszBuf[offDst], &pwszDevName[offSuffix], cwcSuffix * sizeof(wchar_t));
+ pwszBuf[offDst + cwcSuffix] = '\0';
+ }
+ return S_OK;
+ }
+ return E_FAIL;
+}
+
+static BOOL vboxNetCfgWinAdjustHostOnlyNetworkInterfacePriority(IN INetCfg *pNc, IN INetCfgComponent *pNcc, PVOID pContext)
+{
+ GUID * const pGuid = (GUID*)pContext;
+ RT_NOREF1(pNc);
+
+ /* Get component's binding. */
+ INetCfgComponentBindings *pNetCfgBindings = NULL;
+ HRESULT hr = pNcc->QueryInterface(IID_INetCfgComponentBindings, (PVOID *)&pNetCfgBindings);
+ if (SUCCEEDED(hr))
+ {
+ /* Get binding path enumerator reference. */
+ IEnumNetCfgBindingPath *pEnumNetCfgBindPath = NULL;
+ hr = pNetCfgBindings->EnumBindingPaths(EBP_BELOW, &pEnumNetCfgBindPath);
+ if (SUCCEEDED(hr))
+ {
+ bool fFoundIface = false;
+ hr = pEnumNetCfgBindPath->Reset();
+ do
+ {
+ INetCfgBindingPath *pNetCfgBindPath = NULL;
+ hr = pEnumNetCfgBindPath->Next(1, &pNetCfgBindPath, NULL);
+ if (hr == S_OK)
+ {
+ IEnumNetCfgBindingInterface *pEnumNetCfgBindIface;
+ hr = pNetCfgBindPath->EnumBindingInterfaces(&pEnumNetCfgBindIface);
+ if (hr == S_OK)
+ {
+ pEnumNetCfgBindIface->Reset();
+ do
+ {
+ INetCfgBindingInterface *pNetCfgBindIfce;
+ hr = pEnumNetCfgBindIface->Next(1, &pNetCfgBindIfce, NULL);
+ if (hr == S_OK)
+ {
+ INetCfgComponent *pNetCfgCompo;
+ hr = pNetCfgBindIfce->GetLowerComponent(&pNetCfgCompo);
+ if (hr == S_OK)
+ {
+ ULONG uComponentStatus;
+ hr = pNetCfgCompo->GetDeviceStatus(&uComponentStatus);
+ if (hr == S_OK)
+ {
+ GUID guid;
+ hr = pNetCfgCompo->GetInstanceGuid(&guid);
+ if ( hr == S_OK
+ && guid == *pGuid)
+ {
+ hr = pNetCfgBindings->MoveAfter(pNetCfgBindPath, NULL);
+ if (FAILED(hr))
+ NonStandardLogFlow(("Unable to move interface: %Rhrc\n", hr));
+ fFoundIface = true;
+
+ /*
+ * Enable binding paths for host-only adapters bound to bridged filter
+ * (see @bugref{8140}).
+ */
+ HRESULT hr2;
+ LPWSTR pwszHwId = NULL;
+ if ((hr2 = pNcc->GetId(&pwszHwId)) != S_OK)
+ NonStandardLogFlow(("Failed to get HW ID: %Rhrc\n", hr2));
+ else
+ {
+ /** @todo r=bird: Original was:
+ * _wcsnicmp(pwszHwId, VBOXNETCFGWIN_NETLWF_ID, sizeof(VBOXNETCFGWIN_NETLWF_ID)/2)
+ * which is the same as _wcsicmp. Not sure if this was accidental, but it's not the
+ * only one in this code area (VBoxNetFltNobj.cpp had some too IIRC). */
+ if (RTUtf16ICmp(pwszHwId, VBOXNETCFGWIN_NETLWF_ID) != 0)
+ NonStandardLogFlow(("Ignoring component %ls\n", pwszHwId));
+ else if ((hr2 = pNetCfgBindPath->IsEnabled()) != S_FALSE)
+ NonStandardLogFlow(("Already enabled binding path: %Rhrc\n", hr2));
+ else if ((hr2 = pNetCfgBindPath->Enable(TRUE)) != S_OK)
+ NonStandardLogFlow(("Failed to enable binding path: %Rhrc\n", hr2));
+ else
+ NonStandardLogFlow(("Enabled binding path\n"));
+ CoTaskMemFree(pwszHwId);
+ }
+ }
+ }
+ pNetCfgCompo->Release();
+ }
+ else
+ NonStandardLogFlow(("GetLowerComponent failed: %Rhrc\n", hr));
+ pNetCfgBindIfce->Release();
+ }
+ else
+ {
+ if (hr == S_FALSE) /* No more binding interfaces? */
+ hr = S_OK;
+ else
+ NonStandardLogFlow(("Next binding interface failed: %Rhrc\n", hr));
+ break;
+ }
+ } while (!fFoundIface);
+ pEnumNetCfgBindIface->Release();
+ }
+ else
+ NonStandardLogFlow(("EnumBindingInterfaces failed: %Rhrc\n", hr));
+ pNetCfgBindPath->Release();
+ }
+ else
+ {
+ if (hr == S_FALSE) /* No more binding paths? */
+ hr = S_OK;
+ else
+ NonStandardLogFlow(("Next bind path failed: %Rhrc\n", hr));
+ break;
+ }
+ } while (!fFoundIface);
+ pEnumNetCfgBindPath->Release();
+ }
+ else
+ NonStandardLogFlow(("EnumBindingPaths failed: %Rhrc\n", hr));
+ pNetCfgBindings->Release();
+ }
+ else
+ NonStandardLogFlow(("QueryInterface for IID_INetCfgComponentBindings failed: %Rhrc\n", hr));
+ return TRUE;
+}
+
+/** Callback for SetupDiSetDeviceInstallParams used by
+ * vboxNetCfgWinCreateHostOnlyNetworkInterface */
+static UINT WINAPI vboxNetCfgWinPspFileCallback(PVOID Context, UINT Notification, UINT_PTR Param1, UINT_PTR Param2)
+{
+ switch (Notification)
+ {
+ case SPFILENOTIFY_TARGETNEWER:
+ case SPFILENOTIFY_TARGETEXISTS:
+ return TRUE;
+ }
+ return SetupDefaultQueueCallbackW(Context, Notification, Param1, Param2);
+}
+
+
+
+
+/* The original source of the VBoxNetAdp adapter creation/destruction code has the following copyright: */
+/*
+ Copyright 2004 by the Massachusetts Institute of Technology
+
+ All rights reserved.
+
+ Permission to use, copy, modify, and distribute this software and its
+ documentation for any purpose and without fee is hereby granted,
+ provided that the above copyright notice appear in all copies and that
+ both that copyright notice and this permission notice appear in
+ supporting documentation, and that the name of the Massachusetts
+ Institute of Technology (M.I.T.) not be used in advertising or publicity
+ pertaining to distribution of the software without specific, written
+ prior permission.
+
+ M.I.T. DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
+ ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
+ M.I.T. BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
+ ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
+ WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
+ ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
+ SOFTWARE.
+*/
+
+
+/**
+ * Use the IShellFolder API to rename the connection.
+ */
+static HRESULT rename_shellfolder(PCWSTR pwszGuid, PCWSTR pwszNewName)
+{
+ /* Build the display name in the form "::{GUID}". Do this first in case it overflows. */
+ WCHAR wszAdapterGuid[MAX_PATH + 2] = {0};
+ ssize_t cwc = RTUtf16Printf(wszAdapterGuid, RT_ELEMENTS(wszAdapterGuid), "::%ls", pwszGuid);
+ if (cwc < 0)
+ return E_INVALIDARG;
+
+ /* This is the GUID for the network connections folder. It is constant.
+ * {7007ACC7-3202-11D1-AAD2-00805FC1270E} */
+ const GUID MY_CLSID_NetworkConnections = {
+ 0x7007ACC7, 0x3202, 0x11D1, {
+ 0xAA, 0xD2, 0x00, 0x80, 0x5F, 0xC1, 0x27, 0x0E
+ }
+ };
+
+ /* Create an instance of the network connections folder. */
+ IShellFolder *pShellFolder = NULL;
+ HRESULT hr = CoCreateInstance(MY_CLSID_NetworkConnections, NULL, CLSCTX_INPROC_SERVER, IID_IShellFolder,
+ reinterpret_cast<LPVOID *>(&pShellFolder));
+ if (SUCCEEDED(hr))
+ {
+ /* Parse the display name. */
+ LPITEMIDLIST pidl = NULL;
+ hr = pShellFolder->ParseDisplayName(NULL, NULL, wszAdapterGuid, NULL, &pidl, NULL);
+ if (SUCCEEDED(hr))
+ hr = pShellFolder->SetNameOf(NULL, pidl, pwszNewName, SHGDN_NORMAL, &pidl);
+ CoTaskMemFree(pidl);
+ pShellFolder->Release();
+ }
+ return hr;
+}
+
+/**
+ * Loads a system DLL.
+ *
+ * @returns Module handle or NULL
+ * @param pwszName The DLL name.
+ */
+static HMODULE loadSystemDll(const wchar_t *pwszName)
+{
+ WCHAR wszPath[MAX_PATH];
+ UINT cwcPath = GetSystemDirectoryW(wszPath, RT_ELEMENTS(wszPath));
+ size_t cwcName = RTUtf16Len(pwszName) + 1;
+ if (cwcPath + 1 + cwcName > RT_ELEMENTS(wszPath))
+ return NULL;
+
+ wszPath[cwcPath++] = '\\';
+ memcpy(&wszPath[cwcPath], pwszName, cwcName * sizeof(wszPath[0]));
+ return LoadLibraryW(wszPath);
+}
+
+static bool vboxNetCfgWinDetectStaleConnection(PCWSTR pwszName)
+{
+ HKEY hKeyAdapters = NULL;
+ LSTATUS lrc = RegOpenKeyEx(HKEY_LOCAL_MACHINE,
+ L"SYSTEM\\CurrentControlSet\\Control\\Network\\{4D36E972-E325-11CE-BFC1-08002BE10318}",
+ 0 /*ulOptions*/, KEY_ALL_ACCESS, &hKeyAdapters);
+ if (lrc != ERROR_SUCCESS)
+ return false;
+
+ bool fFailureImminent = false;
+ for (DWORD i = 0; !fFailureImminent; ++i)
+ {
+ WCHAR wszAdapterSubKeyName[MAX_PATH];
+ DWORD cwcAdapterSubKeyName = MAX_PATH;
+ lrc = RegEnumKeyEx(hKeyAdapters, i, wszAdapterSubKeyName, &cwcAdapterSubKeyName, NULL, NULL, NULL, NULL);
+ if (lrc != ERROR_SUCCESS)
+ break;
+
+ HKEY hKeyAdapter = NULL;
+ lrc = RegOpenKeyEx(hKeyAdapters, wszAdapterSubKeyName, 0, KEY_ALL_ACCESS, &hKeyAdapter);
+ if (lrc == ERROR_SUCCESS)
+ {
+ HKEY hKeyConnection = NULL;
+ lrc = RegOpenKeyEx(hKeyAdapter, L"Connection", 0, KEY_ALL_ACCESS, &hKeyConnection);
+ if (lrc == ERROR_SUCCESS)
+ {
+ WCHAR wszCurName[MAX_PATH + 1];
+ DWORD cbCurName = sizeof(wszCurName) - sizeof(WCHAR);
+ DWORD dwType = REG_SZ;
+ lrc = RegQueryValueEx(hKeyConnection, L"Name", NULL, NULL, (LPBYTE)wszCurName, &cbCurName);
+ if ( lrc == ERROR_SUCCESS
+ /** @todo r=bird: The original code didn't do any value type checks, thus allowing all SZ types. */
+ && (dwType == REG_SZ || dwType == REG_EXPAND_SZ || dwType == REG_MULTI_SZ))
+ {
+ wszCurName[MAX_PATH] = '\0'; /* returned values doesn't necessarily need to be terminated */
+
+ if (RTUtf16ICmp(pwszName, pwszName) == 0)
+ fFailureImminent = true;
+ }
+ RegCloseKey(hKeyConnection);
+ }
+ RegCloseKey(hKeyAdapter);
+ }
+ }
+ RegCloseKey(hKeyAdapters);
+
+ return fFailureImminent;
+}
+
+VBOXNETCFGWIN_DECL(HRESULT) VBoxNetCfgWinRenameConnection(LPWSTR pwszGuid, PCWSTR NewName)
+{
+ /*
+ * Before attempting to rename the connection, check if there is a stale
+ * connection with the same name. We must return ok, so the rest of
+ * configuration process proceeds normally.
+ */
+ if (vboxNetCfgWinDetectStaleConnection(NewName))
+ return S_OK;
+
+ /* First try the IShellFolder interface, which was unimplemented
+ * for the network connections folder before XP. */
+ HRESULT hrc = rename_shellfolder(pwszGuid, NewName);
+ if (hrc == E_NOTIMPL)
+ {
+/** @todo that code doesn't seem to work! */
+ /* The IShellFolder interface is not implemented on this platform.
+ * Try the (undocumented) HrRenameConnection API in the netshell
+ * library. */
+ CLSID clsid;
+ hrc = CLSIDFromString((LPOLESTR)pwszGuid, &clsid);
+ if (FAILED(hrc))
+ return E_FAIL;
+
+ HINSTANCE hNetShell = loadSystemDll(L"netshell.dll");
+ if (hNetShell == NULL)
+ return E_FAIL;
+
+ typedef HRESULT (WINAPI *PFNHRRENAMECONNECTION)(const GUID *, PCWSTR);
+ PFNHRRENAMECONNECTION pfnRenameConnection = (PFNHRRENAMECONNECTION)GetProcAddress(hNetShell, "HrRenameConnection");
+ if (pfnRenameConnection != NULL)
+ hrc = pfnRenameConnection(&clsid, NewName);
+ else
+ hrc = E_FAIL;
+
+ FreeLibrary(hNetShell);
+ }
+ if (FAILED(hrc))
+ return hrc;
+ return S_OK;
+}
+
+
+VBOXNETCFGWIN_DECL(HRESULT) VBoxNetCfgWinRemoveHostOnlyNetworkInterface(IN const GUID *pGUID, OUT BSTR *pBstrErrMsg)
+{
+ HRESULT hrc = S_OK;
+ com::Bstr bstrError;
+
+ do /* break non-loop */
+ {
+ WCHAR wszPnPInstanceId[512] = {0};
+
+ /*
+ * We have to find the device instance ID through a registry search
+ */
+ HKEY hkeyNetwork = NULL;
+ HKEY hkeyConnection = NULL;
+ do /* another non-loop for breaking out of */
+ {
+ WCHAR wszGuid[50];
+ int cwcGuid = StringFromGUID2(*pGUID, wszGuid, RT_ELEMENTS(wszGuid));
+ if (!cwcGuid)
+ SetErrBreak(("Failed to create a Guid string"));
+
+ WCHAR wszRegLocation[128 + RT_ELEMENTS(wszGuid)];
+ RTUtf16Printf(wszRegLocation, RT_ELEMENTS(wszRegLocation),
+ "SYSTEM\\CurrentControlSet\\Control\\Network\\{4D36E972-E325-11CE-BFC1-08002BE10318}\\%ls", wszGuid);
+
+ LSTATUS lrc = RegOpenKeyExW(HKEY_LOCAL_MACHINE, wszRegLocation, 0, KEY_READ, &hkeyNetwork);
+ if (lrc != ERROR_SUCCESS || !hkeyNetwork)
+ SetErrBreak(("Host interface network is not found in registry (%S): lrc=%u [1]", wszRegLocation, lrc));
+
+ lrc = RegOpenKeyExW(hkeyNetwork, L"Connection", 0, KEY_READ, &hkeyConnection);
+ if (lrc != ERROR_SUCCESS || !hkeyConnection)
+ SetErrBreak(("Host interface network is not found in registry (%S): lrc=%u [2]", wszRegLocation, lrc));
+
+ DWORD cbValue = sizeof(wszPnPInstanceId) - sizeof(WCHAR);
+ DWORD dwType = ~0U;
+ lrc = RegQueryValueExW(hkeyConnection, L"PnPInstanceID", NULL, &dwType, (LPBYTE)wszPnPInstanceId, &cbValue);
+ if (lrc != ERROR_SUCCESS || dwType != REG_SZ)
+ SetErrBreak(("Host interface network is not found in registry (%S): lrc=%u, dwType=%u [3]",
+ wszRegLocation, lrc, dwType));
+ } while (0);
+
+ if (hkeyConnection)
+ RegCloseKey(hkeyConnection);
+ if (hkeyNetwork)
+ RegCloseKey(hkeyNetwork);
+ if (FAILED(hrc))
+ break;
+
+ /*
+ * Now we are going to enumerate all network devices and
+ * wait until we encounter the right device instance ID
+ */
+ HDEVINFO hDeviceInfo = INVALID_HANDLE_VALUE;
+ do /* break-only, not-a-loop */
+ {
+ BOOL ok;
+
+ /* initialize the structure size */
+ SP_DEVINFO_DATA DeviceInfoData = { sizeof(DeviceInfoData) };
+
+ /* copy the net class GUID */
+ GUID netGuid;
+ memcpy(&netGuid, &GUID_DEVCLASS_NET, sizeof(GUID_DEVCLASS_NET));
+
+ /* return a device info set contains all installed devices of the Net class */
+ hDeviceInfo = SetupDiGetClassDevs(&netGuid, NULL, NULL, DIGCF_PRESENT);
+ if (hDeviceInfo == INVALID_HANDLE_VALUE)
+ SetErrBreak(("SetupDiGetClassDevs failed (0x%08X)", GetLastError()));
+
+ /* Enumerate the driver info list. */
+ bool fFound = false;
+ for (DWORD index = 0; !fFound; index++)
+ {
+ if (!SetupDiEnumDeviceInfo(hDeviceInfo, index, &DeviceInfoData))
+ {
+ if (GetLastError() == ERROR_NO_MORE_ITEMS)
+ break;
+ continue;
+ }
+
+ /* try to get the hardware ID registry property */
+ DWORD cbValue = 0;
+ if (SetupDiGetDeviceRegistryPropertyW(hDeviceInfo,
+ &DeviceInfoData,
+ SPDRP_HARDWAREID,
+ NULL,
+ NULL,
+ 0,
+ &cbValue))
+ continue; /* Something is wrong. This shouldn't have worked with a NULL buffer! */
+ if (GetLastError() != ERROR_INSUFFICIENT_BUFFER)
+ continue;
+
+ WCHAR *pwszzDeviceHwId = (WCHAR *)RTMemAllocZ(cbValue + sizeof(WCHAR) * 2);
+ if (!pwszzDeviceHwId)
+ break;
+ if (SetupDiGetDeviceRegistryPropertyW(hDeviceInfo,
+ &DeviceInfoData,
+ SPDRP_HARDWAREID,
+ NULL,
+ (PBYTE)pwszzDeviceHwId,
+ cbValue,
+ &cbValue))
+ {
+ /* search the string list. */
+ for (WCHAR *pwszCurHwId = pwszzDeviceHwId;
+ (uintptr_t)pwszCurHwId - (uintptr_t)pwszzDeviceHwId < cbValue && *pwszCurHwId != L'\0';
+ pwszCurHwId += RTUtf16Len(pwszCurHwId) + 1)
+ if (RTUtf16ICmp(DRIVERHWID, pwszCurHwId) == 0)
+ {
+ /* get the device instance ID */
+ WCHAR wszDevId[MAX_DEVICE_ID_LEN];
+ if (CM_Get_Device_IDW(DeviceInfoData.DevInst, wszDevId, MAX_DEVICE_ID_LEN, 0) == CR_SUCCESS)
+ {
+ /* compare to what we determined before */
+ if (RTUtf16Cmp(wszDevId, wszPnPInstanceId) == 0)
+ {
+ fFound = true;
+ break;
+ }
+ }
+ }
+ }
+ RTMemFree(pwszzDeviceHwId);
+ }
+
+ if (!fFound)
+ SetErrBreak(("Host Interface Network driver not found (0x%08X)", GetLastError()));
+
+ ok = SetupDiSetSelectedDevice(hDeviceInfo, &DeviceInfoData);
+ if (!ok)
+ SetErrBreak(("SetupDiSetSelectedDevice failed (0x%08X)", GetLastError()));
+
+ ok = SetupDiCallClassInstaller(DIF_REMOVE, hDeviceInfo, &DeviceInfoData);
+ if (!ok)
+ SetErrBreak(("SetupDiCallClassInstaller (DIF_REMOVE) failed (0x%08X)", GetLastError()));
+ } while (0);
+
+ /* clean up the device info set */
+ if (hDeviceInfo != INVALID_HANDLE_VALUE)
+ SetupDiDestroyDeviceInfoList (hDeviceInfo);
+ } while (0);
+
+ if (pBstrErrMsg)
+ {
+ *pBstrErrMsg = NULL;
+ if (bstrError.isNotEmpty())
+ bstrError.detachToEx(pBstrErrMsg);
+ }
+ return hrc;
+}
+
+VBOXNETCFGWIN_DECL(HRESULT) VBoxNetCfgWinUpdateHostOnlyNetworkInterface(LPCWSTR pcsxwInf, BOOL *pfRebootRequired, LPCWSTR pcsxwId)
+{
+ return VBoxDrvCfgDrvUpdate(pcsxwId, pcsxwInf, pfRebootRequired);
+}
+
+static const char *vboxNetCfgWinGetStateText(DWORD dwState)
+{
+ switch (dwState)
+ {
+ case SERVICE_STOPPED: return "is not running";
+ case SERVICE_STOP_PENDING: return "is stopping";
+ case SERVICE_CONTINUE_PENDING: return "continue is pending";
+ case SERVICE_PAUSE_PENDING: return "pause is pending";
+ case SERVICE_PAUSED: return "is paused";
+ case SERVICE_RUNNING: return "is running";
+ case SERVICE_START_PENDING: return "is starting";
+ }
+ return "state is invalid";
+}
+
+static DWORD vboxNetCfgWinGetNetSetupState(SC_HANDLE hService)
+{
+ SERVICE_STATUS status;
+ status.dwCurrentState = SERVICE_RUNNING;
+ if (hService) {
+ if (QueryServiceStatus(hService, &status))
+ NonStandardLogFlow(("NetSetupSvc %s\n", vboxNetCfgWinGetStateText(status.dwCurrentState)));
+ else
+ NonStandardLogFlow(("QueryServiceStatus failed (0x%x)\n", GetLastError()));
+ }
+ return status.dwCurrentState;
+}
+
+DECLINLINE(bool) vboxNetCfgWinIsNetSetupRunning(SC_HANDLE hService)
+{
+ return vboxNetCfgWinGetNetSetupState(hService) == SERVICE_RUNNING;
+}
+
+DECLINLINE(bool) vboxNetCfgWinIsNetSetupStopped(SC_HANDLE hService)
+{
+ return vboxNetCfgWinGetNetSetupState(hService) == SERVICE_STOPPED;
+}
+
+typedef struct
+{
+ BSTR bstrName;
+ GUID *pGuid;
+ HRESULT hr;
+} RENAMING_CONTEXT;
+
+static BOOL vboxNetCfgWinRenameHostOnlyNetworkInterface(IN INetCfg *pNc, IN INetCfgComponent *pNcc, PVOID pContext)
+{
+ RT_NOREF1(pNc);
+ RENAMING_CONTEXT *pParams = (RENAMING_CONTEXT *)pContext;
+
+ GUID guid;
+ pParams->hr = pNcc->GetInstanceGuid(&guid);
+ if ( pParams->hr == S_OK && guid == *pParams->pGuid)
+ {
+ /* Located our component, rename it */
+ pParams->hr = pNcc->SetDisplayName(pParams->bstrName);
+ return FALSE;
+ }
+ return TRUE;
+}
+
+/**
+ * Enumerate all host-only adapters collecting their names into a set, then
+ * come up with the next available name by taking the first unoccupied index.
+ */
+static HRESULT vboxNetCfgWinNextAvailableDevName(com::Bstr *pbstrName)
+{
+ SP_DEVINFO_DATA DeviceInfoData = { sizeof(SP_DEVINFO_DATA) };
+ HDEVINFO hDeviceInfoSet = SetupDiGetClassDevsW(&GUID_DEVCLASS_NET, NULL, NULL, DIGCF_PRESENT);
+ if (hDeviceInfoSet == INVALID_HANDLE_VALUE)
+ return HRESULT_FROM_WIN32(GetLastError());
+
+ typedef struct VBOXDEVNAMEENTRY
+ {
+ RTLISTNODE ListEntry;
+ WCHAR wszDevName[64];
+ WCHAR wcZeroParanoia;
+ } VBOXDEVNAMEENTRY;
+
+#if 0
+ /*
+ * Build a list of names starting with HOSTONLY_ADAPTER_NAME_WSZ belonging to our device.
+ */
+ RTLISTANCHOR Head; /* VBOXDEVNAMEENTRY */
+ RTListInit(&Head);
+ HRESULT hrc = S_OK;
+#else
+ /*
+ * Build a bitmap of in-use index values of devices starting with HOSTONLY_ADAPTER_NAME_WSZ.
+ * Reserving 0 for one w/o a suffix, and marking 1 as unusable.
+ */
+ uint64_t bmIndexes[_32K / 64]; /* 4KB - 32767 device should be sufficient. */
+ RT_ZERO(bmIndexes);
+ ASMBitSet(&bmIndexes, 1);
+#endif
+ for (DWORD i = 0; SetupDiEnumDeviceInfo(hDeviceInfoSet, i, &DeviceInfoData); ++i)
+ {
+ /* Should be more than enough for both our device id and our device name, we do not care about the rest */
+ VBOXDEVNAMEENTRY Entry = { { 0, 0 }, L"", 0 }; /* (initialize it to avoid the wrath of asan) */
+ if (!SetupDiGetDeviceRegistryPropertyW(hDeviceInfoSet, &DeviceInfoData, SPDRP_HARDWAREID,
+ NULL, (PBYTE)Entry.wszDevName, sizeof(Entry.wszDevName), NULL))
+ continue;
+
+ /* Ignore everything except our host-only adapters */
+ if (RTUtf16ICmp(Entry.wszDevName, DRIVERHWID) == 0)
+ {
+ if ( SetupDiGetDeviceRegistryPropertyW(hDeviceInfoSet, &DeviceInfoData, SPDRP_FRIENDLYNAME,
+ NULL, (PBYTE)Entry.wszDevName, sizeof(Entry.wszDevName), NULL)
+ || SetupDiGetDeviceRegistryPropertyW(hDeviceInfoSet, &DeviceInfoData, SPDRP_DEVICEDESC,
+ NULL, (PBYTE)Entry.wszDevName, sizeof(Entry.wszDevName), NULL))
+ {
+ /* We can ignore any host-only adapter with a non-standard name. */
+ if (RTUtf16NICmp(Entry.wszDevName, HOSTONLY_ADAPTER_NAME_WSZ,
+ RT_ELEMENTS(HOSTONLY_ADAPTER_NAME_WSZ) - 1) == 0)
+ {
+#if 0
+ VBOXDEVNAMEENTRY *pEntry = (VBOXDEVNAMEENTRY *)RTMemDup(&Entry, sizeof(Entry));
+ if (pEntry)
+ RTListAppend(&Head, &pEntry->ListEntry);
+ else
+ {
+ hrc = E_OUTOFMEMORY;
+ break;
+ }
+#else
+ WCHAR const *pwc = &Entry.wszDevName[RT_ELEMENTS(HOSTONLY_ADAPTER_NAME_WSZ) - 1];
+
+ /* skip leading space */
+ WCHAR wc = *pwc;
+ while (wc == L' ' || wc == L'\t' || wc == L'\n' || wc == L'\r')
+ wc = *++pwc;
+
+ /* If end of string, use index 0. */
+ if (wc == L'\0')
+ ASMBitSet(bmIndexes, 0);
+
+ /* Hash and digit? */
+ else if (wc == L'#')
+ {
+ wc = *++pwc;
+ while (wc == L' ' || wc == L'\t' || wc == L'\n' || wc == L'\r') /* just in case */
+ wc = *++pwc;
+ if (wc >= L'0' && wc <= L'9')
+ {
+ /* Convert what we can to a number and mark it as allocated in the bitmap. */
+ uint64_t uIndex = wc - L'0';
+ while ((wc = *++pwc) >= L'0' && wc <= L'9')
+ uIndex = uIndex * 10 + wc - L'0';
+ if (uIndex < sizeof(bmIndexes) * 8 && uIndex > 0)
+ ASMBitSet(bmIndexes, (int32_t)uIndex);
+ }
+ }
+#endif
+ }
+ }
+ }
+ }
+#if 0
+ if (SUCCEEDED(hrc))
+ {
+ /*
+ * First try a name w/o an index, then try index #2 and up.
+ *
+ * Note! We have to use ASCII/UTF-8 strings here as Bstr will confuse WCHAR
+ * with BSTR/OLECHAR strings and use SysAllocString to duplicate it .
+ */
+ char szName[sizeof(HOSTONLY_ADAPTER_NAME_SZ " #4294967296") + 32] = HOSTONLY_ADAPTER_NAME_SZ;
+ size_t const cchBase = sizeof(HOSTONLY_ADAPTER_NAME_SZ) - 1;
+ for (DWORD idx = 2;; idx++)
+ {
+ bool fFound = false;
+ VBOXDEVNAMEENTRY *pCur;
+ RTListForEach(&Head, pCur, VBOXDEVNAMEENTRY, ListEntry)
+ {
+ fFound = RTUtf16ICmpAscii(pCur->wszDevName, szName) == 0;
+ if (fFound)
+ {
+ hrc = pbstrName->assignEx(szName);
+ break;
+ }
+ }
+ if (fFound)
+ break;
+ RTStrPrintf(&szName[cchBase], sizeof(szName) - cchBase, " #%u", idx);
+ }
+ }
+
+ VBOXDEVNAMEENTRY *pFirst;
+ while ((pFirst = RTListRemoveFirst(&Head, VBOXDEVNAMEENTRY, ListEntry)) != NULL)
+ RTMemFree(pFirst);
+
+#else
+ /*
+ * Find an unused index value and format the corresponding name.
+ */
+ HRESULT hrc;
+ int32_t iBit = ASMBitFirstClear(bmIndexes, sizeof(bmIndexes) * 8);
+ if (iBit >= 0)
+ {
+ if (iBit == 0)
+ hrc = pbstrName->assignEx(HOSTONLY_ADAPTER_NAME_SZ); /* Not _WSZ! */
+ else
+ hrc = pbstrName->printfNoThrow(HOSTONLY_ADAPTER_NAME_SZ " #%u", iBit);
+ }
+ else
+ {
+ NonStandardLogFlow(("vboxNetCfgWinNextAvailableDevName: no unused index in the first 32K!\n"));
+ hrc = E_FAIL;
+ }
+#endif
+
+ if (hDeviceInfoSet)
+ SetupDiDestroyDeviceInfoList(hDeviceInfoSet);
+ return hrc;
+}
+
+static HRESULT vboxNetCfgWinCreateHostOnlyNetworkInterface(IN LPCWSTR pwszInfPath, IN bool fIsInfPathFile,
+ IN BSTR pBstrDesiredName,
+ OUT GUID *pGuid, OUT BSTR *pBstrName, OUT BSTR *pBstrErrMsg)
+{
+ com::Bstr bstrError;
+
+ /* Determine the interface name. We make a copy of the input here for
+ renaming reasons, see futher down. */
+ com::Bstr bstrNewInterfaceName;
+ HRESULT hrc;
+ if (SysStringLen(pBstrDesiredName) != 0)
+ hrc = bstrNewInterfaceName.assignEx(pBstrDesiredName);
+ else
+ {
+ hrc = vboxNetCfgWinNextAvailableDevName(&bstrNewInterfaceName);
+ if (FAILED(hrc))
+ NonStandardLogFlow(("vboxNetCfgWinNextAvailableDevName failed with 0x%x\n", hrc));
+ }
+ if (FAILED(hrc))
+ return hrc;
+
+ WCHAR wszCfgGuidString[50] = {0};
+ WCHAR wszDevName[256 + 1] = {0};
+ SP_DEVINFO_DATA DeviceInfoData = { sizeof(DeviceInfoData) };
+ HDEVINFO hDeviceInfo = INVALID_HANDLE_VALUE;
+ PVOID pQueueCallbackContext = NULL;
+ BOOL fRegistered = FALSE;
+ BOOL destroyList = FALSE;
+ HKEY hkey = (HKEY)INVALID_HANDLE_VALUE;
+ LSTATUS lrcRet = ERROR_SUCCESS; /* the */
+
+ do /* non-loop, for breaking. */
+ {
+ /* copy the net class GUID */
+ GUID netGuid;
+ memcpy(&netGuid, &GUID_DEVCLASS_NET, sizeof(GUID_DEVCLASS_NET));
+
+ /* Create an empty device info set associated with the net class GUID: */
+ hDeviceInfo = SetupDiCreateDeviceInfoList(&netGuid, NULL);
+ if (hDeviceInfo == INVALID_HANDLE_VALUE)
+ SetErrBreak(("SetupDiCreateDeviceInfoList failed (%Rwc)", GetLastError()));
+
+ /* Translate the GUID to a class name: */
+ WCHAR wszClassName[MAX_PATH];
+ if (!SetupDiClassNameFromGuid(&netGuid, wszClassName, MAX_PATH, NULL))
+ SetErrBreak(("SetupDiClassNameFromGuid failed (%Rwc)", GetLastError()));
+
+ /* Create a device info element and add the new device instance key to registry: */
+ if (!SetupDiCreateDeviceInfo(hDeviceInfo, wszClassName, &netGuid, NULL, NULL, DICD_GENERATE_ID, &DeviceInfoData))
+ SetErrBreak(("SetupDiCreateDeviceInfo failed (%Rwc)", GetLastError()));
+
+ /* Select the newly created device info to be the currently selected member: */
+ if (!SetupDiSetSelectedDevice(hDeviceInfo, &DeviceInfoData))
+ SetErrBreak(("SetupDiSetSelectedDevice failed (%Rwc)", GetLastError()));
+
+ SP_DEVINSTALL_PARAMS DeviceInstallParams;
+ if (pwszInfPath)
+ {
+ /* get the device install parameters and disable filecopy */
+ DeviceInstallParams.cbSize = sizeof(SP_DEVINSTALL_PARAMS);
+ if (SetupDiGetDeviceInstallParams(hDeviceInfo, &DeviceInfoData, &DeviceInstallParams))
+ {
+ memset(DeviceInstallParams.DriverPath, 0, sizeof(DeviceInstallParams.DriverPath));
+ size_t pathLenght = wcslen(pwszInfPath) + 1/* null terminator */;
+ if (pathLenght < sizeof(DeviceInstallParams.DriverPath)/sizeof(DeviceInstallParams.DriverPath[0]))
+ {
+ memcpy(DeviceInstallParams.DriverPath, pwszInfPath, pathLenght*sizeof(DeviceInstallParams.DriverPath[0]));
+
+ if (fIsInfPathFile)
+ DeviceInstallParams.Flags |= DI_ENUMSINGLEINF;
+
+ if (!SetupDiSetDeviceInstallParams(hDeviceInfo, &DeviceInfoData, &DeviceInstallParams))
+ {
+ NonStandardLogFlow(("SetupDiSetDeviceInstallParams failed (%Rwc)\n", GetLastError()));
+ break;
+ }
+ }
+ else
+ {
+ NonStandardLogFlow(("SetupDiSetDeviceInstallParams faileed: INF path is too long\n"));
+ break;
+ }
+ }
+ else
+ NonStandardLogFlow(("SetupDiGetDeviceInstallParams failed (%Rwc)\n", GetLastError()));
+ }
+
+ /* build a list of class drivers */
+ if (!SetupDiBuildDriverInfoList(hDeviceInfo, &DeviceInfoData, SPDIT_CLASSDRIVER))
+ SetErrBreak(("SetupDiBuildDriverInfoList failed (%Rwc)", GetLastError()));
+
+ destroyList = TRUE;
+
+ /*
+ * Enumerate the driver info list.
+ */
+ /* For our purposes, 2k buffer is more than enough to obtain the
+ hardware ID of the VBoxNetAdp driver. */ /** @todo r=bird: The buffer isn't 2KB, it's 8KB, but whatever. */
+ DWORD detailBuf[2048];
+ SP_DRVINFO_DATA DriverInfoData = { sizeof(DriverInfoData) };
+ bool fFound = false;
+ for (DWORD index = 0; !fFound; index++)
+ {
+ /* If the function fails with last error set to ERROR_NO_MORE_ITEMS,
+ then we have reached the end of the list. Otherwise there was
+ something wrong with this particular driver. */
+ if (!SetupDiEnumDriverInfo(hDeviceInfo, &DeviceInfoData, SPDIT_CLASSDRIVER, index, &DriverInfoData))
+ {
+ if (GetLastError() == ERROR_NO_MORE_ITEMS)
+ break;
+ continue;
+ }
+
+ /* if we successfully find the hardware ID and it turns out to
+ * be the one for the loopback driver, then we are done. */
+ PSP_DRVINFO_DETAIL_DATA_W pDriverInfoDetail = (PSP_DRVINFO_DETAIL_DATA_W)detailBuf;
+ pDriverInfoDetail->cbSize = sizeof(SP_DRVINFO_DETAIL_DATA);
+ DWORD cbValue = 0;
+ if (SetupDiGetDriverInfoDetailW(hDeviceInfo,
+ &DeviceInfoData,
+ &DriverInfoData,
+ pDriverInfoDetail,
+ sizeof(detailBuf) - sizeof(detailBuf[0]),
+ &cbValue))
+ {
+ /* Sure that the HardwareID string list is properly zero terminated (paranoia). */
+ detailBuf[RT_ELEMENTS(detailBuf) - 1] = 0; AssertCompile(sizeof(detailBuf[0]) == sizeof(WCHAR) * 2);
+
+ /* pDriverInfoDetail->HardwareID is a MULTISZ string. Go through the whole
+ list and see if there is a match somewhere: */
+ for (WCHAR *pwszCurHwId = pDriverInfoDetail->HardwareID;
+ (uintptr_t)pwszCurHwId - (uintptr_t)pDriverInfoDetail < cbValue && *pwszCurHwId != L'\0';
+ pwszCurHwId += RTUtf16Len(pwszCurHwId) + 1)
+ if (RTUtf16ICmp(DRIVERHWID, pwszCurHwId) == 0)
+ {
+ fFound = true;
+ break;
+ }
+ }
+ }
+
+ if (!fFound)
+ SetErrBreak(("Could not find Host Interface Networking driver! Please reinstall"));
+
+ /* set the loopback driver to be the currently selected */
+ if (!SetupDiSetSelectedDriver(hDeviceInfo, &DeviceInfoData, &DriverInfoData))
+ SetErrBreak(("SetupDiSetSelectedDriver failed (%u)", GetLastError()));
+
+ /* register the phantom device to prepare for install */
+ if (!SetupDiCallClassInstaller(DIF_REGISTERDEVICE, hDeviceInfo, &DeviceInfoData))
+ SetErrBreak(("SetupDiCallClassInstaller failed (%u)", GetLastError()));
+
+ /* registered, but remove if errors occur in the following code */
+ fRegistered = TRUE;
+
+ /* ask the installer if we can install the device */
+ if (!SetupDiCallClassInstaller(DIF_ALLOW_INSTALL, hDeviceInfo, &DeviceInfoData))
+ {
+ if (GetLastError() != ERROR_DI_DO_DEFAULT)
+ SetErrBreak(("SetupDiCallClassInstaller (DIF_ALLOW_INSTALL) failed (%u)", GetLastError()));
+ /* that's fine */
+ }
+
+ /* get the device install parameters and disable filecopy */
+ DeviceInstallParams.cbSize = sizeof(SP_DEVINSTALL_PARAMS);
+ if (SetupDiGetDeviceInstallParams(hDeviceInfo, &DeviceInfoData, &DeviceInstallParams))
+ {
+ pQueueCallbackContext = SetupInitDefaultQueueCallback(NULL);
+ if (pQueueCallbackContext)
+ {
+ DeviceInstallParams.InstallMsgHandlerContext = pQueueCallbackContext;
+ DeviceInstallParams.InstallMsgHandler = (PSP_FILE_CALLBACK)vboxNetCfgWinPspFileCallback;
+ if (!SetupDiSetDeviceInstallParamsW(hDeviceInfo, &DeviceInfoData, &DeviceInstallParams))
+ {
+ DWORD winEr = GetLastError();
+ NonStandardLogFlow(("SetupDiSetDeviceInstallParamsW failed, winEr (%d)\n", winEr));
+ Assert(0);
+ }
+ }
+ else
+ {
+ DWORD winEr = GetLastError();
+ NonStandardLogFlow(("SetupInitDefaultQueueCallback failed, winEr (%d)\n", winEr));
+ }
+ }
+ else
+ {
+ DWORD winEr = GetLastError();
+ NonStandardLogFlow(("SetupDiGetDeviceInstallParams failed, winEr (%d)\n", winEr));
+ }
+
+ /* install the files first */
+ if (!SetupDiCallClassInstaller(DIF_INSTALLDEVICEFILES, hDeviceInfo, &DeviceInfoData))
+ SetErrBreak(("SetupDiCallClassInstaller (DIF_INSTALLDEVICEFILES) failed (%Rwc)", GetLastError()));
+
+ /* get the device install parameters and disable filecopy */
+ DeviceInstallParams.cbSize = sizeof(SP_DEVINSTALL_PARAMS);
+ if (SetupDiGetDeviceInstallParams(hDeviceInfo, &DeviceInfoData, &DeviceInstallParams))
+ {
+ DeviceInstallParams.Flags |= DI_NOFILECOPY;
+ if (!SetupDiSetDeviceInstallParamsW(hDeviceInfo, &DeviceInfoData, &DeviceInstallParams))
+ SetErrBreak(("SetupDiSetDeviceInstallParamsW failed (%Rwc)", GetLastError()));
+ }
+ /** @todo r=bird: Why isn't SetupDiGetDeviceInstallParams failure fatal here? */
+
+ /*
+ * Register any device-specific co-installers for this device,
+ */
+ if (!SetupDiCallClassInstaller(DIF_REGISTER_COINSTALLERS, hDeviceInfo, &DeviceInfoData))
+ SetErrBreak(("SetupDiCallClassInstaller (DIF_REGISTER_COINSTALLERS) failed (%Rwc)", GetLastError()));
+
+ /*
+ * install any installer-specified interfaces.
+ * and then do the real install
+ */
+ if (!SetupDiCallClassInstaller(DIF_INSTALLINTERFACES, hDeviceInfo, &DeviceInfoData))
+ SetErrBreak(("SetupDiCallClassInstaller (DIF_INSTALLINTERFACES) failed (%Rwc)", GetLastError()));
+
+ if (!SetupDiCallClassInstaller(DIF_INSTALLDEVICE, hDeviceInfo, &DeviceInfoData))
+ SetErrBreak(("SetupDiCallClassInstaller (DIF_INSTALLDEVICE) failed (%Rwc)", GetLastError()));
+
+ /*
+ * Query the instance ID; on Windows 10, the registry key may take a short
+ * while to appear. Microsoft recommends waiting for up to 5 seconds, but
+ * we want to be on the safe side, so let's wait for 20 seconds. Waiting
+ * longer is harmful as network setup service will shut down after a period
+ * of inactivity.
+ */
+ for (int retries = 0; retries < 2 * 20; ++retries)
+ {
+ Sleep(500); /* half second */
+
+ /* Figure out NetCfgInstanceId */
+ hkey = SetupDiOpenDevRegKey(hDeviceInfo, &DeviceInfoData, DICS_FLAG_GLOBAL, 0, DIREG_DRV, KEY_READ);
+ if (hkey == INVALID_HANDLE_VALUE)
+ break;
+
+ DWORD cbSize = sizeof(wszCfgGuidString);
+ DWORD dwValueType = 0;
+ lrcRet = RegQueryValueExW(hkey, L"NetCfgInstanceId", NULL, &dwValueType, (LPBYTE)wszCfgGuidString, &cbSize);
+ /* As long as the return code is FILE_NOT_FOUND, sleep and retry. */
+ if (lrcRet != ERROR_FILE_NOT_FOUND)
+ break;
+
+ RegCloseKey(hkey);
+ hkey = (HKEY)INVALID_HANDLE_VALUE;
+ }
+
+ if (lrcRet == ERROR_FILE_NOT_FOUND)
+ {
+ hrc = E_ABORT;
+ break;
+ }
+
+ /*
+ * We need to check 'hkey' after we check 'lrcRet' to distinguish the case
+ * of failed SetupDiOpenDevRegKey from the case when we timed out.
+ */
+ if (hkey == INVALID_HANDLE_VALUE)
+ SetErrBreak(("SetupDiOpenDevRegKey failed (%Rwc)", GetLastError()));
+
+ if (lrcRet != ERROR_SUCCESS)
+ SetErrBreak(("Querying NetCfgInstanceId failed (%Rwc)", lrcRet));
+
+ NET_LUID luid;
+ HRESULT hrcSMRes = vboxNetCfgWinGetInterfaceLUID(hkey, &luid);
+
+ /* Close the key as soon as possible. See @bugref{7973}. */
+ RegCloseKey(hkey);
+ hkey = (HKEY)INVALID_HANDLE_VALUE;
+
+ if (FAILED(hrcSMRes))
+ {
+ /*
+ * The setting of Metric is not very important functionality,
+ * So we will not break installation process due to this error.
+ */
+ NonStandardLogFlow(("vboxNetCfgWinCreateHostOnlyNetworkInterface: Warning! "
+ "vboxNetCfgWinGetInterfaceLUID failed, default metric for new interface will not be set: %Rhrc\n",
+ hrcSMRes));
+ }
+ else
+ {
+ /*
+ * Set default metric value of interface to fix multicast issue
+ * See @bugref{6379} for details.
+ */
+ hrcSMRes = vboxNetCfgWinSetupMetric(&luid);
+ if (FAILED(hrcSMRes))
+ {
+ /*
+ * The setting of Metric is not very important functionality,
+ * So we will not break installation process due to this error.
+ */
+ NonStandardLogFlow(("vboxNetCfgWinCreateHostOnlyNetworkInterface: Warning! "
+ "vboxNetCfgWinSetupMetric failed, default metric for new interface will not be set: %Rhrc\n",
+ hrcSMRes));
+ }
+ }
+
+ /*
+ * We need to query the device name after we have succeeded in querying its
+ * instance ID to avoid similar waiting-and-retrying loop (see @bugref{7973}).
+ */
+ if (!SetupDiGetDeviceRegistryPropertyW(hDeviceInfo, &DeviceInfoData,
+ SPDRP_FRIENDLYNAME , /* IN DWORD Property,*/
+ NULL, /*OUT PDWORD PropertyRegDataType, OPTIONAL*/
+ (PBYTE)wszDevName, /*OUT PBYTE PropertyBuffer,*/
+ sizeof(wszDevName) - sizeof(WCHAR), /* IN DWORD PropertyBufferSize,*/
+ NULL /*OUT PDWORD RequiredSize OPTIONAL*/ ))
+ {
+ DWORD dwErr = GetLastError();
+ if (dwErr != ERROR_INVALID_DATA)
+ SetErrBreak(("SetupDiGetDeviceRegistryProperty failed (%Rwc)", dwErr));
+
+ RT_ZERO(wszDevName);
+ if (!SetupDiGetDeviceRegistryPropertyW(hDeviceInfo, &DeviceInfoData,
+ SPDRP_DEVICEDESC, /* IN DWORD Property,*/
+ NULL, /*OUT PDWORD PropertyRegDataType, OPTIONAL*/
+ (PBYTE)wszDevName, /*OUT PBYTE PropertyBuffer,*/
+ sizeof(wszDevName) - sizeof(WCHAR), /* IN DWORD PropertyBufferSize,*/
+ NULL /*OUT PDWORD RequiredSize OPTIONAL*/ ))
+ SetErrBreak(("SetupDiGetDeviceRegistryProperty failed (%Rwc)", GetLastError()));
+ }
+
+ /* No need to rename the device if the names match. */
+ if (RTUtf16Cmp(bstrNewInterfaceName.raw(), wszDevName) == 0)
+ bstrNewInterfaceName.setNull();
+
+#ifdef VBOXNETCFG_DELAYEDRENAME
+ /* Re-use wszDevName for device instance id retrieval. */
+ DWORD cwcReturned = 0;
+ RT_ZERO(wszDevName);
+ if (!SetupDiGetDeviceInstanceIdW(hDeviceInfo, &DeviceInfoData, wszDevName, RT_ELEMENTS(wszDevName) - 1, &cwcReturned))
+ SetErrBreak(("SetupDiGetDeviceInstanceId failed (%Rwc)", GetLastError()));
+#endif /* VBOXNETCFG_DELAYEDRENAME */
+ } while (0);
+
+ /*
+ * Cleanup.
+ */
+ if (hkey != INVALID_HANDLE_VALUE)
+ RegCloseKey(hkey);
+
+ if (pQueueCallbackContext)
+ SetupTermDefaultQueueCallback(pQueueCallbackContext);
+
+ if (hDeviceInfo != INVALID_HANDLE_VALUE)
+ {
+ /* an error has occurred, but the device is registered, we must remove it */
+ if (lrcRet != ERROR_SUCCESS && fRegistered)
+ SetupDiCallClassInstaller(DIF_REMOVE, hDeviceInfo, &DeviceInfoData);
+
+ SetupDiDeleteDeviceInfo(hDeviceInfo, &DeviceInfoData);
+
+ /* destroy the driver info list */
+#if 0
+ /* I've remove this, as I was consistently getting crashes in
+ * SetupDiDestroyDeviceInfoList otherwise during MSI installation
+ * (W10 build 19044, VBox r153431 + nocrt changes).
+ *
+ * Some details from windbg:
+ *
+ * (175e8.1596c): Access violation - code c0000005 (first chance)
+ * First chance exceptions are reported before any exception handling.
+ * This exception may be expected and handled.
+ * SETUPAPI!DereferenceClassDriverList+0x4e:
+ * 00007ffa`83e2a42a 834008ff add dword ptr [rax+8],0FFFFFFFFh ds:00000000`00000008=????????
+ * 0:006> k
+ * # Child-SP RetAddr Call Site
+ * 00 0000007e`ccd7c070 00007ffa`83e2a287 SETUPAPI!DereferenceClassDriverList+0x4e
+ * 01 0000007e`ccd7c0a0 00007ffa`56b96bd3 SETUPAPI!SetupDiDestroyDriverInfoList+0x117
+ * 02 0000007e`ccd7c0f0 00007ffa`56b982a3 MSIF170!vboxNetCfgWinCreateHostOnlyNetworkInterface+0xb23 [E:\vbox\svn\trunk\src\VBox\HostDrivers\VBoxNetFlt\win\cfg\VBoxNetCfg.cpp @ 3378]
+ * 03 0000007e`ccd7ef10 00007ffa`56b92cb8 MSIF170!VBoxNetCfgWinCreateHostOnlyNetworkInterface+0x53 [E:\vbox\svn\trunk\src\VBox\HostDrivers\VBoxNetFlt\win\cfg\VBoxNetCfg.cpp @ 3479]
+ * 04 0000007e`ccd7efc0 00007ffa`610f59d3 MSIF170!_createHostOnlyInterface+0x218 [E:\vbox\svn\trunk\src\VBox\Installer\win\InstallHelper\VBoxInstallHelper.cpp @ 1453]
+ * 05 0000007e`ccd7f260 00007ffa`610d80ac msi!CallCustomDllEntrypoint+0x2b
+ * 06 0000007e`ccd7f2d0 00007ffa`84567034 msi!CMsiCustomAction::CustomActionThread+0x34c
+ * 07 0000007e`ccd7f8c0 00007ffa`849a2651 KERNEL32!BaseThreadInitThunk+0x14
+ * 08 0000007e`ccd7f8f0 00000000`00000000 ntdll!RtlUserThreadStart+0x21
+ * 0:006> r
+ * rax=0000000000000000 rbx=0000025f4f03af90 rcx=00007ffa83f23b40
+ * rdx=0000025f4f03af90 rsi=0000025f5108be50 rdi=0000025f4f066f10
+ * rip=00007ffa83e2a42a rsp=0000007eccd7c070 rbp=00007ffa83f23000
+ * r8=0000007eccd7c0a8 r9=0000007eccd7c1f0 r10=0000000000000000
+ * r11=0000007eccd7c020 r12=0000007eccd7c3d0 r13=00007ffa83f23000
+ * r14=0000000000000000 r15=0000025f4f03af90
+ * iopl=0 nv up ei pl nz na po nc
+ * cs=0033 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00010206
+ * SETUPAPI!DereferenceClassDriverList+0x4e:
+ * 00007ffa`83e2a42a 834008ff add dword ptr [rax+8],0FFFFFFFFh ds:00000000`00000008=????????
+ *
+ */
+ if (destroyList)
+ SetupDiDestroyDriverInfoList(hDeviceInfo, &DeviceInfoData, SPDIT_CLASSDRIVER);
+#else
+ RT_NOREF(destroyList);
+#endif
+
+ /* clean up the device info set */
+ SetupDiDestroyDeviceInfoList(hDeviceInfo);
+ }
+
+ /*
+ * Return the network connection GUID on success.
+ */
+ if (SUCCEEDED(hrc))
+ {
+ /** @todo r=bird: Some please explain this mess. It's not just returning a
+ * GUID here, it's a bit more than that. */
+ RENAMING_CONTEXT context;
+ context.hr = E_FAIL;
+
+ if (pGuid)
+ {
+ hrc = CLSIDFromString(wszCfgGuidString, (LPCLSID)pGuid);
+ if (FAILED(hrc))
+ NonStandardLogFlow(("CLSIDFromString failed, hrc (0x%x)\n", hrc));
+ }
+
+ INetCfg *pNetCfg = NULL;
+ LPWSTR pwszApp = NULL;
+ HRESULT hrc2 = VBoxNetCfgWinQueryINetCfg(&pNetCfg, TRUE, L"VirtualBox Host-Only Creation",
+ 30 * 1000, /* on Vista we often get 6to4svc.dll holding the lock, wait for 30 sec. */
+ /** @todo special handling for 6to4svc.dll ???, i.e. several retrieves */
+ &pwszApp);
+ if (hrc2 == S_OK)
+ {
+ if (bstrNewInterfaceName.isNotEmpty())
+ {
+ /* The assigned name does not match the desired one, rename the device */
+ context.bstrName = bstrNewInterfaceName.raw();
+ context.pGuid = pGuid;
+ hrc2 = vboxNetCfgWinEnumNetCfgComponents(pNetCfg, &GUID_DEVCLASS_NET,
+ vboxNetCfgWinRenameHostOnlyNetworkInterface, &context);
+ }
+ if (SUCCEEDED(hrc2))
+ hrc2 = vboxNetCfgWinEnumNetCfgComponents(pNetCfg, &GUID_DEVCLASS_NETSERVICE,
+ vboxNetCfgWinAdjustHostOnlyNetworkInterfacePriority, pGuid);
+ if (SUCCEEDED(hrc2))
+ hrc2 = vboxNetCfgWinEnumNetCfgComponents(pNetCfg, &GUID_DEVCLASS_NETTRANS,
+ vboxNetCfgWinAdjustHostOnlyNetworkInterfacePriority, pGuid);
+ if (SUCCEEDED(hrc2))
+ hrc2 = vboxNetCfgWinEnumNetCfgComponents(pNetCfg, &GUID_DEVCLASS_NETCLIENT,
+ vboxNetCfgWinAdjustHostOnlyNetworkInterfacePriority, pGuid);
+ if (SUCCEEDED(hrc2))
+ hrc2 = pNetCfg->Apply();
+ else
+ NonStandardLogFlow(("Enumeration failed, hrc2=%Rhrc\n", hrc2));
+
+ VBoxNetCfgWinReleaseINetCfg(pNetCfg, TRUE);
+ }
+ else if (hrc2 == NETCFG_E_NO_WRITE_LOCK && pwszApp)
+ {
+ NonStandardLogFlow(("Application '%ls' is holding the lock, failed\n", pwszApp));
+ CoTaskMemFree(pwszApp);
+ pwszApp = NULL;
+ }
+ else
+ NonStandardLogFlow(("VBoxNetCfgWinQueryINetCfg failed, hrc2=%Rhrc\n", hrc2));
+
+#ifndef VBOXNETCFG_DELAYEDRENAME
+ /* If the device has been successfully renamed, replace the name now. */
+ if (SUCCEEDED(hrc2) && SUCCEEDED(context.hr))
+ RTUtf16Copy(wszDevName, RT_ELEMENTS(wszDevName), pBstrDesiredName);
+
+ WCHAR wszConnectionName[128];
+ hrc2 = VBoxNetCfgWinGenHostonlyConnectionName(wszDevName, wszConnectionName, RT_ELEMENTS(wszConnectionName), NULL);
+ if (SUCCEEDED(hrc2))
+ hrc2 = VBoxNetCfgWinRenameConnection(wszCfgGuidString, wszConnectionName);
+#endif
+
+ /*
+ * Now, return the network connection GUID/name.
+ */
+ if (pBstrName)
+ {
+ *pBstrName = SysAllocString((const OLECHAR *)wszDevName);
+ if (!*pBstrName)
+ {
+ NonStandardLogFlow(("SysAllocString failed\n"));
+ hrc = E_OUTOFMEMORY;
+ }
+ }
+ }
+
+ if (pBstrErrMsg)
+ {
+ *pBstrErrMsg = NULL;
+ if (bstrError.isNotEmpty())
+ bstrError.detachToEx(pBstrErrMsg);
+ }
+ return hrc;
+}
+
+VBOXNETCFGWIN_DECL(HRESULT) VBoxNetCfgWinCreateHostOnlyNetworkInterface(IN LPCWSTR pwszInfPath, IN bool fIsInfPathFile,
+ IN BSTR pwszDesiredName, OUT GUID *pGuid,
+ OUT BSTR *pBstrName, OUT BSTR *pBstrErrMsg)
+{
+ HRESULT hrc = vboxNetCfgWinCreateHostOnlyNetworkInterface(pwszInfPath, fIsInfPathFile, pwszDesiredName,
+ pGuid, pBstrName, pBstrErrMsg);
+ if (hrc == E_ABORT)
+ {
+ NonStandardLogFlow(("Timed out while waiting for NetCfgInstanceId, try again immediately...\n"));
+
+ /*
+ * This is the first time we fail to obtain NetCfgInstanceId, let us
+ * retry it once. It is needed to handle the situation when network
+ * setup fails to recognize the arrival of our device node while it
+ * is busy removing another host-only interface, and it gets stuck
+ * with no matching network interface created for our device node.
+ * See @bugref{7973} for details.
+ */
+ hrc = vboxNetCfgWinCreateHostOnlyNetworkInterface(pwszInfPath, fIsInfPathFile, pwszDesiredName,
+ pGuid, pBstrName, pBstrErrMsg);
+ if (hrc == E_ABORT)
+ {
+ NonStandardLogFlow(("Timed out again while waiting for NetCfgInstanceId, try again after a while...\n"));
+
+ /*
+ * This is the second time we fail to obtain NetCfgInstanceId, let us
+ * retry it once more. This time we wait to network setup service
+ * to go down before retrying. Hopefully it will resolve all error
+ * conditions. See @bugref{7973} for details.
+ */
+ SC_HANDLE hSCM = OpenSCManagerW(NULL, NULL, GENERIC_READ);
+ if (hSCM)
+ {
+ SC_HANDLE hService = OpenServiceW(hSCM, L"NetSetupSvc", GENERIC_READ);
+ if (hService)
+ {
+ for (int retries = 0; retries < 60 && !vboxNetCfgWinIsNetSetupStopped(hService); ++retries)
+ Sleep(1000);
+ CloseServiceHandle(hService);
+ hrc = vboxNetCfgWinCreateHostOnlyNetworkInterface(pwszInfPath, fIsInfPathFile, pwszDesiredName,
+ pGuid, pBstrName, pBstrErrMsg);
+ }
+ else
+ NonStandardLogFlow(("OpenService failed (%Rwc)\n", GetLastError()));
+ CloseServiceHandle(hSCM);
+ }
+ else
+ NonStandardLogFlow(("OpenSCManager failed (%Rwc)", GetLastError()));
+
+ /* Give up and report the error. */
+ if (hrc == E_ABORT)
+ {
+ if (pBstrErrMsg)
+ {
+ com::Bstr bstrError;
+ bstrError.printfNoThrow("Querying NetCfgInstanceId failed (ERROR_FILE_NOT_FOUND)");
+ bstrError.detachToEx(pBstrErrMsg);
+ }
+ hrc = E_FAIL;
+ }
+ }
+ }
+ return hrc;
+}
+
+static HRESULT vboxNetCfgWinGetLoopbackMetric(OUT DWORD *Metric)
+{
+ Assert(g_pfnInitializeIpInterfaceEntry != NULL);
+ Assert(g_pfnGetIpInterfaceEntry != NULL);
+
+ MIB_IPINTERFACE_ROW row;
+ RT_ZERO(row); /* paranoia */
+ g_pfnInitializeIpInterfaceEntry(&row);
+
+ row.Family = AF_INET;
+ row.InterfaceLuid.Info.IfType = IF_TYPE_SOFTWARE_LOOPBACK;
+
+ NETIO_STATUS dwErr = g_pfnGetIpInterfaceEntry(&row);
+ if (dwErr == NO_ERROR)
+ {
+ *Metric = row.Metric;
+ return S_OK;
+ }
+ return HRESULT_FROM_WIN32(dwErr);
+}
+
+static HRESULT vboxNetCfgWinSetInterfaceMetric(IN NET_LUID *pInterfaceLuid, IN DWORD metric)
+{
+ Assert(g_pfnInitializeIpInterfaceEntry != NULL);
+ Assert(g_pfnSetIpInterfaceEntry != NULL);
+
+ MIB_IPINTERFACE_ROW newRow;
+ RT_ZERO(newRow); /* paranoia */
+ g_pfnInitializeIpInterfaceEntry(&newRow);
+
+ // identificate the interface to change
+ newRow.InterfaceLuid = *pInterfaceLuid;
+ newRow.Family = AF_INET;
+
+ // changed settings
+ newRow.UseAutomaticMetric = false;
+ newRow.Metric = metric;
+
+ // change settings
+ NETIO_STATUS dwErr = g_pfnSetIpInterfaceEntry(&newRow);
+ if (dwErr == NO_ERROR)
+ return S_OK;
+ return HRESULT_FROM_WIN32(dwErr);
+}
+
+static HRESULT vboxNetCfgWinSetupMetric(IN NET_LUID* pLuid)
+{
+ HRESULT hrc = E_FAIL;
+ HMODULE hmod = loadSystemDll(L"Iphlpapi.dll");
+ if (hmod)
+ {
+ g_pfnInitializeIpInterfaceEntry = (PFNINITIALIZEIPINTERFACEENTRY)GetProcAddress(hmod, "InitializeIpInterfaceEntry");
+ g_pfnGetIpInterfaceEntry = (PFNGETIPINTERFACEENTRY) GetProcAddress(hmod, "GetIpInterfaceEntry");
+ g_pfnSetIpInterfaceEntry = (PFNSETIPINTERFACEENTRY) GetProcAddress(hmod, "SetIpInterfaceEntry");
+ Assert(g_pfnInitializeIpInterfaceEntry);
+ Assert(g_pfnGetIpInterfaceEntry);
+ Assert(g_pfnSetIpInterfaceEntry);
+
+ if ( g_pfnInitializeIpInterfaceEntry
+ && g_pfnGetIpInterfaceEntry
+ && g_pfnSetIpInterfaceEntry)
+ {
+ DWORD loopbackMetric = 0;
+ hrc = vboxNetCfgWinGetLoopbackMetric(&loopbackMetric);
+ if (SUCCEEDED(hrc))
+ hrc = vboxNetCfgWinSetInterfaceMetric(pLuid, loopbackMetric - 1);
+ }
+
+ g_pfnInitializeIpInterfaceEntry = NULL;
+ g_pfnSetIpInterfaceEntry = NULL;
+ g_pfnGetIpInterfaceEntry = NULL;
+
+ FreeLibrary(hmod);
+ }
+ return hrc;
+}
+
+static HRESULT vboxNetCfgWinGetInterfaceLUID(IN HKEY hKey, OUT NET_LUID *pLUID)
+{
+ if (pLUID == NULL)
+ return E_INVALIDARG;
+
+ DWORD dwLuidIndex = 0;
+ DWORD cbSize = sizeof(dwLuidIndex);
+ DWORD dwValueType = REG_DWORD; /** @todo r=bird: This is output only. No checked after the call. So, only for debugging? */
+ LSTATUS lrc = RegQueryValueExW(hKey, L"NetLuidIndex", NULL, &dwValueType, (LPBYTE)&dwLuidIndex, &cbSize);
+ if (lrc == ERROR_SUCCESS)
+ {
+ DWORD dwIfType = 0;
+ cbSize = sizeof(dwIfType);
+ dwValueType = REG_DWORD; /** @todo r=bird: This is output only. No checked after the call. So, only for debugging? */
+ lrc = RegQueryValueExW(hKey, L"*IfType", NULL, &dwValueType, (LPBYTE)&dwIfType, &cbSize);
+ if (lrc == ERROR_SUCCESS)
+ {
+ RT_ZERO(*pLUID);
+ pLUID->Info.IfType = dwIfType;
+ pLUID->Info.NetLuidIndex = dwLuidIndex;
+ return S_OK;
+ }
+ }
+
+ RT_ZERO(*pLUID);
+ return HRESULT_FROM_WIN32(lrc);
+}
+
+
+#ifdef VBOXNETCFG_DELAYEDRENAME
+VBOXNETCFGWIN_DECL(HRESULT) VBoxNetCfgWinRenameHostOnlyConnection(IN const GUID *pGuid, IN LPCWSTR pwszId, OUT BSTR *pDevName)
+{
+ if (pDevName)
+ *pDevName = NULL;
+
+ HRESULT hr = S_OK; /** @todo r=bird: ODD return status for SetupDiCreateDeviceInfoList failures! */
+ HDEVINFO hDevInfo = SetupDiCreateDeviceInfoList(&GUID_DEVCLASS_NET, NULL);
+ if (hDevInfo != INVALID_HANDLE_VALUE)
+ {
+ SP_DEVINFO_DATA DevInfoData = { sizeof(SP_DEVINFO_DATA) };
+ if (SetupDiOpenDeviceInfo(hDevInfo, pwszId, NULL, 0, &DevInfoData))
+ {
+ WCHAR wszDevName[256 + 1] = {0};
+ DWORD err = ERROR_SUCCESS;
+ if (!SetupDiGetDeviceRegistryPropertyW(hDevInfo, &DevInfoData, SPDRP_FRIENDLYNAME, NULL /*PropertyRegDataType*/,
+ (PBYTE)wszDevName, sizeof(wszDevName) - sizeof(WCHAR) /*yes, bytes*/,
+ NULL /*RequiredSize*/))
+ {
+ err = GetLastError();
+ if (err == ERROR_INVALID_DATA)
+ {
+ RT_ZERO(wszDevName);
+ if (SetupDiGetDeviceRegistryPropertyW(hDevInfo, &DevInfoData, SPDRP_DEVICEDESC, NULL /*PropertyRegDataType*/,
+ (PBYTE)wszDevName, sizeof(wszDevName) - sizeof(WCHAR) /*yes, bytes*/,
+ NULL /*RequiredSize*/))
+ err = ERROR_SUCCESS;
+ else
+ err = GetLastError();
+ }
+ }
+ if (err == ERROR_SUCCESS)
+ {
+ WCHAR wszConnectionNewName[128] = {0};
+ hr = VBoxNetCfgWinGenHostonlyConnectionName(wszDevName, wszConnectionNewName,
+ RT_ELEMENTS(wszConnectionNewName), NULL);
+ if (SUCCEEDED(hr))
+ {
+ WCHAR wszGuid[50];
+ int cbWGuid = StringFromGUID2(*pGuid, wszGuid, RT_ELEMENTS(wszGuid));
+ if (cbWGuid)
+ {
+ hr = VBoxNetCfgWinRenameConnection(wszGuid, wszConnectionNewName);
+ if (FAILED(hr))
+ NonStandardLogFlow(("VBoxNetCfgWinRenameHostOnlyConnection: VBoxNetCfgWinRenameConnection failed (0x%x)\n", hr));
+ }
+ else
+ {
+ err = GetLastError();
+ hr = HRESULT_FROM_WIN32(err);
+ if (SUCCEEDED(hr))
+ hr = E_FAIL;
+ NonStandardLogFlow(("StringFromGUID2 failed err=%u, hr=0x%x\n", err, hr));
+ }
+ }
+ else
+ NonStandardLogFlow(("VBoxNetCfgWinRenameHostOnlyConnection: VBoxNetCfgWinGenHostonlyConnectionName failed (0x%x)\n", hr));
+ if (SUCCEEDED(hr) && pDevName)
+ {
+ *pDevName = SysAllocString((const OLECHAR *)wszDevName);
+ if (!*pDevName)
+ {
+ NonStandardLogFlow(("SysAllocString failed\n"));
+ hr = HRESULT_FROM_WIN32(ERROR_NOT_ENOUGH_MEMORY);
+ }
+ }
+ }
+ else
+ {
+ hr = HRESULT_FROM_WIN32(err);
+ NonStandardLogFlow(("VBoxNetCfgWinRenameHostOnlyConnection: SetupDiGetDeviceRegistryPropertyW failed (0x%x)\n", err));
+ }
+ }
+ else
+ {
+ DWORD err = GetLastError();
+ hr = HRESULT_FROM_WIN32(err);
+ NonStandardLogFlow(("VBoxNetCfgWinRenameHostOnlyConnection: SetupDiOpenDeviceInfo failed (0x%x)\n", err));
+ }
+ SetupDiDestroyDeviceInfoList(hDevInfo);
+ }
+
+ return hr;
+}
+#endif /* VBOXNETCFG_DELAYEDRENAME */
+
+#undef SetErrBreak
+
diff --git a/src/VBox/HostDrivers/VBoxNetFlt/win/drv/Makefile.kup b/src/VBox/HostDrivers/VBoxNetFlt/win/drv/Makefile.kup
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/src/VBox/HostDrivers/VBoxNetFlt/win/drv/Makefile.kup
diff --git a/src/VBox/HostDrivers/VBoxNetFlt/win/drv/VBoxNetAdp.inf b/src/VBox/HostDrivers/VBoxNetFlt/win/drv/VBoxNetAdp.inf
new file mode 100644
index 00000000..9e13a198
--- /dev/null
+++ b/src/VBox/HostDrivers/VBoxNetFlt/win/drv/VBoxNetAdp.inf
@@ -0,0 +1,95 @@
+; $Id: VBoxNetAdp.inf $
+;; @file
+; VBoxNetAdp.inf - VirtualBox Host-Only Driver inf file
+;
+
+;
+; Copyright (C) 2011-2023 Oracle and/or its affiliates.
+;
+; This file is part of VirtualBox base platform packages, as
+; available from https://www.virtualbox.org.
+;
+; This program is free software; you can redistribute it and/or
+; modify it under the terms of the GNU General Public License
+; as published by the Free Software Foundation, in version 3 of the
+; License.
+;
+; This program is distributed in the hope that it will be useful, but
+; WITHOUT ANY WARRANTY; without even the implied warranty of
+; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+; General Public License for more details.
+;
+; You should have received a copy of the GNU General Public License
+; along with this program; if not, see <https://www.gnu.org/licenses>.
+;
+; The contents of this file may alternatively be used under the terms
+; of the Common Development and Distribution License Version 1.0
+; (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+; in the VirtualBox distribution, in which case the provisions of the
+; CDDL are applicable instead of those of the GPL.
+;
+; You may elect to license modified versions of this file under the
+; terms and conditions of either the GPL or the CDDL or both.
+;
+; SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+;
+
+[Version]
+signature = "$Windows NT$"
+;cat CatalogFile = VBoxNetAdp.cat
+Class = Net
+ClassGUID = {4d36e972-e325-11ce-bfc1-08002be10318}
+Provider = %ORACLE%
+; DriverPackageType=Network
+; DriverPackageDisplayName=%VBoxNetAdp_Desc%
+;edit-DriverVer=08/13/2008,1.1.0.1
+
+[ControlFlags]
+;ExcludeFromSelect = sun_VBoxNetAdp
+
+[SourceDisksNames]
+1=%DiskDescription%,"",,
+
+[SourceDisksFiles]
+VBoxNetAdp.sys=1
+
+[DestinationDirs]
+DefaultDestDir = 12
+VBoxNetAdp.Files.Sys = 12 ; %windir%\System32\drivers
+
+[Manufacturer]
+%ORACLE% = VBoxNetAdp@COMMA-NT-ARCH@
+
+[VBoxNetAdp@DOT-NT-ARCH@]
+%VBoxNetAdp_Desc% = VBoxNetAdp.ndi, sun_VBoxNetAdp
+
+[VBoxNetAdp.ndi]
+Characteristics = 0x1 ; NCF_VIRTUAL
+CopyFiles = VBoxNetAdp.Files.Sys
+AddReg = VBoxNetAdp.AddReg
+
+[VBoxNetAdp.Files.Sys]
+VBoxNetAdp.sys,,,2
+
+[VBoxNetAdp.ndi.Services]
+AddService = VBoxNetAdp,0x2, VBoxNetAdp.AddService
+
+[VBoxNetAdp.AddService]
+DisplayName = %VBoxNetAdp_Desc%
+ServiceType = 1 ;SERVICE_KERNEL_DRIVER
+StartType = 3 ;SERVICE_DEMAND_START
+ErrorControl = 1 ;SERVICE_ERROR_NORMAL
+ServiceBinary = %12%\VBoxNetAdp.sys
+LoadOrderGroup = NDIS
+
+[VBoxNetAdp.AddReg]
+HKR, , *NdisDeviceType, 0x00010001, 1 ; NDIS_DEVICE_TYPE_ENDPOINT
+HKR, Ndi, Service, 0, "VBoxNetAdp"
+HKR, Ndi\Interfaces, UpperRange, 0, "ndis5"
+HKR, Ndi\Interfaces, LowerRange, 0, "ethernet"
+
+[Strings]
+ORACLE = "Oracle Corporation"
+VBoxNetAdp_Desc = "VirtualBox Host-Only Ethernet Adapter"
+DiskDescription = "VirtualBox Host-Only Ethernet Adapter"
+
diff --git a/src/VBox/HostDrivers/VBoxNetFlt/win/drv/VBoxNetFlt-win.rc b/src/VBox/HostDrivers/VBoxNetFlt/win/drv/VBoxNetFlt-win.rc
new file mode 100644
index 00000000..979fb6dd
--- /dev/null
+++ b/src/VBox/HostDrivers/VBoxNetFlt/win/drv/VBoxNetFlt-win.rc
@@ -0,0 +1,77 @@
+/* $Id: VBoxNetFlt-win.rc $ */
+/** @file
+ * VBoxNetFlt - Resource file containing version info and icon.
+ */
+/*
+ * Copyright (C) 2011-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+#include <windows.h>
+#include <VBox/version.h>
+
+#ifndef VBOXNETADP
+# define DESCRIPTION_STR "VirtualBox Bridged Networking Driver\0"
+# define FILENAME_STR "VBoxNetFlt"
+#else
+# define DESCRIPTION_STR "VirtualBox Host-Only Network Adapter Driver\0"
+# define FILENAME_STR "VBoxNetAdp"
+#endif
+
+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_NETWORK
+BEGIN
+ BLOCK "StringFileInfo"
+ BEGIN
+ BLOCK "040904b0" // Lang=US English, CharSet=Unicode
+ BEGIN
+ VALUE "CompanyName", VBOX_RC_COMPANY_NAME
+ VALUE "FileDescription", DESCRIPTION_STR
+ VALUE "FileVersion", VBOX_RC_FILE_VERSION_STR
+ VALUE "InternalName", FILENAME_STR "\0"
+ VALUE "LegalCopyright", VBOX_RC_LEGAL_COPYRIGHT
+ VALUE "OriginalFilename", FILENAME_STR ".sys\0"
+ VALUE "ProductName", VBOX_RC_PRODUCT_NAME_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/HostDrivers/VBoxNetFlt/win/drv/VBoxNetFlt.inf b/src/VBox/HostDrivers/VBoxNetFlt/win/drv/VBoxNetFlt.inf
new file mode 100644
index 00000000..22425793
--- /dev/null
+++ b/src/VBox/HostDrivers/VBoxNetFlt/win/drv/VBoxNetFlt.inf
@@ -0,0 +1,117 @@
+; $Id: VBoxNetFlt.inf $
+;; @file
+; VBoxNetFlt.inf - VirtualBox Bridged Networking Driver inf file Protocol edge
+;
+
+;
+; Copyright (C) 2011-2023 Oracle and/or its affiliates.
+;
+; This file is part of VirtualBox base platform packages, as
+; available from https://www.virtualbox.org.
+;
+; This program is free software; you can redistribute it and/or
+; modify it under the terms of the GNU General Public License
+; as published by the Free Software Foundation, in version 3 of the
+; License.
+;
+; This program is distributed in the hope that it will be useful, but
+; WITHOUT ANY WARRANTY; without even the implied warranty of
+; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+; General Public License for more details.
+;
+; You should have received a copy of the GNU General Public License
+; along with this program; if not, see <https://www.gnu.org/licenses>.
+;
+; The contents of this file may alternatively be used under the terms
+; of the Common Development and Distribution License Version 1.0
+; (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+; in the VirtualBox distribution, in which case the provisions of the
+; CDDL are applicable instead of those of the GPL.
+;
+; You may elect to license modified versions of this file under the
+; terms and conditions of either the GPL or the CDDL or both.
+;
+; SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+;
+
+[Version]
+Signature = "$Windows NT$"
+;cat CatalogFile = VBoxNetFlt.cat
+Class = NetService
+ClassGUID = {4D36E974-E325-11CE-BFC1-08002BE10318}
+Provider = %ORACLE%
+;DriverPackageType=Network
+;DriverPackageDisplayName=%VBoxNetFlt_Desc%
+;edit-DriverVer=08/13/2008,1.1.0.1
+
+
+[Manufacturer]
+%ORACLE% = VBoxNetFlt@COMMA-NT-ARCH@
+
+[ControlFlags]
+
+[VBoxNetFlt@DOT-NT-ARCH@]
+%VBoxNetFlt_Desc% = VBoxNetFlt.ndi, sun_VBoxNetFlt
+
+[VBoxNetFlt.ndi]
+AddReg = VBoxNetFlt.ndi.AddReg, VBoxNetFlt.AddReg
+Characteristics = 0x4410 ; NCF_FILTER | NCF_NDIS_PROTOCOL
+CopyFiles = VBoxNetFlt.Files.DLL, VBoxNetFlt.Files.Sys
+CopyInf = VBoxNetFltM.inf
+
+[VBoxNetFlt.ndi.Remove]
+DelFiles = VBoxNetFlt.Files.DLL, VBoxNetFlt.Files.Sys
+
+[VBoxNetFlt.ndi.Services]
+AddService = VBoxNetFlt,, VBoxNetFlt.AddService
+
+[VBoxNetFlt.AddService]
+DisplayName = %VBoxNetFltService_Desc%
+ServiceType = 1 ;SERVICE_KERNEL_DRIVER
+StartType = 3 ;SERVICE_DEMAND_START
+ErrorControl = 1 ;SERVICE_ERROR_NORMAL
+ServiceBinary = %12%\VBoxNetFlt.sys
+LoadOrderGroup = PNP_TDI
+AddReg = VBoxNetFlt.AddService.AddReg
+
+
+[VBoxNetFlt.AddService.AddReg]
+
+[SourceDisksNames]
+1=%DiskDescription%,"",,
+
+[SourceDisksFiles]
+VBoxNetFlt.sys=1
+VBoxNetFltNobj.dll=1
+
+[DestinationDirs]
+DefaultDestDir = 12
+VBoxNetFlt.Files.DLL = 11 ; %windir%\System32
+VBoxNetFlt.Files.Sys = 12 ; %windir%\System32\drivers
+
+[VBoxNetFlt.Files.Sys]
+VBoxNetFlt.sys,,,2
+
+[VBoxNetFlt.Files.DLL]
+VBoxNetFltNobj.dll,,,2
+
+[VBoxNetFlt.ndi.AddReg]
+HKR, Ndi, HelpText, , %VBoxNetFlt_HELP%
+HKR, Ndi, ClsID, 0, {f374d1a0-bf08-4bdc-9cb2-c15ddaeef955}
+HKR, Ndi, ComponentDll, , VBoxNetFltNobj.dll
+HKR, Ndi, FilterClass, , failover
+HKR, Ndi, FilterDeviceInfId, , sun_VBoxNetFltmp
+HKR, Ndi, Service, , VBoxNetFlt
+HKR, Ndi\Interfaces, UpperRange, , noupper
+HKR, Ndi\Interfaces, LowerRange, , nolower
+HKR, Ndi\Interfaces, FilterMediaTypes, , "ethernet, nolower"
+
+[VBoxNetFlt.AddReg]
+HKR, Parameters, Param1, 0, 4
+
+[Strings]
+ORACLE = "Oracle Corporation"
+DiskDescription = "VirtualBox Bridged Networking Driver"
+VBoxNetFlt_Desc = "VirtualBox Bridged Networking Driver"
+VBoxNetFlt_HELP = "VirtualBox Bridged Networking Driver"
+VBoxNetFltService_Desc = "VirtualBox Bridged Networking Service"
diff --git a/src/VBox/HostDrivers/VBoxNetFlt/win/drv/VBoxNetFltCmn-win.h b/src/VBox/HostDrivers/VBoxNetFlt/win/drv/VBoxNetFltCmn-win.h
new file mode 100644
index 00000000..520a7d7c
--- /dev/null
+++ b/src/VBox/HostDrivers/VBoxNetFlt/win/drv/VBoxNetFltCmn-win.h
@@ -0,0 +1,533 @@
+/* $Id: VBoxNetFltCmn-win.h $ */
+/** @file
+ * VBoxNetFltCmn-win.h - Bridged Networking Driver, Windows Specific Code.
+ * Common header with configuration defines and global defs
+ */
+
+/*
+ * Copyright (C) 2011-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+#ifndef VBOX_INCLUDED_SRC_VBoxNetFlt_win_drv_VBoxNetFltCmn_win_h
+#define VBOX_INCLUDED_SRC_VBoxNetFlt_win_drv_VBoxNetFltCmn_win_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+#define LOG_GROUP LOG_GROUP_NET_FLT_DRV
+
+/* debugging flags */
+#ifdef DEBUG
+//# define DEBUG_NETFLT_PACKETS
+# ifndef DEBUG_misha
+# define RT_NO_STRICT
+# endif
+/* # define DEBUG_NETFLT_LOOPBACK */
+/* receive logic has several branches */
+/* the DEBUG_NETFLT_RECV* macros used to debug the ProtocolReceive callback
+ * which is typically not used in case the underlying miniport indicates the packets with NdisMIndicateReceivePacket
+ * the best way to debug the ProtocolReceive (which in turn has several branches) is to enable the DEBUG_NETFLT_RECV
+ * one by one in the below order, i.e.
+ * first DEBUG_NETFLT_RECV
+ * then DEBUG_NETFLT_RECV + DEBUG_NETFLT_RECV_NOPACKET */
+//# define DEBUG_NETFLT_RECV
+//# define DEBUG_NETFLT_RECV_NOPACKET
+//# define DEBUG_NETFLT_RECV_TRANSFERDATA
+/* use ExAllocatePoolWithTag instead of NdisAllocateMemoryWithTag */
+// #define DEBUG_NETFLT_USE_EXALLOC
+#endif
+
+#include <VBox/intnet.h>
+#include <VBox/log.h>
+#include <VBox/err.h>
+#include <VBox/version.h>
+#include <iprt/initterm.h>
+#include <iprt/assert.h>
+#include <iprt/spinlock.h>
+#include <iprt/semaphore.h>
+#include <iprt/process.h>
+#include <iprt/alloc.h>
+#include <iprt/alloca.h>
+#include <iprt/time.h>
+#include <iprt/net.h>
+#include <iprt/list.h>
+
+#include <iprt/nt/ntddk.h>
+#include <iprt/nt/ndis.h>
+
+#define VBOXNETFLT_OS_SPECFIC 1
+
+/** version
+ * NOTE: we are NOT using NDIS 5.1 features now */
+#ifdef NDIS51_MINIPORT
+# define VBOXNETFLT_VERSION_MP_NDIS_MAJOR 5
+# define VBOXNETFLT_VERSION_MP_NDIS_MINOR 1
+#else
+# define VBOXNETFLT_VERSION_MP_NDIS_MAJOR 5
+# define VBOXNETFLT_VERSION_MP_NDIS_MINOR 0
+#endif
+
+#ifndef VBOXNETADP
+#ifdef NDIS51
+# define VBOXNETFLT_VERSION_PT_NDIS_MAJOR 5
+# define VBOXNETFLT_VERSION_PT_NDIS_MINOR 1 /* todo: use 0 here as well ? */
+#else
+# define VBOXNETFLT_VERSION_PT_NDIS_MAJOR 5
+# define VBOXNETFLT_VERSION_PT_NDIS_MINOR 0
+#endif
+
+# define VBOXNETFLT_NAME_PROTOCOL L"VBoxNetFlt"
+/** device to be used to prevent the driver unload & ioctl interface (if necessary in the future) */
+# define VBOXNETFLT_NAME_LINK L"\\DosDevices\\Global\\VBoxNetFlt"
+# define VBOXNETFLT_NAME_DEVICE L"\\Device\\VBoxNetFlt"
+#else
+# define VBOXNETFLT_NAME_LINK L"\\DosDevices\\Global\\VBoxNetAdp"
+# define VBOXNETFLT_NAME_DEVICE L"\\Device\\VBoxNetAdp"
+#endif
+
+typedef struct VBOXNETFLTINS *PVBOXNETFLTINS;
+
+/** configuration */
+
+/** Ndis Packet pool settings
+ * these are applied to both receive and send packet pools */
+/* number of packets for normal used */
+#define VBOXNETFLT_PACKET_POOL_SIZE_NORMAL 0x000000FF
+/* number of additional overflow packets */
+#define VBOXNETFLT_PACKET_POOL_SIZE_OVERFLOW 0x0000FF00
+
+/** packet queue size used when the driver is working in the "active" mode */
+#define VBOXNETFLT_PACKET_INFO_POOL_SIZE 0x0000FFFF
+
+#ifndef VBOXNETADP
+/** memory tag used for memory allocations
+ * (VBNF stands for VBox NetFlt) */
+# define VBOXNETFLT_MEM_TAG 'FNBV'
+#else
+/** memory tag used for memory allocations
+ * (VBNA stands for VBox NetAdp) */
+# define VBOXNETFLT_MEM_TAG 'ANBV'
+#endif
+
+/** receive and transmit Ndis buffer pool size */
+#define VBOXNETFLT_BUFFER_POOL_SIZE_TX 128
+#define VBOXNETFLT_BUFFER_POOL_SIZE_RX 128
+
+#define VBOXNETFLT_PACKET_ETHEADER_SIZE 14
+#define VBOXNETFLT_PACKET_HEADER_MATCH_SIZE 24
+#define VBOXNETFLT_PACKET_QUEUE_SG_SEGS_ALLOC 32
+
+
+#if defined(DEBUG_NETFLT_PACKETS) || !defined(VBOX_LOOPBACK_USEFLAGS)
+# define VBOXNETFLT_PACKETMATCH_LENGTH (VBOXNETFLT_PACKET_ETHEADER_SIZE + 2)
+#endif
+
+#ifdef VBOXNETADP
+#define VBOXNETADP_HEADER_SIZE 14
+#define VBOXNETADP_MAX_DATA_SIZE 1500
+#define VBOXNETADP_MAX_PACKET_SIZE (VBOXNETADP_HEADER_SIZE + VBOXNETADP_MAX_DATA_SIZE)
+#define VBOXNETADP_MIN_PACKET_SIZE 60
+/* link speed 100Mbps (measured in 100 bps) */
+#define VBOXNETADP_LINK_SPEED 1000000
+#define VBOXNETADP_MAX_LOOKAHEAD_SIZE VBOXNETADP_MAX_DATA_SIZE
+#define VBOXNETADP_VENDOR_ID 0x080027
+#define VBOXNETADP_VENDOR_DRIVER_VERSION 0x00010000
+#define VBOXNETADP_VENDOR_DESC "Sun"
+#define VBOXNETADP_MAX_MCAST_LIST 32
+#define VBOXNETADP_ETH_ADDRESS_LENGTH 6
+
+//#define VBOXNETADP_REPORT_DISCONNECTED
+#endif
+/* type defs */
+
+/** Flag specifying that the type of enqueued packet
+ * if set the info contains the PINTNETSG packet
+ * if clear the packet info contains the PNDIS_PACKET packet
+ * Typically the packet queue we are maintaining contains PNDIS_PACKETs only,
+ * however in case the underlying miniport indicates a packet with the NDIS_STATUS_RESOURCES status
+ * we MUST return the packet back to the miniport immediately
+ * this is why we are creating the INTNETSG, copying the ndis packet info there and enqueueing it */
+#define VBOXNETFLT_PACKET_SG 0x00000001
+
+/** the flag specifying that the packet source
+ * if set the packet comes from the host (upperlying protocol)
+ * if clear the packet comes from the wire (underlying miniport) */
+#define VBOXNETFLT_PACKET_SRC_HOST 0x00000002
+
+#ifndef VBOXNETFLT_NO_PACKET_QUEUE
+/** flag specifying the packet was originated by our driver
+ * i.e. we could use it on our needs and should not return it
+ * we are enqueueing "our" packets on ProtocolReceive call-back when
+ * Ndis does not give us a receive packet (the driver below us has called NdisM..IndicateReceive)
+ * this is supported for Ndis Packet only */
+#define VBOXNETFLT_PACKET_MINE 0x00000004
+
+/** flag passed to vboxNetFltWinQuEnqueuePacket specifying that the packet should be copied
+ * this is supported for Ndis Packet only */
+#define VBOXNETFLT_PACKET_COPY 0x00000008
+#endif
+
+/** packet queue element containing the packet info */
+typedef struct VBOXNETFLT_PACKET_INFO
+{
+ /** list entry used for enqueueing the info */
+ LIST_ENTRY ListEntry;
+ /** pointer to the pool containing this packet info */
+ struct VBOXNETFLT_PACKET_INFO_POOL *pPool;
+ /** flags describing the referenced packet. Contains PACKET_xxx flags (i.e. PACKET_SG, PACKET_SRC_HOST) */
+ uint32_t fFlags;
+ /** pointer to the packet this info represents */
+ PVOID pPacket;
+} VBOXNETFLT_PACKET_INFO, *PVBOXNETFLT_PACKET_INFO;
+
+/* paranoid check to make sure the elements in the packet info array are properly aligned */
+AssertCompile((sizeof(VBOXNETFLT_PACKET_INFO) & (sizeof(PVOID) - 1)) == 0);
+
+/** represents the packet queue */
+typedef LIST_ENTRY PVBOXNETFLT_ACKET_QUEUE, *PVBOXNETFLT_PACKET_QUEUE;
+
+/*
+ * we are using non-interlocked versions of LIST_ENTRY-related operations macros and synchronize
+ * access to the queue and its elements by acquiring/releasing a spinlock using Ndis[Acquire,Release]Spinlock
+ *
+ * we are NOT using interlocked versions of insert/remove head/tail list functions because we need to iterate though
+ * the queue elements as well as remove elements from the midle of the queue
+ *
+ * * @todo: it seems that we can switch to using interlocked versions of list-entry functions
+ * since we have removed all functionality (mentioned above, i.e. queue elements iteration, etc.) that might prevent us from doing this
+ */
+typedef struct VBOXNETFLT_INTERLOCKED_PACKET_QUEUE
+{
+ /** queue */
+ PVBOXNETFLT_ACKET_QUEUE Queue;
+ /** queue lock */
+ NDIS_SPIN_LOCK Lock;
+} VBOXNETFLT_INTERLOCKED_PACKET_QUEUE, *PVBOXNETFLT_INTERLOCKED_PACKET_QUEUE;
+
+typedef struct VBOXNETFLT_SINGLE_LIST
+{
+ /** queue */
+ SINGLE_LIST_ENTRY Head;
+ /** pointer to the list tail. used to enqueue elements to the tail of the list */
+ PSINGLE_LIST_ENTRY pTail;
+} VBOXNETFLT_SINGLE_LIST, *PVBOXNETFLT_SINGLE_LIST;
+
+typedef struct VBOXNETFLT_INTERLOCKED_SINGLE_LIST
+{
+ /** queue */
+ VBOXNETFLT_SINGLE_LIST List;
+ /** queue lock */
+ NDIS_SPIN_LOCK Lock;
+} VBOXNETFLT_INTERLOCKED_SINGLE_LIST, *PVBOXNETFLT_INTERLOCKED_SINGLE_LIST;
+
+/** packet info pool contains free packet info elements to be used for the packet queue
+ * we are using the pool mechanism to allocate packet queue elements
+ * the pool mechanism is pretty simple now, we are allocating a bunch of memory
+ * for maintaining VBOXNETFLT_PACKET_INFO_POOL_SIZE queue elements and just returning null when the pool is exhausted
+ * This mechanism seems to be enough for now since we are using VBOXNETFLT_PACKET_INFO_POOL_SIZE = 0xffff which is
+ * the maximum size of packets the ndis packet pool supports */
+typedef struct VBOXNETFLT_PACKET_INFO_POOL
+{
+ /** free packet info queue */
+ VBOXNETFLT_INTERLOCKED_PACKET_QUEUE Queue;
+ /** memory bugger used by the pool */
+ PVOID pBuffer;
+} VBOXNETFLT_PACKET_INFO_POOL, *PVBOXNETFLT_PACKET_INFO_POOL;
+
+typedef enum VBOXNETDEVOPSTATE
+{
+ kVBoxNetDevOpState_InvalidValue = 0,
+ kVBoxNetDevOpState_Initializing,
+ kVBoxNetDevOpState_Initialized,
+ kVBoxNetDevOpState_Deinitializing,
+ kVBoxNetDevOpState_Deinitialized,
+
+} VBOXNETDEVOPSTATE;
+
+typedef enum VBOXNETFLT_WINIFSTATE
+{
+ /** The usual invalid state. */
+ kVBoxWinIfState_Invalid = 0,
+ /** Initialization. */
+ kVBoxWinIfState_Connecting,
+ /** Connected fuly functional state */
+ kVBoxWinIfState_Connected,
+ /** Disconnecting */
+ kVBoxWinIfState_Disconnecting,
+ /** Disconnected */
+ kVBoxWinIfState_Disconnected,
+} VBOXNETFLT_WINIFSTATE;
+
+/** structure used to maintain the state and reference count of the miniport and protocol */
+typedef struct VBOXNETFLT_WINIF_DEVICE
+{
+ /** initialize state */
+ VBOXNETDEVOPSTATE OpState;
+ /** ndis power state */
+ NDIS_DEVICE_POWER_STATE PowerState;
+ /** reference count */
+ uint32_t cReferences;
+} VBOXNETFLT_WINIF_DEVICE, *PVBOXNETFLT_WINIF_DEVICE;
+
+#define VBOXNDISREQUEST_INPROGRESS 1
+#define VBOXNDISREQUEST_QUEUED 2
+
+typedef struct VBOXNETFLTWIN_STATE
+{
+ union
+ {
+ struct
+ {
+ UINT fRequestInfo : 2;
+ UINT fInterfaceClosing : 1;
+ UINT fStandBy : 1;
+ UINT fProcessingPacketFilter : 1;
+ UINT fPPFNetFlt : 1;
+ UINT fUpperProtSetFilterInitialized : 1;
+ UINT Reserved : 25;
+ };
+ UINT Value;
+ };
+} VBOXNETFLTWIN_STATE, *PVBOXNETFLTWIN_STATE;
+
+DECLINLINE(VBOXNETFLTWIN_STATE) vboxNetFltWinAtomicUoReadWinState(VBOXNETFLTWIN_STATE State)
+{
+ UINT fValue = ASMAtomicUoReadU32((volatile uint32_t *)&State.Value);
+ return *((PVBOXNETFLTWIN_STATE)((void*)&fValue));
+}
+
+/* miniport layer globals */
+typedef struct VBOXNETFLTGLOBALS_MP
+{
+ /** our miniport handle */
+ NDIS_HANDLE hMiniport;
+ /** ddis wrapper handle */
+ NDIS_HANDLE hNdisWrapper;
+} VBOXNETFLTGLOBALS_MP, *PVBOXNETFLTGLOBALS_MP;
+
+#ifndef VBOXNETADP
+/* protocol layer globals */
+typedef struct VBOXNETFLTGLOBALS_PT
+{
+ /** our protocol handle */
+ NDIS_HANDLE hProtocol;
+} VBOXNETFLTGLOBALS_PT, *PVBOXNETFLTGLOBALS_PT;
+#endif /* #ifndef VBOXNETADP */
+
+typedef struct VBOXNETFLTGLOBALS_WIN
+{
+ /** synch event used for device creation synchronization */
+ KEVENT SynchEvent;
+ /** Device reference count */
+ int cDeviceRefs;
+ /** ndis device */
+ NDIS_HANDLE hDevice;
+ /** device object */
+ PDEVICE_OBJECT pDevObj;
+ /* loopback flags */
+ /* ndis packet flags to disable packet loopback */
+ UINT fPacketDontLoopBack;
+ /* ndis packet flags specifying whether the packet is looped back */
+ UINT fPacketIsLoopedBack;
+ /* Minport info */
+ VBOXNETFLTGLOBALS_MP Mp;
+#ifndef VBOXNETADP
+ /* Protocol info */
+ VBOXNETFLTGLOBALS_PT Pt;
+ /** lock protecting the filter list */
+ NDIS_SPIN_LOCK lockFilters;
+ /** the head of filter list */
+ RTLISTANCHOR listFilters;
+ /** IP address change notifier handle */
+ HANDLE hNotifier;
+#endif
+} VBOXNETFLTGLOBALS_WIN, *PVBOXNETFLTGLOBALS_WIN;
+
+extern VBOXNETFLTGLOBALS_WIN g_VBoxNetFltGlobalsWin;
+
+/** represents filter driver device context*/
+typedef struct VBOXNETFLTWIN
+{
+ /** handle used by miniport edge for ndis calls */
+ NDIS_HANDLE hMiniport;
+ /** miniport edge state */
+ VBOXNETFLT_WINIF_DEVICE MpState;
+ /** ndis packet pool used for receives */
+ NDIS_HANDLE hRecvPacketPool;
+ /** ndis buffer pool used for receives */
+ NDIS_HANDLE hRecvBufferPool;
+ /** driver bind adapter state. */
+ VBOXNETFLT_WINIFSTATE enmState;
+#ifndef VBOXNETADP
+ /* misc state flags */
+ VBOXNETFLTWIN_STATE StateFlags;
+ /** handle used by protocol edge for ndis calls */
+ NDIS_HANDLE hBinding;
+ /** protocol edge state */
+ VBOXNETFLT_WINIF_DEVICE PtState;
+ /** ndis packet pool used for receives */
+ NDIS_HANDLE hSendPacketPool;
+ /** ndis buffer pool used for receives */
+ NDIS_HANDLE hSendBufferPool;
+ /** used for maintaining the pending send packets for handling packet loopback */
+ VBOXNETFLT_INTERLOCKED_SINGLE_LIST SendPacketQueue;
+ /** used for serializing calls to the NdisRequest in the vboxNetFltWinSynchNdisRequest */
+ RTSEMFASTMUTEX hSynchRequestMutex;
+ /** event used to synchronize with the Ndis Request completion in the vboxNetFltWinSynchNdisRequest */
+ KEVENT hSynchCompletionEvent;
+ /** status of the Ndis Request initiated by the vboxNetFltWinSynchNdisRequest */
+ NDIS_STATUS volatile SynchCompletionStatus;
+ /** pointer to the Ndis Request being executed by the vboxNetFltWinSynchNdisRequest */
+ PNDIS_REQUEST volatile pSynchRequest;
+ /** open/close adapter status.
+ * Since ndis adapter open and close requests may complete asynchronously,
+ * we are using event mechanism to wait for open/close completion
+ * the status field is being set by the completion call-back */
+ NDIS_STATUS OpenCloseStatus;
+ /** open/close adaptor completion event */
+ NDIS_EVENT OpenCloseEvent;
+ /** medium we are attached to */
+ NDIS_MEDIUM enmMedium;
+ /**
+ * Passdown request info
+ */
+ /** ndis request we pass down to the miniport below */
+ NDIS_REQUEST PassDownRequest;
+ /** Ndis pass down request bytes read or written original pointer */
+ PULONG pcPDRBytesRW;
+ /** Ndis pass down request bytes needed original pointer */
+ PULONG pcPDRBytesNeeded;
+ /** true if we should indicate the receive complete used by the ProtocolReceive mechanism.
+ * We need to indicate it only with the ProtocolReceive + NdisMEthIndicateReceive path.
+ * Note: we're using KeGetCurrentProcessorNumber, which is not entirely correct in case
+ * we're running on 64bit win7+, which can handle > 64 CPUs, however since KeGetCurrentProcessorNumber
+ * always returns the number < than the number of CPUs in the first group, we're guaranteed to have CPU index < 64
+ * @todo: use KeGetCurrentProcessorNumberEx for Win7+ 64 and dynamically extended array */
+ bool abIndicateRxComplete[64];
+ /** Pending transfer data packet queue (i.e. packets that were indicated as pending on NdisTransferData call */
+ VBOXNETFLT_INTERLOCKED_SINGLE_LIST TransferDataList;
+ /* mac options initialized on OID_GEN_MAC_OPTIONS */
+ ULONG fMacOptions;
+ /** our miniport devuice name */
+ NDIS_STRING MpDeviceName;
+ /** synchronize with unbind with Miniport initialization */
+ NDIS_EVENT MpInitCompleteEvent;
+ /** media connect status that we indicated */
+ NDIS_STATUS MpIndicatedMediaStatus;
+ /** media connect status pending to indicate */
+ NDIS_STATUS MpUnindicatedMediaStatus;
+ /** packet filter flags set by the upper protocols */
+ ULONG fUpperProtocolSetFilter;
+ /** packet filter flags set by the upper protocols */
+ ULONG fSetFilterBuffer;
+ /** packet filter flags set by us */
+ ULONG fOurSetFilter;
+ /** our own list of filters, needed by notifier */
+ RTLISTNODE node;
+#else
+ volatile ULONG cTxSuccess;
+ volatile ULONG cRxSuccess;
+ volatile ULONG cTxError;
+ volatile ULONG cRxError;
+#endif
+} VBOXNETFLTWIN, *PVBOXNETFLTWIN;
+
+typedef struct VBOXNETFLT_PACKET_QUEUE_WORKER
+{
+ /** this event is used to initiate a packet queue worker thread kill */
+ KEVENT KillEvent;
+ /** this event is used to notify a worker thread that the packets are added to the queue */
+ KEVENT NotifyEvent;
+ /** pointer to the packet queue worker thread object */
+ PKTHREAD pThread;
+ /** pointer to the SG used by the packet queue for IntNet receive notifications */
+ PINTNETSG pSG;
+ /** Packet queue */
+ VBOXNETFLT_INTERLOCKED_PACKET_QUEUE PacketQueue;
+ /** Packet info pool, i.e. the pool for the packet queue elements */
+ VBOXNETFLT_PACKET_INFO_POOL PacketInfoPool;
+} VBOXNETFLT_PACKET_QUEUE_WORKER, *PVBOXNETFLT_PACKET_QUEUE_WORKER;
+
+/* protocol reserved data held in ndis packet */
+typedef struct VBOXNETFLT_PKTRSVD_PT
+{
+ /** original packet received from the upperlying protocol
+ * can be null if the packet was originated by intnet */
+ PNDIS_PACKET pOrigPacket;
+ /** pointer to the buffer to be freed on send completion
+ * can be null if no buffer is to be freed */
+ PVOID pBufToFree;
+#if !defined(VBOX_LOOPBACK_USEFLAGS) || defined(DEBUG_NETFLT_PACKETS)
+ SINGLE_LIST_ENTRY ListEntry;
+ /* true if the packet is from IntNet */
+ bool bFromIntNet;
+#endif
+} VBOXNETFLT_PKTRSVD_PT, *PVBOXNETFLT_PKTRSVD_PT;
+
+/** miniport reserved data held in ndis packet */
+typedef struct VBOXNETFLT_PKTRSVD_MP
+{
+ /** original packet received from the underling miniport
+ * can be null if the packet was originated by intnet */
+ PNDIS_PACKET pOrigPacket;
+ /** pointer to the buffer to be freed on receive completion
+ * can be null if no buffer is to be freed */
+ PVOID pBufToFree;
+} VBOXNETFLT_PKTRSVD_MP, *PVBOXNETFLT_PKTRSVD_MP;
+
+/** represents the data stored in the protocol reserved field of ndis packet on NdisTransferData processing */
+typedef struct VBOXNETFLT_PKTRSVD_TRANSFERDATA_PT
+{
+ /** next packet in a list */
+ SINGLE_LIST_ENTRY ListEntry;
+ /* packet buffer start */
+ PNDIS_BUFFER pOrigBuffer;
+} VBOXNETFLT_PKTRSVD_TRANSFERDATA_PT, *PVBOXNETFLT_PKTRSVD_TRANSFERDATA_PT;
+
+/* VBOXNETFLT_PKTRSVD_TRANSFERDATA_PT should fit into PROTOCOL_RESERVED_SIZE_IN_PACKET because we use protocol reserved part
+ * of our miniport edge on transfer data processing for honding our own info */
+AssertCompile(sizeof (VBOXNETFLT_PKTRSVD_TRANSFERDATA_PT) <= PROTOCOL_RESERVED_SIZE_IN_PACKET);
+/* this should fit in MiniportReserved */
+AssertCompile(sizeof (VBOXNETFLT_PKTRSVD_MP) <= RT_SIZEOFMEMB(NDIS_PACKET, MiniportReserved));
+/* we use RTAsmAtomic*U32 for those, make sure we're correct */
+AssertCompile(sizeof (NDIS_DEVICE_POWER_STATE) == sizeof (uint32_t));
+AssertCompile(sizeof (UINT) == sizeof (uint32_t));
+
+
+#define NDIS_FLAGS_SKIP_LOOPBACK_W2K 0x400
+
+#include "../../VBoxNetFltInternal.h"
+#include "VBoxNetFltRt-win.h"
+#ifndef VBOXNETADP
+# include "VBoxNetFltP-win.h"
+#endif
+#include "VBoxNetFltM-win.h"
+
+#endif /* !VBOX_INCLUDED_SRC_VBoxNetFlt_win_drv_VBoxNetFltCmn_win_h */
diff --git a/src/VBox/HostDrivers/VBoxNetFlt/win/drv/VBoxNetFltM-win.cpp b/src/VBox/HostDrivers/VBoxNetFlt/win/drv/VBoxNetFltM-win.cpp
new file mode 100644
index 00000000..65bde454
--- /dev/null
+++ b/src/VBox/HostDrivers/VBoxNetFlt/win/drv/VBoxNetFltM-win.cpp
@@ -0,0 +1,1570 @@
+/* $Id: VBoxNetFltM-win.cpp $ */
+/** @file
+ * VBoxNetFltM-win.cpp - Bridged Networking Driver, Windows Specific Code.
+ * Miniport edge
+ */
+/*
+ * Copyright (C) 2011-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+#include "VBoxNetFltCmn-win.h"
+
+static const char* vboxNetFltWinMpDumpOid(ULONG oid);
+
+#ifndef VBOXNETADP
+static NDIS_STATUS vboxNetFltWinMpInitialize(OUT PNDIS_STATUS OpenErrorStatus,
+ OUT PUINT SelectedMediumIndex,
+ IN PNDIS_MEDIUM MediumArray,
+ IN UINT MediumArraySize,
+ IN NDIS_HANDLE MiniportAdapterHandle,
+ IN NDIS_HANDLE WrapperConfigurationContext)
+{
+ RT_NOREF1(WrapperConfigurationContext);
+ PVBOXNETFLTINS pNetFlt = (PVBOXNETFLTINS)NdisIMGetDeviceContext(MiniportAdapterHandle);
+ NDIS_STATUS Status = NDIS_STATUS_FAILURE;
+
+ LogFlowFunc(("ENTER: pNetFlt (0x%p)\n", pNetFlt));
+
+ pNetFlt->u.s.WinIf.hMiniport = MiniportAdapterHandle;
+ Assert(vboxNetFltWinGetOpState(&pNetFlt->u.s.WinIf.MpState) == kVBoxNetDevOpState_Initializing);
+ /* the MP state should be already set to kVBoxNetDevOpState_Initializing, just a paranoia
+ * in case NDIS for some reason calls us in some irregular way */
+ vboxNetFltWinSetOpState(&pNetFlt->u.s.WinIf.MpState, kVBoxNetDevOpState_Initializing);
+
+ NDIS_MEDIUM enmMedium = pNetFlt->u.s.WinIf.enmMedium;
+ if (enmMedium == NdisMediumWan)
+ enmMedium = NdisMedium802_3;
+
+ UINT i = 0;
+ for (; i < MediumArraySize; i++)
+ {
+ if (MediumArray[i] == enmMedium)
+ {
+ *SelectedMediumIndex = i;
+ break;
+ }
+ }
+
+ do
+ {
+ if (i != MediumArraySize)
+ {
+ NdisMSetAttributesEx(MiniportAdapterHandle, pNetFlt, 0,
+ NDIS_ATTRIBUTE_IGNORE_PACKET_TIMEOUT |
+ NDIS_ATTRIBUTE_IGNORE_REQUEST_TIMEOUT|
+ NDIS_ATTRIBUTE_INTERMEDIATE_DRIVER |
+ NDIS_ATTRIBUTE_DESERIALIZE |
+ NDIS_ATTRIBUTE_NO_HALT_ON_SUSPEND,
+ NdisInterfaceInternal /* 0 */);
+
+ pNetFlt->u.s.WinIf.MpIndicatedMediaStatus = NDIS_STATUS_MEDIA_CONNECT;
+ Assert(vboxNetFltWinGetPowerState(&pNetFlt->u.s.WinIf.MpState) == NdisDeviceStateD3);
+ vboxNetFltWinSetPowerState(&pNetFlt->u.s.WinIf.MpState, NdisDeviceStateD0);
+ Assert(pNetFlt->u.s.WinIf.MpState.OpState == kVBoxNetDevOpState_Initializing);
+ vboxNetFltWinSetOpState(&pNetFlt->u.s.WinIf.MpState, kVBoxNetDevOpState_Initialized);
+
+ Status = NDIS_STATUS_SUCCESS;
+ break;
+ }
+ else
+ {
+ Status = NDIS_STATUS_UNSUPPORTED_MEDIA;
+ }
+
+ Assert(Status != NDIS_STATUS_SUCCESS);
+ Assert(vboxNetFltWinGetPowerState(&pNetFlt->u.s.WinIf.MpState) == NdisDeviceStateD3);
+ Assert(pNetFlt->u.s.WinIf.MpState.OpState == kVBoxNetDevOpState_Initializing);
+ vboxNetFltWinSetOpState(&pNetFlt->u.s.WinIf.MpState, kVBoxNetDevOpState_Deinitialized);
+ } while (0);
+
+ NdisSetEvent(&pNetFlt->u.s.WinIf.MpInitCompleteEvent);
+
+ LogFlowFunc(("LEAVE: pNetFlt (0x%p), Status (0x%x)\n", pNetFlt, Status));
+
+ *OpenErrorStatus = Status;
+
+ return Status;
+}
+
+/**
+ * process the packet send in a "passthru" mode
+ */
+static NDIS_STATUS vboxNetFltWinSendPassThru(PVBOXNETFLTINS pNetFlt, PNDIS_PACKET pPacket
+#ifdef VBOXNETFLT_NO_PACKET_QUEUE
+ , bool bNetFltActive
+#endif
+ )
+{
+ PNDIS_PACKET pMyPacket;
+ NDIS_STATUS Status = vboxNetFltWinPrepareSendPacket(pNetFlt, pPacket, &pMyPacket);
+ Assert(Status == NDIS_STATUS_SUCCESS);
+ if (Status == NDIS_STATUS_SUCCESS)
+ {
+#if !defined(VBOX_LOOPBACK_USEFLAGS) /* || defined(DEBUG_NETFLT_PACKETS) */
+# ifdef VBOXNETFLT_NO_PACKET_QUEUE
+ if (bNetFltActive)
+ vboxNetFltWinLbPutSendPacket(pNetFlt, pMyPacket, false /* bFromIntNet */);
+# else
+ /* no need for the loop enqueue & check in a passthru mode , ndis will do everything for us */
+# endif
+#endif
+ NdisSend(&Status, pNetFlt->u.s.WinIf.hBinding, pMyPacket);
+ if (Status != NDIS_STATUS_PENDING)
+ {
+ NdisIMCopySendCompletePerPacketInfo(pPacket, pMyPacket);
+#if defined(VBOXNETFLT_NO_PACKET_QUEUE) && !defined(VBOX_LOOPBACK_USEFLAGS)
+ if (bNetFltActive)
+ vboxNetFltWinLbRemoveSendPacket(pNetFlt, pMyPacket);
+#endif
+ NdisFreePacket(pMyPacket);
+ }
+ }
+ return Status;
+}
+
+#else /* defined VBOXNETADP */
+DECLHIDDEN(NDIS_STATUS) vboxNetFltWinMpDoDeinitialization(PVBOXNETFLTINS pNetFlt)
+{
+ uint64_t NanoTS = RTTimeSystemNanoTS();
+
+ Assert(vboxNetFltWinGetOpState(&pNetFlt->u.s.WinIf.MpState) == kVBoxNetDevOpState_Initialized);
+
+ RTSpinlockAcquire(pNetFlt->hSpinlock);
+ ASMAtomicUoWriteBool(&pNetFlt->fDisconnectedFromHost, true);
+ ASMAtomicUoWriteBool(&pNetFlt->fRediscoveryPending, false);
+ ASMAtomicUoWriteU64(&pNetFlt->NanoTSLastRediscovery, NanoTS);
+
+ vboxNetFltWinSetOpState(&pNetFlt->u.s.WinIf.MpState, kVBoxNetDevOpState_Deinitializing);
+
+ RTSpinlockRelease(pNetFlt->hSpinlock);
+
+ vboxNetFltWinWaitDereference(&pNetFlt->u.s.WinIf.MpState);
+
+ /* check packet pool is empty */
+ int cPPUsage = NdisPacketPoolUsage(pNetFlt->u.s.WinIf.hRecvPacketPool);
+ Assert(cPPUsage == 0);
+ /* for debugging only, ignore the err in release */
+ NOREF(cPPUsage);
+
+ vboxNetFltWinSetOpState(&pNetFlt->u.s.WinIf.MpState, kVBoxNetDevOpState_Deinitialized);
+
+ return NDIS_STATUS_SUCCESS;
+}
+
+static NDIS_STATUS vboxNetFltWinMpReadApplyConfig(PVBOXNETFLTINS pThis, NDIS_HANDLE hMiniportAdapter,
+ NDIS_HANDLE hWrapperConfigurationContext)
+{
+ RT_NOREF1(hMiniportAdapter);
+ NDIS_STATUS Status = NDIS_STATUS_SUCCESS;
+ NDIS_HANDLE hConfiguration;
+ PNDIS_CONFIGURATION_PARAMETER pParameterValue;
+ NDIS_STRING strMAC = NDIS_STRING_CONST("MAC");
+ RTMAC mac;
+
+ NdisOpenConfiguration(
+ &Status,
+ &hConfiguration,
+ hWrapperConfigurationContext);
+ Assert(Status == NDIS_STATUS_SUCCESS);
+ if (Status == NDIS_STATUS_SUCCESS)
+ {
+ do
+ {
+ int rc;
+ NDIS_CONFIGURATION_PARAMETER param;
+ WCHAR MacBuf[13];
+
+ NdisReadConfiguration(&Status,
+ &pParameterValue,
+ hConfiguration,
+ &strMAC,
+ NdisParameterString);
+// Assert(Status == NDIS_STATUS_SUCCESS);
+ if (Status == NDIS_STATUS_SUCCESS)
+ {
+
+ rc = vboxNetFltWinMACFromNdisString(&mac, &pParameterValue->ParameterData.StringData);
+ AssertRC(rc);
+ if (RT_SUCCESS(rc))
+ {
+ break;
+ }
+ }
+
+ vboxNetFltWinGenerateMACAddress(&mac);
+ param.ParameterType = NdisParameterString;
+ param.ParameterData.StringData.Buffer = MacBuf;
+ param.ParameterData.StringData.MaximumLength = sizeof(MacBuf);
+
+ rc = vboxNetFltWinMAC2NdisString(&mac, &param.ParameterData.StringData);
+ Assert(RT_SUCCESS(rc));
+ if (RT_SUCCESS(rc))
+ {
+ NdisWriteConfiguration(&Status,
+ hConfiguration,
+ &strMAC,
+ &param);
+ Assert(Status == NDIS_STATUS_SUCCESS);
+ if (Status != NDIS_STATUS_SUCCESS)
+ {
+ /* ignore the failure */
+ Status = NDIS_STATUS_SUCCESS;
+ }
+ }
+ } while (0);
+
+ NdisCloseConfiguration(hConfiguration);
+ }
+ else
+ {
+ vboxNetFltWinGenerateMACAddress(&mac);
+ }
+
+ pThis->u.s.MacAddr = mac;
+
+ return NDIS_STATUS_SUCCESS;
+}
+
+DECLHIDDEN(NDIS_STATUS) vboxNetFltWinMpDoInitialization(PVBOXNETFLTINS pNetFlt, NDIS_HANDLE hMiniportAdapter, NDIS_HANDLE hWrapperConfigurationContext)
+{
+ NDIS_STATUS Status;
+ pNetFlt->u.s.WinIf.hMiniport = hMiniportAdapter;
+
+ LogFlowFunc(("ENTER: pNetFlt 0x%p\n", pNetFlt));
+
+ Assert(vboxNetFltWinGetOpState(&pNetFlt->u.s.WinIf.MpState) == kVBoxNetDevOpState_Deinitialized);
+ vboxNetFltWinSetOpState(&pNetFlt->u.s.WinIf.MpState, kVBoxNetDevOpState_Initializing);
+
+ vboxNetFltWinMpReadApplyConfig(pNetFlt, hMiniportAdapter, hWrapperConfigurationContext);
+
+ NdisMSetAttributesEx(hMiniportAdapter, pNetFlt,
+ 0, /* CheckForHangTimeInSeconds */
+ NDIS_ATTRIBUTE_DESERIALIZE |
+ NDIS_ATTRIBUTE_NO_HALT_ON_SUSPEND,
+ NdisInterfaceInternal/* 0 */);
+
+ Assert(vboxNetFltWinGetPowerState(&pNetFlt->u.s.WinIf.MpState) == NdisDeviceStateD3);
+ vboxNetFltWinSetPowerState(&pNetFlt->u.s.WinIf.MpState, NdisDeviceStateD0);
+ Assert(vboxNetFltWinGetOpState(&pNetFlt->u.s.WinIf.MpState) == kVBoxNetDevOpState_Initializing);
+ vboxNetFltWinSetOpState(&pNetFlt->u.s.WinIf.MpState, kVBoxNetDevOpState_Initialized);
+
+ Status = NDIS_STATUS_SUCCESS;
+
+ LogFlowFunc(("pNetFlt 0x%p, Status 0x%x\n", pNetFlt, Status));
+
+ return Status;
+}
+
+static NDIS_STATUS vboxNetFltWinMpInitialize(OUT PNDIS_STATUS OpenErrorStatus,
+ OUT PUINT SelectedMediumIndex,
+ IN PNDIS_MEDIUM MediumArray,
+ IN UINT MediumArraySize,
+ IN NDIS_HANDLE MiniportAdapterHandle,
+ IN NDIS_HANDLE WrapperConfigurationContext)
+{
+
+ NDIS_STATUS Status = NDIS_STATUS_FAILURE;
+ UINT i = 0;
+
+ LogFlowFuncEnter();
+
+ for (; i < MediumArraySize; i++)
+ {
+ if (MediumArray[i] == NdisMedium802_3)
+ {
+ *SelectedMediumIndex = i;
+ break;
+ }
+ }
+
+ if (i != MediumArraySize)
+ {
+ PDEVICE_OBJECT pPdo, pFdo;
+#define KEY_PREFIX L"\\Registry\\Machine\\System\\CurrentControlSet\\Control\\Class\\"
+ UCHAR Buf[512];
+ PUCHAR pSuffix;
+ ULONG cbBuf;
+ NDIS_STRING RtlStr;
+
+ wcscpy((WCHAR*)Buf, KEY_PREFIX);
+ pSuffix = Buf + (sizeof(KEY_PREFIX)-2);
+
+ NdisMGetDeviceProperty(MiniportAdapterHandle,
+ &pPdo,
+ &pFdo,
+ NULL, //Next Device Object
+ NULL,
+ NULL);
+
+ Status = IoGetDeviceProperty (pPdo,
+ DevicePropertyDriverKeyName,
+ sizeof(Buf) - (sizeof(KEY_PREFIX)-2),
+ pSuffix,
+ &cbBuf);
+ if (Status == STATUS_SUCCESS)
+ {
+ OBJECT_ATTRIBUTES ObjAttr;
+ HANDLE hDrvKey;
+ RtlStr.Buffer=(WCHAR*)Buf;
+ RtlStr.Length=(USHORT)cbBuf - 2 + sizeof(KEY_PREFIX) - 2;
+ RtlStr.MaximumLength=sizeof(Buf);
+
+ InitializeObjectAttributes(&ObjAttr, &RtlStr, OBJ_CASE_INSENSITIVE, NULL, NULL);
+
+ Status = ZwOpenKey(&hDrvKey, KEY_READ, &ObjAttr);
+ if (Status == STATUS_SUCCESS)
+ {
+ static UNICODE_STRING NetCfgInstanceIdValue = NDIS_STRING_CONST("NetCfgInstanceId");
+// UCHAR valBuf[sizeof(KEY_VALUE_PARTIAL_INFORMATION) + RTUUID_STR_LENGTH*2 + 10];
+// ULONG cLength = sizeof(valBuf);
+#define NAME_PREFIX L"\\DEVICE\\"
+ PKEY_VALUE_PARTIAL_INFORMATION pInfo = (PKEY_VALUE_PARTIAL_INFORMATION)Buf;
+ Status = ZwQueryValueKey(hDrvKey,
+ &NetCfgInstanceIdValue,
+ KeyValuePartialInformation,
+ pInfo,
+ sizeof(Buf),
+ &cbBuf);
+ if (Status == STATUS_SUCCESS)
+ {
+ if (pInfo->Type == REG_SZ && pInfo->DataLength > 2)
+ {
+ WCHAR *pName;
+ Status = vboxNetFltWinMemAlloc((PVOID*)&pName, pInfo->DataLength + sizeof(NAME_PREFIX));
+ if (Status == STATUS_SUCCESS)
+ {
+ PVBOXNETFLTINS pNetFlt;
+ wcscpy(pName, NAME_PREFIX);
+ wcscpy(pName+(sizeof(NAME_PREFIX)-2)/2, (WCHAR*)pInfo->Data);
+ RtlStr.Buffer=pName;
+ RtlStr.Length = (USHORT)pInfo->DataLength - 2 + sizeof(NAME_PREFIX) - 2;
+ RtlStr.MaximumLength = (USHORT)pInfo->DataLength + sizeof(NAME_PREFIX);
+
+ Status = vboxNetFltWinPtInitBind(&pNetFlt, MiniportAdapterHandle, &RtlStr, WrapperConfigurationContext);
+
+ if (Status == STATUS_SUCCESS)
+ {
+ Assert(vboxNetFltWinGetOpState(&pNetFlt->u.s.WinIf.MpState) == kVBoxNetDevOpState_Initialized);
+ vboxNetFltWinSetOpState(&pNetFlt->u.s.WinIf.MpState, kVBoxNetDevOpState_Initialized);
+#if 0
+ NdisMIndicateStatus(pNetFlt->u.s.WinIf.hMiniport,
+ NDIS_STATUS_MEDIA_CONNECT,
+ (PVOID)NULL,
+ 0);
+#endif
+ }
+ else
+ {
+ Assert(vboxNetFltWinGetOpState(&pNetFlt->u.s.WinIf.MpState) == kVBoxNetDevOpState_Deinitialized);
+ vboxNetFltWinSetOpState(&pNetFlt->u.s.WinIf.MpState, kVBoxNetDevOpState_Deinitialized);
+ }
+
+ vboxNetFltWinMemFree(pName);
+
+ }
+ }
+ else
+ {
+ Status = NDIS_STATUS_FAILURE;
+ }
+ }
+ }
+ }
+ }
+ else
+ {
+ Status = NDIS_STATUS_UNSUPPORTED_MEDIA;
+ }
+
+ /** @todo */
+ *OpenErrorStatus = Status;
+
+ LogFlowFunc(("LEAVE: Status (0x%x)\n", Status));
+
+ return Status;
+}
+#endif
+
+static VOID vboxNetFltWinMpSendPackets(IN NDIS_HANDLE hMiniportAdapterContext,
+ IN PPNDIS_PACKET pPacketArray,
+ IN UINT cNumberOfPackets)
+{
+ PVBOXNETFLTINS pNetFlt = (PVBOXNETFLTINS)hMiniportAdapterContext;
+ NDIS_STATUS Status = NDIS_STATUS_SUCCESS;
+ bool bNetFltActive;
+
+ LogFlowFunc(("ENTER: pNetFlt (0x%p)\n", pNetFlt));
+
+ Assert(cNumberOfPackets);
+
+ if (vboxNetFltWinIncReferenceWinIfNetFlt(pNetFlt, cNumberOfPackets, &bNetFltActive))
+ {
+ uint32_t cAdaptRefs = cNumberOfPackets;
+ uint32_t cNetFltRefs;
+ uint32_t cPassThruRefs;
+ if (bNetFltActive)
+ {
+ cNetFltRefs = cNumberOfPackets;
+ cPassThruRefs = 0;
+ }
+ else
+ {
+ cPassThruRefs = cNumberOfPackets;
+ cNetFltRefs = 0;
+ }
+
+ for (UINT i = 0; i < cNumberOfPackets; i++)
+ {
+ PNDIS_PACKET pPacket;
+
+ pPacket = pPacketArray[i];
+
+ if (!cNetFltRefs
+#ifdef VBOXNETFLT_NO_PACKET_QUEUE
+ || !vboxNetFltWinPostIntnet(pNetFlt, pPacket, VBOXNETFLT_PACKET_SRC_HOST)
+#else
+ || (fStatus = vboxNetFltWinQuEnqueuePacket(pNetFlt, pPacket, VBOXNETFLT_PACKET_SRC_HOST)) != NDIS_STATUS_SUCCESS
+#endif
+ )
+ {
+#ifndef VBOXNETADP
+ Status = vboxNetFltWinSendPassThru(pNetFlt, pPacket
+#ifdef VBOXNETFLT_NO_PACKET_QUEUE
+ , !!cNetFltRefs
+#endif
+ );
+#else
+ if (!cNetFltRefs)
+ {
+# ifdef VBOXNETADP_REPORT_DISCONNECTED
+ Status = NDIS_STATUS_MEDIA_DISCONNECT;
+ STATISTIC_INCREASE(pNetFlt->u.s.WinIf.cTxError);
+# else
+ Status = NDIS_STATUS_SUCCESS;
+# endif
+ }
+#endif
+
+ if (Status != NDIS_STATUS_PENDING)
+ {
+ NdisMSendComplete(pNetFlt->u.s.WinIf.hMiniport, pPacket, Status);
+ }
+ else
+ {
+ cAdaptRefs--;
+ }
+ }
+ else
+ {
+#ifdef VBOXNETFLT_NO_PACKET_QUEUE
+ NdisMSendComplete(pNetFlt->u.s.WinIf.hMiniport, pPacket, NDIS_STATUS_SUCCESS);
+#else
+ cAdaptRefs--;
+ cNetFltRefs--;
+#endif
+ }
+ }
+
+ if (cNetFltRefs)
+ {
+ vboxNetFltWinDecReferenceNetFlt(pNetFlt, cNetFltRefs);
+ }
+ else if (cPassThruRefs)
+ {
+ vboxNetFltWinDecReferenceModePassThru(pNetFlt, cPassThruRefs);
+ }
+ if (cAdaptRefs)
+ {
+ vboxNetFltWinDecReferenceWinIf(pNetFlt, cAdaptRefs);
+ }
+ }
+ else
+ {
+ NDIS_HANDLE h = pNetFlt->u.s.WinIf.hMiniport;
+ AssertFailed();
+ if (h)
+ {
+ for (UINT i = 0; i < cNumberOfPackets; i++)
+ {
+ PNDIS_PACKET pPacket;
+ pPacket = pPacketArray[i];
+ NdisMSendComplete(h, pPacket, NDIS_STATUS_FAILURE);
+ }
+ }
+ }
+
+ LogFlowFunc(("LEAVE: pNetFlt (0x%p)\n", pNetFlt));
+}
+
+#ifndef VBOXNETADP
+static UINT vboxNetFltWinMpRequestStatePrep(PVBOXNETFLTINS pNetFlt, NDIS_STATUS *pStatus)
+{
+ Assert(!pNetFlt->u.s.WinIf.StateFlags.fRequestInfo);
+
+ if (vboxNetFltWinGetOpState(&pNetFlt->u.s.WinIf.PtState) > kVBoxNetDevOpState_Initialized /* protocol unbind in progress */
+ || vboxNetFltWinGetPowerState(&pNetFlt->u.s.WinIf.MpState) > NdisDeviceStateD0)
+ {
+ *pStatus = NDIS_STATUS_FAILURE;
+ return 0;
+ }
+
+ RTSpinlockAcquire(pNetFlt->hSpinlock);
+ Assert(!pNetFlt->u.s.WinIf.StateFlags.fRequestInfo);
+ if (vboxNetFltWinGetOpState(&pNetFlt->u.s.WinIf.PtState) > kVBoxNetDevOpState_Initialized /* protocol unbind in progress */
+ || vboxNetFltWinGetPowerState(&pNetFlt->u.s.WinIf.MpState) > NdisDeviceStateD0)
+ {
+ RTSpinlockRelease(pNetFlt->hSpinlock);
+ *pStatus = NDIS_STATUS_FAILURE;
+ return 0;
+ }
+
+ if ((vboxNetFltWinGetPowerState(&pNetFlt->u.s.WinIf.PtState) > NdisDeviceStateD0)
+ && !pNetFlt->u.s.WinIf.StateFlags.fStandBy)
+ {
+ pNetFlt->u.s.WinIf.StateFlags.fRequestInfo = VBOXNDISREQUEST_INPROGRESS | VBOXNDISREQUEST_QUEUED;
+ RTSpinlockRelease(pNetFlt->hSpinlock);
+ *pStatus = NDIS_STATUS_PENDING;
+ return VBOXNDISREQUEST_INPROGRESS | VBOXNDISREQUEST_QUEUED;
+ }
+
+ if (pNetFlt->u.s.WinIf.StateFlags.fStandBy)
+ {
+ RTSpinlockRelease(pNetFlt->hSpinlock);
+ *pStatus = NDIS_STATUS_FAILURE;
+ return 0;
+ }
+
+ pNetFlt->u.s.WinIf.StateFlags.fRequestInfo = VBOXNDISREQUEST_INPROGRESS;
+
+ RTSpinlockRelease(pNetFlt->hSpinlock);
+
+ *pStatus = NDIS_STATUS_SUCCESS;
+ return VBOXNDISREQUEST_INPROGRESS;
+}
+
+static NDIS_STATUS vboxNetFltWinMpRequestPostQuery(PVBOXNETFLTINS pNetFlt)
+{
+ if (pNetFlt->u.s.WinIf.PassDownRequest.DATA.QUERY_INFORMATION.Oid == OID_GEN_CURRENT_PACKET_FILTER && VBOXNETFLT_PROMISCUOUS_SUPPORTED(pNetFlt))
+ {
+ bool fNetFltActive;
+ const bool fWinIfActive = vboxNetFltWinReferenceWinIfNetFlt(pNetFlt, &fNetFltActive);
+
+ Assert(pNetFlt->u.s.WinIf.PassDownRequest.DATA.QUERY_INFORMATION.InformationBuffer);
+ Assert(!pNetFlt->u.s.WinIf.StateFlags.fProcessingPacketFilter);
+
+ if (fNetFltActive)
+ {
+ /* netflt is active, simply return the cached value */
+ *((PULONG)pNetFlt->u.s.WinIf.PassDownRequest.DATA.QUERY_INFORMATION.InformationBuffer) = pNetFlt->u.s.WinIf.fUpperProtocolSetFilter;
+
+ /* we've intercepted the query and completed it */
+ vboxNetFltWinMpRequestStateComplete(pNetFlt);
+
+ vboxNetFltWinDereferenceNetFlt(pNetFlt);
+ vboxNetFltWinDereferenceWinIf(pNetFlt);
+
+ return NDIS_STATUS_SUCCESS;
+ }
+ else if (fWinIfActive)
+ {
+ pNetFlt->u.s.WinIf.StateFlags.fProcessingPacketFilter = 1;
+ pNetFlt->u.s.WinIf.StateFlags.fPPFNetFlt = 0;
+ /* we're cleaning it in RequestComplete */
+ }
+ }
+
+ NDIS_STATUS Status;
+ /* issue the request */
+ NdisRequest(&Status, pNetFlt->u.s.WinIf.hBinding, &pNetFlt->u.s.WinIf.PassDownRequest);
+ if (Status != NDIS_STATUS_PENDING)
+ {
+ vboxNetFltWinPtRequestComplete(pNetFlt, &pNetFlt->u.s.WinIf.PassDownRequest, Status);
+ Status = NDIS_STATUS_PENDING;
+ }
+
+ return Status;
+}
+
+static NDIS_STATUS vboxNetFltWinMpQueryInformation(IN NDIS_HANDLE MiniportAdapterContext,
+ IN NDIS_OID Oid,
+ IN PVOID InformationBuffer,
+ IN ULONG InformationBufferLength,
+ OUT PULONG BytesWritten,
+ OUT PULONG BytesNeeded)
+{
+ PVBOXNETFLTINS pNetFlt = (PVBOXNETFLTINS)MiniportAdapterContext;
+ NDIS_STATUS Status = NDIS_STATUS_FAILURE;
+
+ LogFlowFunc(("ENTER: pNetFlt (0x%p), Oid (%s)\n", pNetFlt, vboxNetFltWinMpDumpOid(Oid)));
+
+ /* fist check if this is the oid we want to pass down */
+ switch (Oid)
+ {
+ case OID_PNP_QUERY_POWER:
+ Status = NDIS_STATUS_SUCCESS;
+ break;
+ case OID_TCP_TASK_OFFLOAD:
+ case OID_GEN_SUPPORTED_GUIDS:
+ Status = NDIS_STATUS_NOT_SUPPORTED;
+ break;
+ default:
+ {
+ /* the oid is to be passed down,
+ * check the device state if we can do it
+ * and update device state accordingly */
+ UINT uOp = vboxNetFltWinMpRequestStatePrep(pNetFlt, &Status);
+ if (uOp)
+ {
+ /* save the request info */
+ pNetFlt->u.s.WinIf.PassDownRequest.RequestType = NdisRequestQueryInformation;
+ pNetFlt->u.s.WinIf.PassDownRequest.DATA.QUERY_INFORMATION.Oid = Oid;
+ pNetFlt->u.s.WinIf.PassDownRequest.DATA.QUERY_INFORMATION.InformationBuffer = InformationBuffer;
+ pNetFlt->u.s.WinIf.PassDownRequest.DATA.QUERY_INFORMATION.InformationBufferLength = InformationBufferLength;
+ pNetFlt->u.s.WinIf.pcPDRBytesNeeded = BytesNeeded;
+ pNetFlt->u.s.WinIf.pcPDRBytesRW = BytesWritten;
+
+ /* the oid can be processed */
+ if (!(uOp & VBOXNDISREQUEST_QUEUED))
+ {
+ Status = vboxNetFltWinMpRequestPostQuery(pNetFlt);
+ }
+ }
+ break;
+ }
+ }
+
+ LogFlowFunc(("LEAVE: pNetFlt (0x%p), Oid (%s), Status (0x%x)\n", pNetFlt, vboxNetFltWinMpDumpOid(Oid), Status));
+
+ return Status;
+}
+
+#endif /* ifndef VBOXNETADP*/
+
+static NDIS_STATUS vboxNetFltWinMpHandlePowerState(PVBOXNETFLTINS pNetFlt, NDIS_DEVICE_POWER_STATE enmState)
+{
+ if (vboxNetFltWinGetPowerState(&pNetFlt->u.s.WinIf.MpState) > NdisDeviceStateD0
+ && enmState != NdisDeviceStateD0)
+ {
+ /* invalid state transformation */
+ AssertFailed();
+ return NDIS_STATUS_FAILURE;
+ }
+
+#ifndef VBOXNETADP
+ if (vboxNetFltWinGetPowerState(&pNetFlt->u.s.WinIf.MpState) == NdisDeviceStateD0
+ && enmState > NdisDeviceStateD0)
+ {
+ pNetFlt->u.s.WinIf.StateFlags.fStandBy = TRUE;
+ }
+
+ if (vboxNetFltWinGetPowerState(&pNetFlt->u.s.WinIf.MpState) > NdisDeviceStateD0
+ && enmState == NdisDeviceStateD0)
+ {
+ pNetFlt->u.s.WinIf.StateFlags.fStandBy = FALSE;
+ }
+#endif
+
+ vboxNetFltWinSetPowerState(&pNetFlt->u.s.WinIf.MpState, enmState);
+
+#ifndef VBOXNETADP
+ if (pNetFlt->u.s.WinIf.StateFlags.fStandBy == FALSE)
+ {
+ if (pNetFlt->u.s.WinIf.MpIndicatedMediaStatus != pNetFlt->u.s.WinIf.MpUnindicatedMediaStatus)
+ {
+ NdisMIndicateStatus(pNetFlt->u.s.WinIf.hMiniport, pNetFlt->u.s.WinIf.MpUnindicatedMediaStatus, NULL, 0);
+ NdisMIndicateStatusComplete(pNetFlt->u.s.WinIf.hMiniport);
+ pNetFlt->u.s.WinIf.MpIndicatedMediaStatus = pNetFlt->u.s.WinIf.MpUnindicatedMediaStatus;
+ }
+ }
+ else
+ {
+ pNetFlt->u.s.WinIf.MpUnindicatedMediaStatus = pNetFlt->u.s.WinIf.MpIndicatedMediaStatus;
+ }
+#endif
+
+ return NDIS_STATUS_SUCCESS;
+}
+
+#ifndef VBOXNETADP
+static NDIS_STATUS vboxNetFltWinMpRequestPostSet(PVBOXNETFLTINS pNetFlt)
+{
+ if (pNetFlt->u.s.WinIf.PassDownRequest.DATA.SET_INFORMATION.Oid == OID_GEN_CURRENT_PACKET_FILTER && VBOXNETFLT_PROMISCUOUS_SUPPORTED(pNetFlt))
+ {
+ /* need to disable cleaning promiscuous here ?? */
+ bool fNetFltActive;
+ const bool fWinIfActive = vboxNetFltWinReferenceWinIfNetFlt(pNetFlt, &fNetFltActive);
+
+ Assert(pNetFlt->u.s.WinIf.PassDownRequest.DATA.SET_INFORMATION.InformationBuffer);
+ Assert(!pNetFlt->u.s.WinIf.StateFlags.fProcessingPacketFilter);
+
+ if (fNetFltActive)
+ {
+ Assert(fWinIfActive);
+
+ /* netflt is active, update the cached value */
+ /** @todo in case we are are not in promiscuous now, we are issuing a request.
+ * what should we do in case of a failure?
+ * i.e. should we update the fUpperProtocolSetFilter in completion routine in this case? etc. */
+ pNetFlt->u.s.WinIf.fUpperProtocolSetFilter = *((PULONG)pNetFlt->u.s.WinIf.PassDownRequest.DATA.SET_INFORMATION.InformationBuffer);
+ pNetFlt->u.s.WinIf.StateFlags.fUpperProtSetFilterInitialized = TRUE;
+
+ if (!(pNetFlt->u.s.WinIf.fOurSetFilter & NDIS_PACKET_TYPE_PROMISCUOUS))
+ {
+ pNetFlt->u.s.WinIf.fSetFilterBuffer = NDIS_PACKET_TYPE_PROMISCUOUS;
+ pNetFlt->u.s.WinIf.PassDownRequest.DATA.SET_INFORMATION.InformationBuffer = &pNetFlt->u.s.WinIf.fSetFilterBuffer;
+ pNetFlt->u.s.WinIf.PassDownRequest.DATA.SET_INFORMATION.InformationBufferLength = sizeof (pNetFlt->u.s.WinIf.fSetFilterBuffer);
+ pNetFlt->u.s.WinIf.StateFlags.fProcessingPacketFilter = 1;
+ pNetFlt->u.s.WinIf.StateFlags.fPPFNetFlt = 1;
+ /* we'll do dereferencing in request complete */
+ }
+ else
+ {
+ vboxNetFltWinDereferenceNetFlt(pNetFlt);
+ vboxNetFltWinDereferenceWinIf(pNetFlt);
+
+ /* we've intercepted the query and completed it */
+ vboxNetFltWinMpRequestStateComplete(pNetFlt);
+ return NDIS_STATUS_SUCCESS;
+ }
+ }
+ else if (fWinIfActive)
+ {
+ pNetFlt->u.s.WinIf.StateFlags.fProcessingPacketFilter = 1;
+ pNetFlt->u.s.WinIf.StateFlags.fPPFNetFlt = 0;
+ /* dereference on completion */
+ }
+ }
+
+ NDIS_STATUS Status;
+
+ NdisRequest(&Status, pNetFlt->u.s.WinIf.hBinding, &pNetFlt->u.s.WinIf.PassDownRequest);
+ if (Status != NDIS_STATUS_PENDING)
+ {
+ vboxNetFltWinPtRequestComplete(pNetFlt, &pNetFlt->u.s.WinIf.PassDownRequest, Status);
+ }
+
+ return Status;
+}
+
+DECLHIDDEN(NDIS_STATUS) vboxNetFltWinMpRequestPost(PVBOXNETFLTINS pNetFlt)
+{
+ switch (pNetFlt->u.s.WinIf.PassDownRequest.RequestType)
+ {
+ case NdisRequestQueryInformation:
+ return vboxNetFltWinMpRequestPostQuery(pNetFlt);
+ case NdisRequestSetInformation:
+ return vboxNetFltWinMpRequestPostSet(pNetFlt);
+ default:
+ AssertBreakpoint();
+ return NDIS_STATUS_FAILURE;
+ }
+}
+
+static NDIS_STATUS vboxNetFltWinMpSetInformation(IN NDIS_HANDLE MiniportAdapterContext,
+ IN NDIS_OID Oid,
+ IN PVOID InformationBuffer,
+ IN ULONG InformationBufferLength,
+ OUT PULONG BytesRead,
+ OUT PULONG BytesNeeded)
+{
+ PVBOXNETFLTINS pNetFlt = (PVBOXNETFLTINS)MiniportAdapterContext;
+ NDIS_STATUS Status = NDIS_STATUS_FAILURE;
+
+ LogFlowFunc(("ENTER: pNetFlt (0x%p), Oid (%s)\n", pNetFlt, vboxNetFltWinMpDumpOid(Oid)));
+
+ switch (Oid)
+ {
+ case OID_PNP_SET_POWER:
+ {
+ if (InformationBufferLength >= sizeof (NDIS_DEVICE_POWER_STATE))
+ {
+ NDIS_DEVICE_POWER_STATE *penmState = (NDIS_DEVICE_POWER_STATE*)InformationBuffer;
+ Status = vboxNetFltWinMpHandlePowerState(pNetFlt, *penmState);
+ }
+ else
+ {
+ Status = NDIS_STATUS_INVALID_LENGTH;
+ }
+
+ if (Status == NDIS_STATUS_SUCCESS)
+ {
+ *BytesRead = sizeof (NDIS_DEVICE_POWER_STATE);
+ *BytesNeeded = 0;
+ }
+ else
+ {
+ *BytesRead = 0;
+ *BytesNeeded = sizeof (NDIS_DEVICE_POWER_STATE);
+ }
+ break;
+ }
+ default:
+ {
+ /* the oid is to be passed down,
+ * check the device state if we can do it
+ * and update device state accordingly */
+ UINT uOp = vboxNetFltWinMpRequestStatePrep(pNetFlt, &Status);
+ if (uOp)
+ {
+ /* save the request info */
+ pNetFlt->u.s.WinIf.PassDownRequest.RequestType = NdisRequestSetInformation;
+ pNetFlt->u.s.WinIf.PassDownRequest.DATA.SET_INFORMATION.Oid = Oid;
+ pNetFlt->u.s.WinIf.PassDownRequest.DATA.SET_INFORMATION.InformationBuffer = InformationBuffer;
+ pNetFlt->u.s.WinIf.PassDownRequest.DATA.SET_INFORMATION.InformationBufferLength = InformationBufferLength;
+ pNetFlt->u.s.WinIf.pcPDRBytesNeeded = BytesNeeded;
+ pNetFlt->u.s.WinIf.pcPDRBytesRW = BytesRead;
+
+ /* the oid can be processed */
+ if (!(uOp & VBOXNDISREQUEST_QUEUED))
+ {
+ Status = vboxNetFltWinMpRequestPostSet(pNetFlt);
+ }
+ }
+ break;
+ }
+ }
+
+ LogFlowFunc(("LEAVE: pNetFlt (0x%p), Oid (%s), Status (0x%x)\n", pNetFlt, vboxNetFltWinMpDumpOid(Oid), Status));
+
+ return Status;
+}
+#else
+static NDIS_OID g_vboxNetFltWinMpSupportedOids[] =
+{
+ OID_GEN_SUPPORTED_LIST,
+ OID_GEN_HARDWARE_STATUS,
+ OID_GEN_MEDIA_SUPPORTED,
+ OID_GEN_MEDIA_IN_USE,
+ OID_GEN_MAXIMUM_LOOKAHEAD,
+ OID_GEN_CURRENT_LOOKAHEAD,
+ OID_GEN_MAXIMUM_FRAME_SIZE,
+ OID_GEN_MAXIMUM_TOTAL_SIZE,
+ OID_GEN_TRANSMIT_BLOCK_SIZE,
+ OID_GEN_RECEIVE_BLOCK_SIZE,
+ OID_GEN_MAC_OPTIONS,
+ OID_GEN_LINK_SPEED,
+ OID_GEN_TRANSMIT_BUFFER_SPACE,
+ OID_GEN_RECEIVE_BUFFER_SPACE,
+ OID_GEN_VENDOR_ID,
+ OID_GEN_VENDOR_DESCRIPTION,
+ OID_GEN_VENDOR_DRIVER_VERSION,
+ OID_GEN_DRIVER_VERSION,
+ OID_GEN_MAXIMUM_SEND_PACKETS,
+ OID_GEN_MEDIA_CONNECT_STATUS,
+ OID_GEN_CURRENT_PACKET_FILTER,
+ OID_PNP_CAPABILITIES,
+ OID_PNP_QUERY_POWER,
+ OID_GEN_XMIT_OK,
+ OID_GEN_RCV_OK,
+ OID_GEN_XMIT_ERROR,
+ OID_GEN_RCV_ERROR,
+ OID_GEN_RCV_NO_BUFFER,
+ OID_GEN_RCV_CRC_ERROR,
+ OID_GEN_TRANSMIT_QUEUE_LENGTH,
+ OID_PNP_SET_POWER,
+ OID_802_3_PERMANENT_ADDRESS,
+ OID_802_3_CURRENT_ADDRESS,
+ OID_802_3_MULTICAST_LIST,
+ OID_802_3_MAC_OPTIONS,
+ OID_802_3_MAXIMUM_LIST_SIZE,
+ OID_802_3_RCV_ERROR_ALIGNMENT,
+ OID_802_3_XMIT_ONE_COLLISION,
+ OID_802_3_XMIT_MORE_COLLISIONS,
+ OID_802_3_XMIT_DEFERRED,
+ OID_802_3_XMIT_MAX_COLLISIONS,
+ OID_802_3_RCV_OVERRUN,
+ OID_802_3_XMIT_UNDERRUN,
+ OID_802_3_XMIT_HEARTBEAT_FAILURE,
+ OID_802_3_XMIT_TIMES_CRS_LOST,
+ OID_802_3_XMIT_LATE_COLLISIONS,
+};
+
+static NDIS_STATUS vboxNetFltWinMpQueryInformation(IN NDIS_HANDLE MiniportAdapterContext,
+ IN NDIS_OID Oid,
+ IN PVOID InformationBuffer,
+ IN ULONG InformationBufferLength,
+ OUT PULONG BytesWritten,
+ OUT PULONG BytesNeeded)
+{
+ /* static data */
+ static const NDIS_HARDWARE_STATUS enmHwStatus = NdisHardwareStatusReady;
+ static const NDIS_MEDIUM enmMedium = NdisMedium802_3;
+ static NDIS_PNP_CAPABILITIES PnPCaps = {0};
+ static BOOLEAN bPnPCapsInited = FALSE;
+
+ PVBOXNETFLTINS pNetFlt = (PVBOXNETFLTINS)MiniportAdapterContext;
+ NDIS_STATUS Status = NDIS_STATUS_SUCCESS;
+ ULONG64 u64Info = 0;
+ ULONG u32Info = 0;
+ USHORT u16Info = 0;
+ /* default is 4bytes */
+ const void* pvInfo = (void*)&u32Info;
+ ULONG cbInfo = sizeof (u32Info);
+
+ LogFlowFunc(("ENTER: pNetFlt (0x%p), Oid (%s)\n", pNetFlt, vboxNetFltWinMpDumpOid(Oid)));
+
+ *BytesWritten = 0;
+ *BytesNeeded = 0;
+
+ switch (Oid)
+ {
+ case OID_GEN_SUPPORTED_LIST:
+ pvInfo = g_vboxNetFltWinMpSupportedOids;
+ cbInfo = sizeof (g_vboxNetFltWinMpSupportedOids);
+ break;
+
+ case OID_GEN_HARDWARE_STATUS:
+ pvInfo = &enmHwStatus;
+ cbInfo = sizeof (NDIS_HARDWARE_STATUS);
+ break;
+
+ case OID_GEN_MEDIA_SUPPORTED:
+ case OID_GEN_MEDIA_IN_USE:
+ pvInfo = &enmMedium;
+ cbInfo = sizeof (NDIS_MEDIUM);
+ break;
+
+ case OID_GEN_MAXIMUM_LOOKAHEAD:
+ case OID_GEN_CURRENT_LOOKAHEAD:
+ u32Info = VBOXNETADP_MAX_LOOKAHEAD_SIZE;
+ break;
+
+ case OID_GEN_MAXIMUM_FRAME_SIZE:
+ u32Info = VBOXNETADP_MAX_PACKET_SIZE - VBOXNETADP_HEADER_SIZE;
+ break;
+
+ case OID_GEN_MAXIMUM_TOTAL_SIZE:
+ case OID_GEN_TRANSMIT_BLOCK_SIZE:
+ case OID_GEN_RECEIVE_BLOCK_SIZE:
+ u32Info = VBOXNETADP_MAX_PACKET_SIZE;
+ break;
+
+ case OID_GEN_MAC_OPTIONS:
+ u32Info = NDIS_MAC_OPTION_COPY_LOOKAHEAD_DATA |
+ NDIS_MAC_OPTION_TRANSFERS_NOT_PEND |
+ NDIS_MAC_OPTION_NO_LOOPBACK;
+ break;
+
+ case OID_GEN_LINK_SPEED:
+ u32Info = VBOXNETADP_LINK_SPEED;
+ break;
+
+ case OID_GEN_TRANSMIT_BUFFER_SPACE:
+ case OID_GEN_RECEIVE_BUFFER_SPACE:
+ u32Info = VBOXNETADP_MAX_PACKET_SIZE * VBOXNETFLT_PACKET_INFO_POOL_SIZE;
+ break;
+
+ case OID_GEN_VENDOR_ID:
+ u32Info = VBOXNETADP_VENDOR_ID;
+ break;
+
+ case OID_GEN_VENDOR_DESCRIPTION:
+ pvInfo = VBOXNETADP_VENDOR_DESC;
+ cbInfo = sizeof (VBOXNETADP_VENDOR_DESC);
+ break;
+
+ case OID_GEN_VENDOR_DRIVER_VERSION:
+ u32Info = VBOXNETADP_VENDOR_DRIVER_VERSION;
+ break;
+
+ case OID_GEN_DRIVER_VERSION:
+ u16Info = (USHORT)((VBOXNETFLT_VERSION_MP_NDIS_MAJOR << 8) + VBOXNETFLT_VERSION_MP_NDIS_MINOR);
+ pvInfo = (PVOID)&u16Info;
+ cbInfo = sizeof (USHORT);
+ break;
+
+ case OID_GEN_MAXIMUM_SEND_PACKETS:
+ u32Info = VBOXNETFLT_PACKET_INFO_POOL_SIZE;
+ break;
+
+ case OID_GEN_MEDIA_CONNECT_STATUS:
+#ifdef VBOXNETADP_REPORT_DISCONNECTED
+ {
+ bool bNetFltActive;
+ bool bActive = vboxNetFltWinReferenceWinIfNetFltFromAdapt(pNetFlt, bNetFltActive);
+ if (bActive && bNetFltActive)
+ {
+ u32Info = NdisMediaStateConnected;
+ }
+ else
+ {
+ u32Info = NdisMediaStateDisconnected;
+ }
+
+ if (bActive)
+ {
+ vboxNetFltWinDereferenceWinIf(pNetFlt);
+ }
+ if (bNetFltActive)
+ {
+ vboxNetFltWinDereferenceNetFlt(pNetFlt);
+ }
+ else
+ {
+ vboxNetFltWinDereferenceModePassThru(pNetFlt);
+ }
+ }
+#else
+ u32Info = NdisMediaStateConnected;
+#endif
+ break;
+
+ case OID_GEN_CURRENT_PACKET_FILTER:
+ u32Info = NDIS_PACKET_TYPE_BROADCAST
+ | NDIS_PACKET_TYPE_DIRECTED
+ | NDIS_PACKET_TYPE_ALL_FUNCTIONAL
+ | NDIS_PACKET_TYPE_ALL_LOCAL
+ | NDIS_PACKET_TYPE_GROUP
+ | NDIS_PACKET_TYPE_MULTICAST;
+ break;
+
+ case OID_PNP_CAPABILITIES:
+ if (!bPnPCapsInited)
+ {
+ PnPCaps.WakeUpCapabilities.MinMagicPacketWakeUp = NdisDeviceStateUnspecified;
+ PnPCaps.WakeUpCapabilities.MinPatternWakeUp = NdisDeviceStateUnspecified;
+ bPnPCapsInited = TRUE;
+ }
+ cbInfo = sizeof (NDIS_PNP_CAPABILITIES);
+ pvInfo = &PnPCaps;
+
+ break;
+
+ case OID_PNP_QUERY_POWER:
+ Status = NDIS_STATUS_SUCCESS;
+ break;
+
+ case OID_GEN_XMIT_OK:
+ u64Info = pNetFlt->u.s.WinIf.cTxSuccess;
+ pvInfo = &u64Info;
+ if (InformationBufferLength >= sizeof (ULONG64) || InformationBufferLength == 0)
+ {
+ cbInfo = sizeof (ULONG64);
+ }
+ else
+ {
+ cbInfo = sizeof (ULONG);
+ }
+ *BytesNeeded = sizeof (ULONG64);
+ break;
+
+ case OID_GEN_RCV_OK:
+ u64Info = pNetFlt->u.s.WinIf.cRxSuccess;
+ pvInfo = &u64Info;
+ if (InformationBufferLength >= sizeof (ULONG64) || InformationBufferLength == 0)
+ {
+ cbInfo = sizeof (ULONG64);
+ }
+ else
+ {
+ cbInfo = sizeof (ULONG);
+ }
+ *BytesNeeded = sizeof (ULONG64);
+ break;
+
+ case OID_GEN_XMIT_ERROR:
+ u32Info = pNetFlt->u.s.WinIf.cTxError;
+ break;
+
+ case OID_GEN_RCV_ERROR:
+ u32Info = pNetFlt->u.s.WinIf.cRxError;
+ break;
+
+ case OID_GEN_RCV_NO_BUFFER:
+ case OID_GEN_RCV_CRC_ERROR:
+ u32Info = 0;
+ break;
+
+ case OID_GEN_TRANSMIT_QUEUE_LENGTH:
+ u32Info = VBOXNETFLT_PACKET_INFO_POOL_SIZE;
+ break;
+
+ case OID_802_3_PERMANENT_ADDRESS:
+ pvInfo = &pNetFlt->u.s.MacAddr;
+ cbInfo = VBOXNETADP_ETH_ADDRESS_LENGTH;
+ break;
+
+ case OID_802_3_CURRENT_ADDRESS:
+ pvInfo = &pNetFlt->u.s.MacAddr;
+ cbInfo = VBOXNETADP_ETH_ADDRESS_LENGTH;
+ break;
+
+ case OID_802_3_MAXIMUM_LIST_SIZE:
+ u32Info = VBOXNETADP_MAX_MCAST_LIST;
+ break;
+
+ case OID_802_3_MAC_OPTIONS:
+ case OID_802_3_RCV_ERROR_ALIGNMENT:
+ case OID_802_3_XMIT_ONE_COLLISION:
+ case OID_802_3_XMIT_MORE_COLLISIONS:
+ case OID_802_3_XMIT_DEFERRED:
+ case OID_802_3_XMIT_MAX_COLLISIONS:
+ case OID_802_3_RCV_OVERRUN:
+ case OID_802_3_XMIT_UNDERRUN:
+ case OID_802_3_XMIT_HEARTBEAT_FAILURE:
+ case OID_802_3_XMIT_TIMES_CRS_LOST:
+ case OID_802_3_XMIT_LATE_COLLISIONS:
+ u32Info = 0;
+ break;
+
+ default:
+ Status = NDIS_STATUS_NOT_SUPPORTED;
+ break;
+ }
+
+ if (Status == NDIS_STATUS_SUCCESS)
+ {
+ if (cbInfo <= InformationBufferLength)
+ {
+ *BytesWritten = cbInfo;
+ if (cbInfo)
+ NdisMoveMemory(InformationBuffer, pvInfo, cbInfo);
+ }
+ else
+ {
+ *BytesNeeded = cbInfo;
+ Status = NDIS_STATUS_INVALID_LENGTH;
+ }
+ }
+
+
+ LogFlowFunc(("LEAVE: pNetFlt (0x%p), Oid (%s), Status (0x%x)\n", pNetFlt, vboxNetFltWinMpDumpOid(Oid), Status));
+
+ return Status;
+}
+
+static NDIS_STATUS vboxNetFltWinMpSetInformation(IN NDIS_HANDLE MiniportAdapterContext,
+ IN NDIS_OID Oid,
+ IN PVOID InformationBuffer,
+ IN ULONG InformationBufferLength,
+ OUT PULONG BytesRead,
+ OUT PULONG BytesNeeded)
+{
+ PVBOXNETFLTINS pNetFlt = (PVBOXNETFLTINS) MiniportAdapterContext;
+ NDIS_STATUS Status = NDIS_STATUS_SUCCESS;
+
+ LogFlowFunc(("ENTER: pNetFlt (0x%p), Oid (%s)\n", pNetFlt, vboxNetFltWinMpDumpOid(Oid)));
+
+ *BytesRead = 0;
+ *BytesNeeded = 0;
+
+ switch (Oid)
+ {
+ case OID_802_3_MULTICAST_LIST:
+ *BytesRead = InformationBufferLength;
+ if (InformationBufferLength % VBOXNETADP_ETH_ADDRESS_LENGTH)
+ {
+ Status = NDIS_STATUS_INVALID_LENGTH;
+ break;
+ }
+
+ if (InformationBufferLength > (VBOXNETADP_MAX_MCAST_LIST * VBOXNETADP_ETH_ADDRESS_LENGTH))
+ {
+ Status = NDIS_STATUS_MULTICAST_FULL;
+ *BytesNeeded = VBOXNETADP_MAX_MCAST_LIST * VBOXNETADP_ETH_ADDRESS_LENGTH;
+ break;
+ }
+ break;
+
+ case OID_GEN_CURRENT_PACKET_FILTER:
+ if (InformationBufferLength != sizeof (ULONG))
+ {
+ *BytesNeeded = sizeof (ULONG);
+ Status = NDIS_STATUS_INVALID_LENGTH;
+ break;
+ }
+
+ *BytesRead = InformationBufferLength;
+
+ break;
+
+ case OID_GEN_CURRENT_LOOKAHEAD:
+ if (InformationBufferLength != sizeof (ULONG)){
+ *BytesNeeded = sizeof(ULONG);
+ Status = NDIS_STATUS_INVALID_LENGTH;
+ break;
+ }
+
+ break;
+
+ case OID_PNP_SET_POWER:
+ if (InformationBufferLength >= sizeof(NDIS_DEVICE_POWER_STATE))
+ {
+ NDIS_DEVICE_POWER_STATE *penmState = (NDIS_DEVICE_POWER_STATE*)InformationBuffer;
+ Status = vboxNetFltWinMpHandlePowerState(pNetFlt, *penmState);
+ }
+ else
+ {
+ Status = NDIS_STATUS_INVALID_LENGTH;
+ }
+
+ if (Status == NDIS_STATUS_SUCCESS)
+ {
+ *BytesRead = sizeof (NDIS_DEVICE_POWER_STATE);
+ *BytesNeeded = 0;
+ }
+ else
+ {
+ *BytesRead = 0;
+ *BytesNeeded = sizeof (NDIS_DEVICE_POWER_STATE);
+ }
+ break;
+
+ default:
+ Status = NDIS_STATUS_INVALID_OID;
+ break;
+ }
+
+ LogFlowFunc(("LEAVE: pNetFlt (0x%p), Oid (%s), Status (0x%x)\n", pNetFlt, vboxNetFltWinMpDumpOid(Oid), Status));
+
+ return Status;
+}
+
+#endif
+
+#define VBOXNETFLTDUMP_STRCASE(_t) \
+ case _t: return #_t;
+#define VBOXNETFLTDUMP_STRCASE_UNKNOWN() \
+ default: /*AssertFailed();*/ return "Unknown";
+
+static const char* vboxNetFltWinMpDumpOid(ULONG oid)
+{
+ switch (oid)
+ {
+ VBOXNETFLTDUMP_STRCASE(OID_GEN_SUPPORTED_LIST)
+ VBOXNETFLTDUMP_STRCASE(OID_GEN_HARDWARE_STATUS)
+ VBOXNETFLTDUMP_STRCASE(OID_GEN_MEDIA_SUPPORTED)
+ VBOXNETFLTDUMP_STRCASE(OID_GEN_MEDIA_IN_USE)
+ VBOXNETFLTDUMP_STRCASE(OID_GEN_MAXIMUM_LOOKAHEAD)
+ VBOXNETFLTDUMP_STRCASE(OID_GEN_MAXIMUM_FRAME_SIZE)
+ VBOXNETFLTDUMP_STRCASE(OID_GEN_LINK_SPEED)
+ VBOXNETFLTDUMP_STRCASE(OID_GEN_TRANSMIT_BUFFER_SPACE)
+ VBOXNETFLTDUMP_STRCASE(OID_GEN_RECEIVE_BUFFER_SPACE)
+ VBOXNETFLTDUMP_STRCASE(OID_GEN_TRANSMIT_BLOCK_SIZE)
+ VBOXNETFLTDUMP_STRCASE(OID_GEN_RECEIVE_BLOCK_SIZE)
+ VBOXNETFLTDUMP_STRCASE(OID_GEN_VENDOR_ID)
+ VBOXNETFLTDUMP_STRCASE(OID_GEN_VENDOR_DESCRIPTION)
+ VBOXNETFLTDUMP_STRCASE(OID_GEN_CURRENT_PACKET_FILTER)
+ VBOXNETFLTDUMP_STRCASE(OID_GEN_CURRENT_LOOKAHEAD)
+ VBOXNETFLTDUMP_STRCASE(OID_GEN_DRIVER_VERSION)
+ VBOXNETFLTDUMP_STRCASE(OID_GEN_MAXIMUM_TOTAL_SIZE)
+ VBOXNETFLTDUMP_STRCASE(OID_GEN_PROTOCOL_OPTIONS)
+ VBOXNETFLTDUMP_STRCASE(OID_GEN_MAC_OPTIONS)
+ VBOXNETFLTDUMP_STRCASE(OID_GEN_MEDIA_CONNECT_STATUS)
+ VBOXNETFLTDUMP_STRCASE(OID_GEN_MAXIMUM_SEND_PACKETS)
+ VBOXNETFLTDUMP_STRCASE(OID_GEN_VENDOR_DRIVER_VERSION)
+ VBOXNETFLTDUMP_STRCASE(OID_GEN_SUPPORTED_GUIDS)
+ VBOXNETFLTDUMP_STRCASE(OID_GEN_NETWORK_LAYER_ADDRESSES)
+ VBOXNETFLTDUMP_STRCASE(OID_GEN_TRANSPORT_HEADER_OFFSET)
+ VBOXNETFLTDUMP_STRCASE(OID_GEN_MACHINE_NAME)
+ VBOXNETFLTDUMP_STRCASE(OID_GEN_RNDIS_CONFIG_PARAMETER)
+ VBOXNETFLTDUMP_STRCASE(OID_GEN_VLAN_ID)
+ VBOXNETFLTDUMP_STRCASE(OID_GEN_MEDIA_CAPABILITIES)
+ VBOXNETFLTDUMP_STRCASE(OID_GEN_PHYSICAL_MEDIUM)
+ VBOXNETFLTDUMP_STRCASE(OID_GEN_XMIT_OK)
+ VBOXNETFLTDUMP_STRCASE(OID_GEN_RCV_OK)
+ VBOXNETFLTDUMP_STRCASE(OID_GEN_XMIT_ERROR)
+ VBOXNETFLTDUMP_STRCASE(OID_GEN_RCV_ERROR)
+ VBOXNETFLTDUMP_STRCASE(OID_GEN_RCV_NO_BUFFER)
+ VBOXNETFLTDUMP_STRCASE(OID_GEN_DIRECTED_BYTES_XMIT)
+ VBOXNETFLTDUMP_STRCASE(OID_GEN_DIRECTED_FRAMES_XMIT)
+ VBOXNETFLTDUMP_STRCASE(OID_GEN_MULTICAST_BYTES_XMIT)
+ VBOXNETFLTDUMP_STRCASE(OID_GEN_MULTICAST_FRAMES_XMIT)
+ VBOXNETFLTDUMP_STRCASE(OID_GEN_BROADCAST_BYTES_XMIT)
+ VBOXNETFLTDUMP_STRCASE(OID_GEN_BROADCAST_FRAMES_XMIT)
+ VBOXNETFLTDUMP_STRCASE(OID_GEN_DIRECTED_BYTES_RCV)
+ VBOXNETFLTDUMP_STRCASE(OID_GEN_DIRECTED_FRAMES_RCV)
+ VBOXNETFLTDUMP_STRCASE(OID_GEN_MULTICAST_BYTES_RCV)
+ VBOXNETFLTDUMP_STRCASE(OID_GEN_MULTICAST_FRAMES_RCV)
+ VBOXNETFLTDUMP_STRCASE(OID_GEN_BROADCAST_BYTES_RCV)
+ VBOXNETFLTDUMP_STRCASE(OID_GEN_BROADCAST_FRAMES_RCV)
+ VBOXNETFLTDUMP_STRCASE(OID_GEN_RCV_CRC_ERROR)
+ VBOXNETFLTDUMP_STRCASE(OID_GEN_TRANSMIT_QUEUE_LENGTH)
+ VBOXNETFLTDUMP_STRCASE(OID_GEN_GET_TIME_CAPS)
+ VBOXNETFLTDUMP_STRCASE(OID_GEN_GET_NETCARD_TIME)
+ VBOXNETFLTDUMP_STRCASE(OID_GEN_NETCARD_LOAD)
+ VBOXNETFLTDUMP_STRCASE(OID_GEN_DEVICE_PROFILE)
+ VBOXNETFLTDUMP_STRCASE(OID_GEN_INIT_TIME_MS)
+ VBOXNETFLTDUMP_STRCASE(OID_GEN_RESET_COUNTS)
+ VBOXNETFLTDUMP_STRCASE(OID_GEN_MEDIA_SENSE_COUNTS)
+ VBOXNETFLTDUMP_STRCASE(OID_PNP_CAPABILITIES)
+ VBOXNETFLTDUMP_STRCASE(OID_PNP_SET_POWER)
+ VBOXNETFLTDUMP_STRCASE(OID_PNP_QUERY_POWER)
+ VBOXNETFLTDUMP_STRCASE(OID_PNP_ADD_WAKE_UP_PATTERN)
+ VBOXNETFLTDUMP_STRCASE(OID_PNP_REMOVE_WAKE_UP_PATTERN)
+ VBOXNETFLTDUMP_STRCASE(OID_PNP_ENABLE_WAKE_UP)
+ VBOXNETFLTDUMP_STRCASE(OID_802_3_PERMANENT_ADDRESS)
+ VBOXNETFLTDUMP_STRCASE(OID_802_3_CURRENT_ADDRESS)
+ VBOXNETFLTDUMP_STRCASE(OID_802_3_MULTICAST_LIST)
+ VBOXNETFLTDUMP_STRCASE(OID_802_3_MAXIMUM_LIST_SIZE)
+ VBOXNETFLTDUMP_STRCASE(OID_802_3_MAC_OPTIONS)
+ VBOXNETFLTDUMP_STRCASE(OID_802_3_RCV_ERROR_ALIGNMENT)
+ VBOXNETFLTDUMP_STRCASE(OID_802_3_XMIT_ONE_COLLISION)
+ VBOXNETFLTDUMP_STRCASE(OID_802_3_XMIT_MORE_COLLISIONS)
+ VBOXNETFLTDUMP_STRCASE(OID_802_3_XMIT_DEFERRED)
+ VBOXNETFLTDUMP_STRCASE(OID_802_3_XMIT_MAX_COLLISIONS)
+ VBOXNETFLTDUMP_STRCASE(OID_802_3_RCV_OVERRUN)
+ VBOXNETFLTDUMP_STRCASE(OID_802_3_XMIT_UNDERRUN)
+ VBOXNETFLTDUMP_STRCASE(OID_802_3_XMIT_HEARTBEAT_FAILURE)
+ VBOXNETFLTDUMP_STRCASE(OID_802_3_XMIT_TIMES_CRS_LOST)
+ VBOXNETFLTDUMP_STRCASE(OID_802_3_XMIT_LATE_COLLISIONS)
+ VBOXNETFLTDUMP_STRCASE(OID_TCP_TASK_OFFLOAD)
+ VBOXNETFLTDUMP_STRCASE(OID_TCP_TASK_IPSEC_ADD_SA)
+ VBOXNETFLTDUMP_STRCASE(OID_TCP_TASK_IPSEC_DELETE_SA)
+ VBOXNETFLTDUMP_STRCASE(OID_TCP_SAN_SUPPORT)
+ VBOXNETFLTDUMP_STRCASE(OID_TCP_TASK_IPSEC_ADD_UDPESP_SA)
+ VBOXNETFLTDUMP_STRCASE(OID_TCP_TASK_IPSEC_DELETE_UDPESP_SA)
+ VBOXNETFLTDUMP_STRCASE_UNKNOWN()
+ }
+}
+
+DECLHIDDEN(VOID) vboxNetFltWinMpReturnPacket(IN NDIS_HANDLE hMiniportAdapterContext, IN PNDIS_PACKET pPacket)
+{
+ PVBOXNETFLTINS pNetFlt = (PVBOXNETFLTINS)hMiniportAdapterContext;
+ PVBOXNETFLT_PKTRSVD_MP pInfo = (PVBOXNETFLT_PKTRSVD_MP)pPacket->MiniportReserved;
+ PNDIS_PACKET pOrigPacket = pInfo->pOrigPacket;
+ PVOID pBufToFree = pInfo->pBufToFree;
+
+ LogFlowFunc(("ENTER: pNetFlt (0x%p)\n", pNetFlt));
+
+ if (pOrigPacket)
+ {
+ /* the packet was sent from underlying miniport */
+ NdisFreePacket(pPacket);
+ NdisReturnPackets(&pOrigPacket, 1);
+ }
+ else
+ {
+ /* the packet was sent from IntNet or it is a packet we allocated on PtReceive for TransferData processing */
+ vboxNetFltWinFreeSGNdisPacket(pPacket, !pBufToFree /* bFreeMem */);
+ }
+
+ if (pBufToFree)
+ {
+ vboxNetFltWinMemFree(pBufToFree);
+ }
+
+ vboxNetFltWinDereferenceWinIf(pNetFlt);
+
+ LogFlowFunc(("LEAVE: pNetFlt (0x%p)\n", pNetFlt));
+}
+
+static NDIS_STATUS vboxNetFltWinMpTransferData(OUT PNDIS_PACKET Packet,
+ OUT PUINT BytesTransferred,
+ IN NDIS_HANDLE hContext,
+ IN NDIS_HANDLE MiniportReceiveContext,
+ IN UINT ByteOffset,
+ IN UINT BytesToTransfer)
+{
+#ifndef VBOXNETADP
+ PVBOXNETFLTINS pNetFlt = (PVBOXNETFLTINS)hContext;
+ NDIS_STATUS Status;
+
+ LogFlowFunc(("ENTER: pNetFlt (0x%p)\n", pNetFlt));
+
+ if ( vboxNetFltWinGetPowerState(&pNetFlt->u.s.WinIf.PtState) != NdisDeviceStateD0
+ || vboxNetFltWinGetPowerState(&pNetFlt->u.s.WinIf.MpState) != NdisDeviceStateD0)
+ {
+ LogFlowFunc(("LEAVE: pNetFlt (0x%p), Status (0x%x)\n", pNetFlt, NDIS_STATUS_FAILURE));
+ return NDIS_STATUS_FAILURE;
+ }
+
+ NdisTransferData(&Status, pNetFlt->u.s.WinIf.hBinding, MiniportReceiveContext,
+ ByteOffset, BytesToTransfer, Packet, BytesTransferred);
+
+ LogFlowFunc(("LEAVE: pNetFlt (0x%p), Status (0x%x)\n", pNetFlt, Status));
+ return Status;
+
+#else
+ RT_NOREF6(Packet, BytesTransferred, hContext, MiniportReceiveContext, ByteOffset, BytesToTransfer);
+ LogFlowFunc(("ENTER: pNetFlt (0x%p)\n", hContext));
+ /* should never be here */
+ AssertFailed();
+ LogFlowFunc(("LEAVE: pNetFlt (0x%p), Status (0x%x)\n", hContext, NDIS_STATUS_FAILURE));
+ return NDIS_STATUS_FAILURE;
+#endif
+}
+
+static void vboxNetFltWinMpHalt(IN NDIS_HANDLE hContext)
+{
+ PVBOXNETFLTINS pNetFlt = (PVBOXNETFLTINS)hContext;
+ NDIS_STATUS Status;
+
+ LogFlowFunc(("ENTER: pNetFlt (0x%p)\n", pNetFlt));
+
+#ifndef VBOXNETADP
+ if (vboxNetFltWinGetWinIfState(pNetFlt) == kVBoxWinIfState_Disconnecting)
+ {
+ Assert(vboxNetFltWinGetOpState(&pNetFlt->u.s.WinIf.MpState) == kVBoxNetDevOpState_Deinitializing);
+ vboxNetFltWinSetOpState(&pNetFlt->u.s.WinIf.MpState, kVBoxNetDevOpState_Deinitializing);
+
+ vboxNetFltWinPtCloseInterface(pNetFlt, &Status);
+
+ Assert(vboxNetFltWinGetOpState(&pNetFlt->u.s.WinIf.PtState) == kVBoxNetDevOpState_Deinitializing);
+ vboxNetFltWinSetOpState(&pNetFlt->u.s.WinIf.PtState, kVBoxNetDevOpState_Deinitialized);
+ vboxNetFltWinSetOpState(&pNetFlt->u.s.WinIf.MpState, kVBoxNetDevOpState_Deinitialized);
+ }
+ else
+#endif
+ {
+ /* we're NOT called from protocolUnbinAdapter, perform a full disconnect */
+ Assert(vboxNetFltWinGetOpState(&pNetFlt->u.s.WinIf.MpState) == kVBoxNetDevOpState_Initialized);
+#ifndef VBOXNETADP
+ AssertBreakpoint();
+#endif
+ Status = vboxNetFltWinDetachFromInterface(pNetFlt, false);
+ Assert(Status == NDIS_STATUS_SUCCESS);
+ }
+
+ LogFlowFunc(("LEAVE: pNetFlt (0x%p)\n", pNetFlt));
+}
+
+/**
+ * register the miniport edge
+ */
+DECLHIDDEN(NDIS_STATUS) vboxNetFltWinMpRegister(PVBOXNETFLTGLOBALS_MP pGlobalsMp, PDRIVER_OBJECT pDriverObject, PUNICODE_STRING pRegistryPathStr)
+{
+ NDIS_MINIPORT_CHARACTERISTICS MpChars;
+
+ NdisMInitializeWrapper(&pGlobalsMp->hNdisWrapper, pDriverObject, pRegistryPathStr, NULL);
+
+ NdisZeroMemory(&MpChars, sizeof (MpChars));
+
+ MpChars.MajorNdisVersion = VBOXNETFLT_VERSION_MP_NDIS_MAJOR;
+ MpChars.MinorNdisVersion = VBOXNETFLT_VERSION_MP_NDIS_MINOR;
+
+ MpChars.HaltHandler = vboxNetFltWinMpHalt;
+ MpChars.InitializeHandler = vboxNetFltWinMpInitialize;
+ MpChars.QueryInformationHandler = vboxNetFltWinMpQueryInformation;
+ MpChars.SetInformationHandler = vboxNetFltWinMpSetInformation;
+ MpChars.TransferDataHandler = vboxNetFltWinMpTransferData;
+ MpChars.ReturnPacketHandler = vboxNetFltWinMpReturnPacket;
+ MpChars.SendPacketsHandler = vboxNetFltWinMpSendPackets;
+
+#ifndef VBOXNETADP
+ NDIS_STATUS Status = NdisIMRegisterLayeredMiniport(pGlobalsMp->hNdisWrapper, &MpChars, sizeof (MpChars), &pGlobalsMp->hMiniport);
+#else
+ NDIS_STATUS Status = NdisMRegisterMiniport(pGlobalsMp->hNdisWrapper, &MpChars, sizeof (MpChars));
+#endif
+ Assert(Status == NDIS_STATUS_SUCCESS);
+ if (Status == NDIS_STATUS_SUCCESS)
+ {
+ NdisMRegisterUnloadHandler(pGlobalsMp->hNdisWrapper, vboxNetFltWinUnload);
+ }
+
+ return Status;
+}
+
+/**
+ * deregister the miniport edge
+ */
+DECLHIDDEN(VOID) vboxNetFltWinMpDeregister(PVBOXNETFLTGLOBALS_MP pGlobalsMp)
+{
+#ifndef VBOXNETADP
+ NdisIMDeregisterLayeredMiniport(pGlobalsMp->hMiniport);
+#endif
+ NdisTerminateWrapper(pGlobalsMp->hNdisWrapper, NULL);
+
+ NdisZeroMemory(pGlobalsMp, sizeof (*pGlobalsMp));
+}
+
+#ifndef VBOXNETADP
+
+DECLHIDDEN(NDIS_STATUS) vboxNetFltWinMpInitializeDevideInstance(PVBOXNETFLTINS pThis)
+{
+ NDIS_STATUS Status;
+ Assert(vboxNetFltWinGetOpState(&pThis->u.s.WinIf.MpState) == kVBoxNetDevOpState_Deinitialized);
+ vboxNetFltWinSetOpState(&pThis->u.s.WinIf.MpState, kVBoxNetDevOpState_Initializing);
+
+ Status = NdisIMInitializeDeviceInstanceEx(g_VBoxNetFltGlobalsWin.Mp.hMiniport,
+ &pThis->u.s.WinIf.MpDeviceName,
+ pThis);
+ if (Status == NDIS_STATUS_SUCCESS)
+ {
+ if (pThis->u.s.WinIf.OpenCloseStatus == NDIS_STATUS_SUCCESS)
+ {
+ return NDIS_STATUS_SUCCESS;
+ }
+ AssertBreakpoint();
+ vboxNetFltWinMpDeInitializeDeviceInstance(pThis, &Status);
+ Assert(vboxNetFltWinGetOpState(&pThis->u.s.WinIf.MpState) == kVBoxNetDevOpState_Deinitialized);
+ vboxNetFltWinSetOpState(&pThis->u.s.WinIf.MpState, kVBoxNetDevOpState_Deinitialized);
+ return pThis->u.s.WinIf.OpenCloseStatus;
+ }
+
+ Assert(vboxNetFltWinGetOpState(&pThis->u.s.WinIf.MpState) == kVBoxNetDevOpState_Deinitialized);
+ vboxNetFltWinSetOpState(&pThis->u.s.WinIf.MpState, kVBoxNetDevOpState_Deinitialized);
+
+ return Status;
+}
+
+DECLHIDDEN(bool) vboxNetFltWinMpDeInitializeDeviceInstance(PVBOXNETFLTINS pThis, PNDIS_STATUS pStatus)
+{
+ NDIS_STATUS Status;
+
+ if (vboxNetFltWinGetOpState(&pThis->u.s.WinIf.MpState) == kVBoxNetDevOpState_Initializing)
+ {
+ Status = NdisIMCancelInitializeDeviceInstance(g_VBoxNetFltGlobalsWin.Mp.hMiniport, &pThis->u.s.WinIf.MpDeviceName);
+ if (Status == NDIS_STATUS_SUCCESS)
+ {
+ /* we've canceled the initialization successfully */
+ Assert(pThis->u.s.WinIf.hMiniport == NULL);
+ Assert(vboxNetFltWinGetOpState(&pThis->u.s.WinIf.MpState) == kVBoxNetDevOpState_Deinitialized);
+ vboxNetFltWinSetOpState(&pThis->u.s.WinIf.MpState, kVBoxNetDevOpState_Deinitialized);
+ }
+ else
+ NdisWaitEvent(&pThis->u.s.WinIf.MpInitCompleteEvent, 0);
+ }
+ else
+ Status = NDIS_STATUS_SUCCESS;
+
+ Assert( vboxNetFltWinGetOpState(&pThis->u.s.WinIf.MpState) == kVBoxNetDevOpState_Initialized
+ || vboxNetFltWinGetOpState(&pThis->u.s.WinIf.MpState) == kVBoxNetDevOpState_Deinitialized);
+ if (vboxNetFltWinGetOpState(&pThis->u.s.WinIf.MpState) == kVBoxNetDevOpState_Initialized)
+ {
+ vboxNetFltWinSetOpState(&pThis->u.s.WinIf.MpState, kVBoxNetDevOpState_Deinitializing);
+
+ Status = NdisIMDeInitializeDeviceInstance(pThis->u.s.WinIf.hMiniport);
+
+ vboxNetFltWinSetOpState(&pThis->u.s.WinIf.MpState, kVBoxNetDevOpState_Deinitialized);
+ if (Status != NDIS_STATUS_SUCCESS)
+ Status = NDIS_STATUS_FAILURE;
+
+ *pStatus = Status;
+ return true;
+ }
+
+ Assert(vboxNetFltWinGetOpState(&pThis->u.s.WinIf.MpState) == kVBoxNetDevOpState_Deinitialized);
+ vboxNetFltWinSetOpState(&pThis->u.s.WinIf.MpState, kVBoxNetDevOpState_Deinitialized);
+
+ *pStatus = Status;
+ return false;
+}
+
+#endif /* !VBOXNETADP */
diff --git a/src/VBox/HostDrivers/VBoxNetFlt/win/drv/VBoxNetFltM-win.h b/src/VBox/HostDrivers/VBoxNetFlt/win/drv/VBoxNetFltM-win.h
new file mode 100644
index 00000000..7a904aa8
--- /dev/null
+++ b/src/VBox/HostDrivers/VBoxNetFlt/win/drv/VBoxNetFltM-win.h
@@ -0,0 +1,67 @@
+/* $Id: VBoxNetFltM-win.h $ */
+/** @file
+ * VBoxNetFltM-win.h - Bridged Networking Driver, Windows Specific Code - Miniport edge API
+ */
+
+/*
+ * Copyright (C) 2011-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+#ifndef VBOX_INCLUDED_SRC_VBoxNetFlt_win_drv_VBoxNetFltM_win_h
+#define VBOX_INCLUDED_SRC_VBoxNetFlt_win_drv_VBoxNetFltM_win_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+DECLHIDDEN(NDIS_STATUS) vboxNetFltWinMpRegister(PVBOXNETFLTGLOBALS_MP pGlobalsMp, PDRIVER_OBJECT pDriverObject, PUNICODE_STRING pRegistryPathStr);
+DECLHIDDEN(VOID) vboxNetFltWinMpDeregister(PVBOXNETFLTGLOBALS_MP pGlobalsMp);
+DECLHIDDEN(VOID) vboxNetFltWinMpReturnPacket(IN NDIS_HANDLE hMiniportAdapterContext, IN PNDIS_PACKET pPacket);
+
+#ifdef VBOXNETADP
+DECLHIDDEN(NDIS_STATUS) vboxNetFltWinMpDoInitialization(PVBOXNETFLTINS pThis, NDIS_HANDLE hMiniportAdapter, NDIS_HANDLE hWrapperConfigurationContext);
+DECLHIDDEN(NDIS_STATUS) vboxNetFltWinMpDoDeinitialization(PVBOXNETFLTINS pThis);
+
+#else
+
+DECLHIDDEN(NDIS_STATUS) vboxNetFltWinMpInitializeDevideInstance(PVBOXNETFLTINS pThis);
+DECLHIDDEN(bool) vboxNetFltWinMpDeInitializeDeviceInstance(PVBOXNETFLTINS pThis, PNDIS_STATUS pStatus);
+
+DECLINLINE(VOID) vboxNetFltWinMpRequestStateComplete(PVBOXNETFLTINS pNetFlt)
+{
+ RTSpinlockAcquire(pNetFlt->hSpinlock);
+ pNetFlt->u.s.WinIf.StateFlags.fRequestInfo = 0;
+ RTSpinlockRelease(pNetFlt->hSpinlock);
+}
+
+DECLHIDDEN(NDIS_STATUS) vboxNetFltWinMpRequestPost(PVBOXNETFLTINS pNetFlt);
+#endif
+
+#endif /* !VBOX_INCLUDED_SRC_VBoxNetFlt_win_drv_VBoxNetFltM_win_h */
+
diff --git a/src/VBox/HostDrivers/VBoxNetFlt/win/drv/VBoxNetFltM.inf b/src/VBox/HostDrivers/VBoxNetFlt/win/drv/VBoxNetFltM.inf
new file mode 100644
index 00000000..fcf30fc8
--- /dev/null
+++ b/src/VBox/HostDrivers/VBoxNetFlt/win/drv/VBoxNetFltM.inf
@@ -0,0 +1,82 @@
+; $Id: VBoxNetFltM.inf $
+;; @file
+; VBoxNetFltM.inf - VirtualBox Bridged Networking Driver inf file Miniport edge
+;
+
+;
+; Copyright (C) 2011-2023 Oracle and/or its affiliates.
+;
+; This file is part of VirtualBox base platform packages, as
+; available from https://www.virtualbox.org.
+;
+; This program is free software; you can redistribute it and/or
+; modify it under the terms of the GNU General Public License
+; as published by the Free Software Foundation, in version 3 of the
+; License.
+;
+; This program is distributed in the hope that it will be useful, but
+; WITHOUT ANY WARRANTY; without even the implied warranty of
+; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+; General Public License for more details.
+;
+; You should have received a copy of the GNU General Public License
+; along with this program; if not, see <https://www.gnu.org/licenses>.
+;
+; The contents of this file may alternatively be used under the terms
+; of the Common Development and Distribution License Version 1.0
+; (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+; in the VirtualBox distribution, in which case the provisions of the
+; CDDL are applicable instead of those of the GPL.
+;
+; You may elect to license modified versions of this file under the
+; terms and conditions of either the GPL or the CDDL or both.
+;
+; SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+;
+
+[Version]
+signature = "$Windows NT$"
+;cat CatalogFile = VBoxNetFlt.cat
+Class = Net
+ClassGUID = {4d36e972-e325-11ce-bfc1-08002be10318}
+Provider = %ORACLE%
+;DriverPackageType=NdisImMiniport
+;DriverPackageDisplayName=%VBoxNetFltMP_Desc%
+;edit-DriverVer=08/13/2008,1.1.0.1
+
+[ControlFlags]
+ExcludeFromSelect = sun_VBoxNetFltmp
+
+[DestinationDirs]
+DefaultDestDir=12
+; No files to copy
+
+[Manufacturer]
+%ORACLE% = VBoxNetFltMP@COMMA-NT-ARCH@
+
+[VBoxNetFltMP@DOT-NT-ARCH@]
+%VBoxNetFltMP_Desc% = VBoxNetFltMP.ndi, sun_VBoxNetFltmp
+
+[VBoxNetFltMP.ndi]
+Characteristics = 0x29 ;NCF_NOT_USER_REMOVABLE | NCF_VIRTUAL | NCF_HIDDEN
+CopyFiles =
+
+[VBoxNetFltMP.ndi.Services]
+AddService = VBoxNetFlt,0x2, VBoxNetFltMP.AddService
+
+[VBoxNetFltMP.AddService]
+ServiceType = 1 ;SERVICE_KERNEL_DRIVER
+StartType = 3 ;SERVICE_DEMAND_START
+ErrorControl = 1 ;SERVICE_ERROR_NORMAL
+ServiceBinary = %12%\VBoxNetFlt.sys
+
+[VBoxNetFltMP.AddService.AddReg]
+
+[Strings]
+ORACLE = "Oracle Corporation"
+VBoxNetFltMP_Desc = "VirtualBox Bridged Networking Driver Miniport"
+
+[SourceDisksNames]
+
+[SourceDisksFiles]
+
diff --git a/src/VBox/HostDrivers/VBoxNetFlt/win/drv/VBoxNetFltP-win.cpp b/src/VBox/HostDrivers/VBoxNetFlt/win/drv/VBoxNetFltP-win.cpp
new file mode 100644
index 00000000..3e06f3c2
--- /dev/null
+++ b/src/VBox/HostDrivers/VBoxNetFlt/win/drv/VBoxNetFltP-win.cpp
@@ -0,0 +1,1595 @@
+/* $Id: VBoxNetFltP-win.cpp $ */
+/** @file
+ * VBoxNetFltP-win.cpp - Bridged Networking Driver, Windows Specific Code.
+ * Protocol edge
+ */
+/*
+ * Copyright (C) 2011-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+#include "VBoxNetFltCmn-win.h"
+
+#ifdef VBOXNETADP
+# error "No protocol edge"
+#endif
+
+#define VBOXNETFLT_PT_STATUS_IS_FILTERED(_s) (\
+ (_s) == NDIS_STATUS_MEDIA_CONNECT \
+ || (_s) == NDIS_STATUS_MEDIA_DISCONNECT \
+ )
+
+/**
+ * performs binding to the given adapter
+ */
+DECLHIDDEN(NDIS_STATUS) vboxNetFltWinPtDoBinding(PVBOXNETFLTINS pThis, PNDIS_STRING pOurDeviceName, PNDIS_STRING pBindToDeviceName)
+{
+ Assert(pThis->u.s.WinIf.PtState.PowerState == NdisDeviceStateD3);
+ Assert(pThis->u.s.WinIf.PtState.OpState == kVBoxNetDevOpState_Deinitialized);
+ Assert(KeGetCurrentIrql() == PASSIVE_LEVEL);
+
+ vboxNetFltWinSetOpState(&pThis->u.s.WinIf.PtState, kVBoxNetDevOpState_Initializing);
+
+ NDIS_STATUS Status = vboxNetFltWinCopyString(&pThis->u.s.WinIf.MpDeviceName, pOurDeviceName);
+ Assert (Status == NDIS_STATUS_SUCCESS);
+ if (Status == NDIS_STATUS_SUCCESS)
+ {
+ vboxNetFltWinSetPowerState(&pThis->u.s.WinIf.PtState, NdisDeviceStateD0);
+ pThis->u.s.WinIf.OpenCloseStatus = NDIS_STATUS_SUCCESS;
+
+ UINT iMedium;
+ NDIS_STATUS TmpStatus;
+ NDIS_MEDIUM aenmNdisMedium[] =
+ {
+ /* Ethernet */
+ NdisMedium802_3,
+ /* Wan */
+ NdisMediumWan
+ };
+
+ NdisResetEvent(&pThis->u.s.WinIf.OpenCloseEvent);
+
+ NdisOpenAdapter(&Status, &TmpStatus, &pThis->u.s.WinIf.hBinding, &iMedium,
+ aenmNdisMedium, RT_ELEMENTS(aenmNdisMedium),
+ g_VBoxNetFltGlobalsWin.Pt.hProtocol,
+ pThis,
+ pBindToDeviceName,
+ 0, /* IN UINT OpenOptions, (reserved, should be NULL) */
+ NULL /* IN PSTRING AddressingInformation OPTIONAL */
+ );
+ Assert(Status == NDIS_STATUS_PENDING || Status == STATUS_SUCCESS);
+ if (Status == NDIS_STATUS_PENDING)
+ {
+ NdisWaitEvent(&pThis->u.s.WinIf.OpenCloseEvent, 0);
+ Status = pThis->u.s.WinIf.OpenCloseStatus;
+ }
+
+ Assert(Status == NDIS_STATUS_SUCCESS);
+ if (Status == NDIS_STATUS_SUCCESS)
+ {
+ Assert(pThis->u.s.WinIf.hBinding);
+ pThis->u.s.WinIf.enmMedium = aenmNdisMedium[iMedium];
+ vboxNetFltWinSetOpState(&pThis->u.s.WinIf.PtState, kVBoxNetDevOpState_Initialized);
+
+ Status = vboxNetFltWinMpInitializeDevideInstance(pThis);
+ Assert(Status == NDIS_STATUS_SUCCESS);
+ if (Status == NDIS_STATUS_SUCCESS)
+ {
+ return NDIS_STATUS_SUCCESS;
+ }
+ else
+ {
+ LogRelFunc(("vboxNetFltWinMpInitializeDevideInstance failed, Status 0x%x\n", Status));
+ }
+
+ vboxNetFltWinSetOpState(&pThis->u.s.WinIf.PtState, kVBoxNetDevOpState_Deinitializing);
+ vboxNetFltWinPtCloseInterface(pThis, &TmpStatus);
+ Assert(TmpStatus == NDIS_STATUS_SUCCESS);
+ vboxNetFltWinSetOpState(&pThis->u.s.WinIf.PtState, kVBoxNetDevOpState_Deinitialized);
+ }
+ else
+ {
+ LogRelFunc(("NdisOpenAdapter failed, Status (0x%x)", Status));
+ }
+
+ vboxNetFltWinSetOpState(&pThis->u.s.WinIf.PtState, kVBoxNetDevOpState_Deinitialized);
+ pThis->u.s.WinIf.hBinding = NULL;
+ }
+
+ return Status;
+}
+
+static VOID vboxNetFltWinPtBindAdapter(OUT PNDIS_STATUS pStatus,
+ IN NDIS_HANDLE hBindContext,
+ IN PNDIS_STRING pDeviceNameStr,
+ IN PVOID pvSystemSpecific1,
+ IN PVOID pvSystemSpecific2)
+{
+ LogFlowFuncEnter();
+ RT_NOREF2(hBindContext, pvSystemSpecific2);
+
+ NDIS_STATUS Status;
+ NDIS_HANDLE hConfig = NULL;
+
+ NdisOpenProtocolConfiguration(&Status, &hConfig, (PNDIS_STRING)pvSystemSpecific1);
+ Assert(Status == NDIS_STATUS_SUCCESS);
+ if (Status == NDIS_STATUS_SUCCESS)
+ {
+ PNDIS_CONFIGURATION_PARAMETER pParam;
+ NDIS_STRING UppedBindStr = NDIS_STRING_CONST("UpperBindings");
+ NdisReadConfiguration(&Status, &pParam, hConfig, &UppedBindStr, NdisParameterString);
+ Assert(Status == NDIS_STATUS_SUCCESS);
+ if (Status == NDIS_STATUS_SUCCESS)
+ {
+ PVBOXNETFLTINS pNetFlt;
+ Status = vboxNetFltWinPtInitBind(&pNetFlt, &pParam->ParameterData.StringData, pDeviceNameStr);
+ Assert(Status == NDIS_STATUS_SUCCESS);
+ }
+
+ NdisCloseConfiguration(hConfig);
+ }
+
+ *pStatus = Status;
+
+ LogFlowFunc(("LEAVE: Status 0x%x\n", Status));
+}
+
+static VOID vboxNetFltWinPtOpenAdapterComplete(IN NDIS_HANDLE hProtocolBindingContext, IN NDIS_STATUS Status, IN NDIS_STATUS OpenErrorStatus)
+{
+ PVBOXNETFLTINS pNetFlt = (PVBOXNETFLTINS)hProtocolBindingContext;
+ RT_NOREF1(OpenErrorStatus);
+
+ LogFlowFunc(("ENTER: pNetFlt (0x%p), Status (0x%x), OpenErrorStatus(0x%x)\n", pNetFlt, Status, OpenErrorStatus));
+ Assert(pNetFlt->u.s.WinIf.OpenCloseStatus == NDIS_STATUS_SUCCESS);
+ Assert(Status == NDIS_STATUS_SUCCESS);
+ if (pNetFlt->u.s.WinIf.OpenCloseStatus == NDIS_STATUS_SUCCESS)
+ {
+ pNetFlt->u.s.WinIf.OpenCloseStatus = Status;
+ Assert(Status == NDIS_STATUS_SUCCESS);
+ if (Status != NDIS_STATUS_SUCCESS)
+ LogRelFunc(("Open Complete status is 0x%x", Status));
+ }
+ else
+ LogRelFunc(("Adapter maintained status is 0x%x", pNetFlt->u.s.WinIf.OpenCloseStatus));
+ NdisSetEvent(&pNetFlt->u.s.WinIf.OpenCloseEvent);
+ LogFlowFunc(("LEAVE: pNetFlt (0x%p), Status (0x%x), OpenErrorStatus(0x%x)\n", pNetFlt, Status, OpenErrorStatus));
+}
+
+static void vboxNetFltWinPtRequestsWaitComplete(PVBOXNETFLTINS pNetFlt)
+{
+ /* wait for request to complete */
+ while (vboxNetFltWinAtomicUoReadWinState(pNetFlt->u.s.WinIf.StateFlags).fRequestInfo == VBOXNDISREQUEST_INPROGRESS)
+ {
+ vboxNetFltWinSleep(2);
+ }
+
+ /*
+ * If the below miniport is going to low power state, complete the queued request
+ */
+ RTSpinlockAcquire(pNetFlt->hSpinlock);
+ if (pNetFlt->u.s.WinIf.StateFlags.fRequestInfo & VBOXNDISREQUEST_QUEUED)
+ {
+ /* mark the request as InProgress before posting it to RequestComplete */
+ pNetFlt->u.s.WinIf.StateFlags.fRequestInfo = VBOXNDISREQUEST_INPROGRESS;
+ RTSpinlockRelease(pNetFlt->hSpinlock);
+ vboxNetFltWinPtRequestComplete(pNetFlt, &pNetFlt->u.s.WinIf.PassDownRequest, NDIS_STATUS_FAILURE);
+ }
+ else
+ {
+ RTSpinlockRelease(pNetFlt->hSpinlock);
+ }
+}
+
+DECLHIDDEN(NDIS_STATUS) vboxNetFltWinPtDoUnbinding(PVBOXNETFLTINS pNetFlt, bool bOnUnbind)
+{
+ NDIS_STATUS Status;
+ uint64_t NanoTS = RTTimeSystemNanoTS();
+ int cPPUsage;
+
+ LogFlowFunc(("ENTER: pNetFlt 0x%p\n", pNetFlt));
+
+ Assert(KeGetCurrentIrql() == PASSIVE_LEVEL);
+
+ Assert(vboxNetFltWinGetOpState(&pNetFlt->u.s.WinIf.PtState) == kVBoxNetDevOpState_Initialized);
+
+ RTSpinlockAcquire(pNetFlt->hSpinlock);
+
+ ASMAtomicUoWriteBool(&pNetFlt->fDisconnectedFromHost, true);
+ ASMAtomicUoWriteBool(&pNetFlt->fRediscoveryPending, false);
+ ASMAtomicUoWriteU64(&pNetFlt->NanoTSLastRediscovery, NanoTS);
+
+ vboxNetFltWinSetOpState(&pNetFlt->u.s.WinIf.PtState, kVBoxNetDevOpState_Deinitializing);
+ if (!bOnUnbind)
+ {
+ vboxNetFltWinSetOpState(&pNetFlt->u.s.WinIf.MpState, kVBoxNetDevOpState_Deinitializing);
+ }
+
+ RTSpinlockRelease(pNetFlt->hSpinlock);
+
+ vboxNetFltWinPtRequestsWaitComplete(pNetFlt);
+
+ vboxNetFltWinWaitDereference(&pNetFlt->u.s.WinIf.MpState);
+ vboxNetFltWinWaitDereference(&pNetFlt->u.s.WinIf.PtState);
+
+ /* check packet pool is empty */
+ cPPUsage = NdisPacketPoolUsage(pNetFlt->u.s.WinIf.hSendPacketPool);
+ Assert(cPPUsage == 0);
+ cPPUsage = NdisPacketPoolUsage(pNetFlt->u.s.WinIf.hRecvPacketPool);
+ Assert(cPPUsage == 0);
+ /* for debugging only, ignore the err in release */
+ NOREF(cPPUsage);
+
+ if (!bOnUnbind || !vboxNetFltWinMpDeInitializeDeviceInstance(pNetFlt, &Status))
+ {
+ vboxNetFltWinPtCloseInterface(pNetFlt, &Status);
+ vboxNetFltWinSetOpState(&pNetFlt->u.s.WinIf.PtState, kVBoxNetDevOpState_Deinitialized);
+
+ if (!bOnUnbind)
+ {
+ Assert(vboxNetFltWinGetOpState(&pNetFlt->u.s.WinIf.MpState) == kVBoxNetDevOpState_Deinitializing);
+ vboxNetFltWinSetOpState(&pNetFlt->u.s.WinIf.MpState, kVBoxNetDevOpState_Deinitialized);
+ }
+ else
+ {
+ Assert(vboxNetFltWinGetOpState(&pNetFlt->u.s.WinIf.MpState) == kVBoxNetDevOpState_Deinitialized);
+ }
+ }
+ else
+ {
+ Assert(vboxNetFltWinGetOpState(&pNetFlt->u.s.WinIf.MpState) == kVBoxNetDevOpState_Deinitialized);
+ }
+
+ LogFlowFunc(("LEAVE: pNetFlt 0x%p\n", pNetFlt));
+
+ return Status;
+}
+
+static VOID vboxNetFltWinPtUnbindAdapter(OUT PNDIS_STATUS pStatus,
+ IN NDIS_HANDLE hContext,
+ IN NDIS_HANDLE hUnbindContext)
+{
+ PVBOXNETFLTINS pNetFlt = (PVBOXNETFLTINS)hContext;
+ RT_NOREF1(hUnbindContext);
+
+ LogFlowFunc(("ENTER: pNetFlt (0x%p)\n", pNetFlt));
+
+ *pStatus = vboxNetFltWinDetachFromInterface(pNetFlt, true);
+ Assert(*pStatus == NDIS_STATUS_SUCCESS);
+
+ LogFlowFunc(("LEAVE: pNetFlt (0x%p)\n", pNetFlt));
+}
+
+static VOID vboxNetFltWinPtUnloadProtocol()
+{
+ LogFlowFuncEnter();
+ NDIS_STATUS Status = vboxNetFltWinPtDeregister(&g_VBoxNetFltGlobalsWin.Pt);
+ Assert(Status == NDIS_STATUS_SUCCESS); NOREF(Status);
+ LogFlowFunc(("LEAVE: PtDeregister Status (0x%x)\n", Status));
+}
+
+
+static VOID vboxNetFltWinPtCloseAdapterComplete(IN NDIS_HANDLE ProtocolBindingContext, IN NDIS_STATUS Status)
+{
+ PVBOXNETFLTINS pNetFlt = (PVBOXNETFLTINS)ProtocolBindingContext;
+
+ LogFlowFunc(("ENTER: pNetFlt (0x%p), Status (0x%x)\n", pNetFlt, Status));
+ Assert(pNetFlt->u.s.WinIf.OpenCloseStatus == NDIS_STATUS_SUCCESS);
+ Assert(Status == NDIS_STATUS_SUCCESS);
+ Assert(pNetFlt->u.s.WinIf.OpenCloseStatus == NDIS_STATUS_SUCCESS);
+ if (pNetFlt->u.s.WinIf.OpenCloseStatus == NDIS_STATUS_SUCCESS)
+ {
+ pNetFlt->u.s.WinIf.OpenCloseStatus = Status;
+ }
+ NdisSetEvent(&pNetFlt->u.s.WinIf.OpenCloseEvent);
+ LogFlowFunc(("LEAVE: pNetFlt (0x%p), Status (0x%x)\n", pNetFlt, Status));
+}
+
+static VOID vboxNetFltWinPtResetComplete(IN NDIS_HANDLE hProtocolBindingContext, IN NDIS_STATUS Status)
+{
+ RT_NOREF2(hProtocolBindingContext, Status);
+ LogFlowFunc(("ENTER: pNetFlt 0x%p, Status 0x%x\n", hProtocolBindingContext, Status));
+ /*
+ * should never be here
+ */
+ AssertFailed();
+ LogFlowFunc(("LEAVE: pNetFlt 0x%p, Status 0x%x\n", hProtocolBindingContext, Status));
+}
+
+static NDIS_STATUS vboxNetFltWinPtHandleQueryInfoComplete(PVBOXNETFLTINS pNetFlt, NDIS_STATUS Status)
+{
+ PNDIS_REQUEST pRequest = &pNetFlt->u.s.WinIf.PassDownRequest;
+
+ switch (pRequest->DATA.QUERY_INFORMATION.Oid)
+ {
+ case OID_PNP_CAPABILITIES:
+ {
+ if (Status == NDIS_STATUS_SUCCESS)
+ {
+ if (pRequest->DATA.QUERY_INFORMATION.InformationBufferLength >= sizeof (NDIS_PNP_CAPABILITIES))
+ {
+ PNDIS_PNP_CAPABILITIES pPnPCaps = (PNDIS_PNP_CAPABILITIES)(pRequest->DATA.QUERY_INFORMATION.InformationBuffer);
+ PNDIS_PM_WAKE_UP_CAPABILITIES pPmWuCaps = &pPnPCaps->WakeUpCapabilities;
+ pPmWuCaps->MinMagicPacketWakeUp = NdisDeviceStateUnspecified;
+ pPmWuCaps->MinPatternWakeUp = NdisDeviceStateUnspecified;
+ pPmWuCaps->MinLinkChangeWakeUp = NdisDeviceStateUnspecified;
+ *pNetFlt->u.s.WinIf.pcPDRBytesRW = sizeof (NDIS_PNP_CAPABILITIES);
+ *pNetFlt->u.s.WinIf.pcPDRBytesNeeded = 0;
+ Status = NDIS_STATUS_SUCCESS;
+ }
+ else
+ {
+ AssertFailed();
+ *pNetFlt->u.s.WinIf.pcPDRBytesNeeded = sizeof(NDIS_PNP_CAPABILITIES);
+ Status = NDIS_STATUS_RESOURCES;
+ }
+ }
+ break;
+ }
+
+ case OID_GEN_MAC_OPTIONS:
+ {
+ if (Status == NDIS_STATUS_SUCCESS)
+ {
+ if (pRequest->DATA.QUERY_INFORMATION.InformationBufferLength >= sizeof (ULONG))
+ {
+ pNetFlt->u.s.WinIf.fMacOptions = *(PULONG)pRequest->DATA.QUERY_INFORMATION.InformationBuffer;
+#ifndef VBOX_LOOPBACK_USEFLAGS
+ /* clearing this flag tells ndis we'll handle loopback ourselves
+ * the ndis layer or nic driver below us would loopback packets as necessary */
+ *(PULONG)pRequest->DATA.QUERY_INFORMATION.InformationBuffer &= ~NDIS_MAC_OPTION_NO_LOOPBACK;
+#else
+ /* we have to catch loopbacks from the underlying driver, so no duplications will occur,
+ * just indicate NDIS to handle loopbacks for the packets coming from the protocol */
+ *(PULONG)pRequest->DATA.QUERY_INFORMATION.InformationBuffer |= NDIS_MAC_OPTION_NO_LOOPBACK;
+#endif
+ }
+ else
+ {
+ AssertFailed();
+ *pNetFlt->u.s.WinIf.pcPDRBytesNeeded = sizeof (ULONG);
+ Status = NDIS_STATUS_RESOURCES;
+ }
+ }
+ break;
+ }
+
+ case OID_GEN_CURRENT_PACKET_FILTER:
+ {
+ if (VBOXNETFLT_PROMISCUOUS_SUPPORTED(pNetFlt))
+ {
+ /* we're here _ONLY_ in the passthru mode */
+ Assert(pNetFlt->u.s.WinIf.StateFlags.fProcessingPacketFilter && !pNetFlt->u.s.WinIf.StateFlags.fPPFNetFlt);
+ if (pNetFlt->u.s.WinIf.StateFlags.fProcessingPacketFilter && !pNetFlt->u.s.WinIf.StateFlags.fPPFNetFlt)
+ {
+ Assert(pNetFlt->enmTrunkState != INTNETTRUNKIFSTATE_ACTIVE);
+ vboxNetFltWinDereferenceModePassThru(pNetFlt);
+ vboxNetFltWinDereferenceWinIf(pNetFlt);
+ }
+
+ if (Status == NDIS_STATUS_SUCCESS)
+ {
+ if (pRequest->DATA.QUERY_INFORMATION.InformationBufferLength >= sizeof (ULONG))
+ {
+ /* the filter request is issued below only in case netflt is not active,
+ * simply update the cache here */
+ /* cache the filter used by upper protocols */
+ pNetFlt->u.s.WinIf.fUpperProtocolSetFilter = *(PULONG)pRequest->DATA.QUERY_INFORMATION.InformationBuffer;
+ pNetFlt->u.s.WinIf.StateFlags.fUpperProtSetFilterInitialized = TRUE;
+ }
+ else
+ {
+ AssertFailed();
+ *pNetFlt->u.s.WinIf.pcPDRBytesNeeded = sizeof (ULONG);
+ Status = NDIS_STATUS_RESOURCES;
+ }
+ }
+ }
+ break;
+ }
+
+ default:
+ Assert(pRequest->DATA.QUERY_INFORMATION.Oid != OID_PNP_QUERY_POWER);
+ break;
+ }
+
+ *pNetFlt->u.s.WinIf.pcPDRBytesRW = pRequest->DATA.QUERY_INFORMATION.BytesWritten;
+ *pNetFlt->u.s.WinIf.pcPDRBytesNeeded = pRequest->DATA.QUERY_INFORMATION.BytesNeeded;
+
+ return Status;
+}
+
+static NDIS_STATUS vboxNetFltWinPtHandleSetInfoComplete(PVBOXNETFLTINS pNetFlt, NDIS_STATUS Status)
+{
+ PNDIS_REQUEST pRequest = &pNetFlt->u.s.WinIf.PassDownRequest;
+
+ switch (pRequest->DATA.SET_INFORMATION.Oid)
+ {
+ case OID_GEN_CURRENT_PACKET_FILTER:
+ {
+ if (VBOXNETFLT_PROMISCUOUS_SUPPORTED(pNetFlt))
+ {
+ Assert(Status == NDIS_STATUS_SUCCESS);
+ if (pNetFlt->u.s.WinIf.StateFlags.fProcessingPacketFilter)
+ {
+ if (pNetFlt->u.s.WinIf.StateFlags.fPPFNetFlt)
+ {
+ Assert(pNetFlt->enmTrunkState == INTNETTRUNKIFSTATE_ACTIVE);
+ pNetFlt->u.s.WinIf.StateFlags.fPPFNetFlt = 0;
+ if (Status == NDIS_STATUS_SUCCESS)
+ {
+ if (pRequest->DATA.SET_INFORMATION.InformationBufferLength >= sizeof (ULONG))
+ {
+ pNetFlt->u.s.WinIf.fOurSetFilter = *((PULONG)pRequest->DATA.SET_INFORMATION.InformationBuffer);
+ Assert(pNetFlt->u.s.WinIf.fOurSetFilter == NDIS_PACKET_TYPE_PROMISCUOUS);
+ }
+ else
+ {
+ AssertFailed();
+ *pNetFlt->u.s.WinIf.pcPDRBytesNeeded = sizeof (ULONG);
+ Status = NDIS_STATUS_RESOURCES;
+ }
+ }
+ vboxNetFltWinDereferenceNetFlt(pNetFlt);
+ }
+ else
+ {
+ Assert(pNetFlt->enmTrunkState != INTNETTRUNKIFSTATE_ACTIVE);
+
+ if (Status == NDIS_STATUS_SUCCESS)
+ {
+ if (pRequest->DATA.SET_INFORMATION.InformationBufferLength >= sizeof (ULONG))
+ {
+ /* the request was issued when the netflt was not active, simply update the cache here */
+ pNetFlt->u.s.WinIf.fUpperProtocolSetFilter = *((PULONG)pRequest->DATA.SET_INFORMATION.InformationBuffer);
+ pNetFlt->u.s.WinIf.StateFlags.fUpperProtSetFilterInitialized = TRUE;
+ }
+ else
+ {
+ AssertFailed();
+ *pNetFlt->u.s.WinIf.pcPDRBytesNeeded = sizeof (ULONG);
+ Status = NDIS_STATUS_RESOURCES;
+ }
+ }
+ vboxNetFltWinDereferenceModePassThru(pNetFlt);
+ }
+
+ pNetFlt->u.s.WinIf.StateFlags.fProcessingPacketFilter = 0;
+ vboxNetFltWinDereferenceWinIf(pNetFlt);
+ }
+#ifdef DEBUG_misha
+ else
+ {
+ AssertFailed();
+ }
+#endif
+ }
+ break;
+ }
+
+ default:
+ Assert(pRequest->DATA.SET_INFORMATION.Oid != OID_PNP_SET_POWER);
+ break;
+ }
+
+ *pNetFlt->u.s.WinIf.pcPDRBytesRW = pRequest->DATA.SET_INFORMATION.BytesRead;
+ *pNetFlt->u.s.WinIf.pcPDRBytesNeeded = pRequest->DATA.SET_INFORMATION.BytesNeeded;
+
+ return Status;
+}
+
+DECLHIDDEN(VOID) vboxNetFltWinPtRequestComplete(NDIS_HANDLE hContext, PNDIS_REQUEST pNdisRequest, NDIS_STATUS Status)
+{
+ PVBOXNETFLTINS pNetFlt = (PVBOXNETFLTINS)hContext;
+ PNDIS_REQUEST pSynchRequest = pNetFlt->u.s.WinIf.pSynchRequest;
+
+ LogFlowFunc(("ENTER: pNetFlt (0x%p), pNdisRequest (0x%p), Status (0x%x)\n", pNetFlt, pNdisRequest, Status));
+
+ if (pSynchRequest == pNdisRequest)
+ {
+ /* asynchronous completion of our sync request */
+ /*1.set the status */
+ pNetFlt->u.s.WinIf.SynchCompletionStatus = Status;
+ /* 2. set event */
+ KeSetEvent(&pNetFlt->u.s.WinIf.hSynchCompletionEvent, 0, FALSE);
+ /* 3. return; */
+
+ LogFlowFunc(("LEAVE: pNetFlt (0x%p), pNdisRequest (0x%p), Status (0x%x)\n", pNetFlt, pNdisRequest, Status));
+ return;
+ }
+
+ Assert(&pNetFlt->u.s.WinIf.PassDownRequest == pNdisRequest);
+ Assert(pNetFlt->u.s.WinIf.StateFlags.fRequestInfo == VBOXNDISREQUEST_INPROGRESS);
+ vboxNetFltWinMpRequestStateComplete(pNetFlt);
+
+ switch (pNdisRequest->RequestType)
+ {
+ case NdisRequestQueryInformation:
+ Status = vboxNetFltWinPtHandleQueryInfoComplete(pNetFlt, Status);
+ NdisMQueryInformationComplete(pNetFlt->u.s.WinIf.hMiniport, Status);
+ break;
+
+ case NdisRequestSetInformation:
+ Status = vboxNetFltWinPtHandleSetInfoComplete(pNetFlt, Status);
+ NdisMSetInformationComplete(pNetFlt->u.s.WinIf.hMiniport, Status);
+ break;
+
+ default:
+ AssertFailed();
+ break;
+ }
+
+ LogFlowFunc(("LEAVE: pNetFlt (0x%p), pNdisRequest (0x%p), Status (0x%x)\n", pNetFlt, pNdisRequest, Status));
+}
+
+static VOID vboxNetFltWinPtStatus(IN NDIS_HANDLE hProtocolBindingContext, IN NDIS_STATUS GeneralStatus, IN PVOID pvStatusBuffer, IN UINT cbStatusBuffer)
+{
+ PVBOXNETFLTINS pNetFlt = (PVBOXNETFLTINS)hProtocolBindingContext;
+
+ LogFlowFunc(("ENTER: pNetFlt (0x%p), GeneralStatus (0x%x)\n", pNetFlt, GeneralStatus));
+
+ if (vboxNetFltWinReferenceWinIf(pNetFlt))
+ {
+ Assert(pNetFlt->u.s.WinIf.hMiniport);
+
+ if (VBOXNETFLT_PT_STATUS_IS_FILTERED(GeneralStatus))
+ {
+ pNetFlt->u.s.WinIf.MpIndicatedMediaStatus = GeneralStatus;
+ }
+ NdisMIndicateStatus(pNetFlt->u.s.WinIf.hMiniport,
+ GeneralStatus,
+ pvStatusBuffer,
+ cbStatusBuffer);
+
+ vboxNetFltWinDereferenceWinIf(pNetFlt);
+ }
+ else
+ {
+ if (pNetFlt->u.s.WinIf.hMiniport != NULL
+ && VBOXNETFLT_PT_STATUS_IS_FILTERED(GeneralStatus)
+ )
+ {
+ pNetFlt->u.s.WinIf.MpUnindicatedMediaStatus = GeneralStatus;
+ }
+ }
+
+ LogFlowFunc(("LEAVE: pNetFlt (0x%p), GeneralStatus (0x%x)\n", pNetFlt, GeneralStatus));
+}
+
+
+static VOID vboxNetFltWinPtStatusComplete(IN NDIS_HANDLE hProtocolBindingContext)
+{
+ PVBOXNETFLTINS pNetFlt = (PVBOXNETFLTINS)hProtocolBindingContext;
+
+ LogFlowFunc(("ENTER: pNetFlt (0x%p)\n", pNetFlt));
+
+ if (vboxNetFltWinReferenceWinIf(pNetFlt))
+ {
+ NdisMIndicateStatusComplete(pNetFlt->u.s.WinIf.hMiniport);
+
+ vboxNetFltWinDereferenceWinIf(pNetFlt);
+ }
+
+ LogFlowFunc(("LEAVE: pNetFlt (0x%p)\n", pNetFlt));
+}
+
+static VOID vboxNetFltWinPtSendComplete(IN NDIS_HANDLE hProtocolBindingContext, IN PNDIS_PACKET pPacket, IN NDIS_STATUS Status)
+{
+ PVBOXNETFLTINS pNetFlt = (PVBOXNETFLTINS)hProtocolBindingContext;
+ PVBOXNETFLT_PKTRSVD_PT pSendInfo = (PVBOXNETFLT_PKTRSVD_PT)pPacket->ProtocolReserved;
+ PNDIS_PACKET pOrigPacket = pSendInfo->pOrigPacket;
+ PVOID pBufToFree = pSendInfo->pBufToFree;
+ LogFlowFunc(("ENTER: pNetFlt (0x%p), pPacket (0x%p), Status (0x%x)\n", pNetFlt, pPacket, Status));
+
+#if defined(DEBUG_NETFLT_PACKETS) || !defined(VBOX_LOOPBACK_USEFLAGS)
+ /** @todo for optimization we could check only for netflt-mode packets
+ * do it for all for now */
+ vboxNetFltWinLbRemoveSendPacket(pNetFlt, pPacket);
+#endif
+
+ if (pOrigPacket)
+ {
+ NdisIMCopySendCompletePerPacketInfo(pOrigPacket, pPacket);
+ NdisFreePacket(pPacket);
+ /* the ptk was posted from the upperlying protocol */
+ NdisMSendComplete(pNetFlt->u.s.WinIf.hMiniport, pOrigPacket, Status);
+ }
+ else
+ {
+ /* if the pOrigPacket is zero - the ptk was originated by netFlt send/receive
+ * need to free packet buffers */
+ vboxNetFltWinFreeSGNdisPacket(pPacket, !pBufToFree);
+ }
+
+ if (pBufToFree)
+ {
+ vboxNetFltWinMemFree(pBufToFree);
+ }
+
+ vboxNetFltWinDereferenceWinIf(pNetFlt);
+
+ LogFlowFunc(("LEAVE: pNetFlt (0x%p), pPacket (0x%p), Status (0x%x)\n", pNetFlt, pPacket, Status));
+}
+
+/**
+ * removes searches for the packet in the list and removes it if found
+ * @return true if the packet was found and removed, false - otherwise
+ */
+static bool vboxNetFltWinRemovePacketFromList(PVBOXNETFLT_INTERLOCKED_SINGLE_LIST pList, PNDIS_PACKET pPacket)
+{
+ PVBOXNETFLT_PKTRSVD_TRANSFERDATA_PT pTDR = (PVBOXNETFLT_PKTRSVD_TRANSFERDATA_PT)pPacket->ProtocolReserved;
+ return vboxNetFltWinInterlockedSearchListEntry(pList, &pTDR->ListEntry, true /* remove*/);
+}
+
+/**
+ * puts the packet to the tail of the list
+ */
+static void vboxNetFltWinPutPacketToList(PVBOXNETFLT_INTERLOCKED_SINGLE_LIST pList, PNDIS_PACKET pPacket, PNDIS_BUFFER pOrigBuffer)
+{
+ PVBOXNETFLT_PKTRSVD_TRANSFERDATA_PT pTDR = (PVBOXNETFLT_PKTRSVD_TRANSFERDATA_PT)pPacket->ProtocolReserved;
+ pTDR->pOrigBuffer = pOrigBuffer;
+ vboxNetFltWinInterlockedPutTail(pList, &pTDR->ListEntry);
+}
+
+static bool vboxNetFltWinPtTransferDataCompleteActive(PVBOXNETFLTINS pNetFltIf, PNDIS_PACKET pPacket, NDIS_STATUS Status)
+{
+ PNDIS_BUFFER pBuffer;
+ PVBOXNETFLT_PKTRSVD_TRANSFERDATA_PT pTDR;
+
+ if (!vboxNetFltWinRemovePacketFromList(&pNetFltIf->u.s.WinIf.TransferDataList, pPacket))
+ return false;
+
+ pTDR = (PVBOXNETFLT_PKTRSVD_TRANSFERDATA_PT)pPacket->ProtocolReserved;
+ Assert(pTDR);
+ Assert(pTDR->pOrigBuffer);
+
+ do
+ {
+ NdisUnchainBufferAtFront(pPacket, &pBuffer);
+
+ Assert(pBuffer);
+
+ NdisFreeBuffer(pBuffer);
+
+ pBuffer = pTDR->pOrigBuffer;
+
+ NdisChainBufferAtBack(pPacket, pBuffer);
+
+ /* data transfer was initiated when the netFlt was active
+ * the netFlt is still retained by us
+ * 1. check if loopback
+ * 2. enqueue packet
+ * 3. release netFlt */
+
+ if (Status == NDIS_STATUS_SUCCESS)
+ {
+
+#ifdef VBOX_LOOPBACK_USEFLAGS
+ if (vboxNetFltWinIsLoopedBackPacket(pPacket))
+ {
+ /* should not be here */
+ AssertFailed();
+ }
+#else
+ PNDIS_PACKET pLb = vboxNetFltWinLbSearchLoopBack(pNetFltIf, pPacket, false);
+ if (pLb)
+ {
+#ifndef DEBUG_NETFLT_RECV_TRANSFERDATA
+ /* should not be here */
+ AssertFailed();
+#endif
+ if (!vboxNetFltWinLbIsFromIntNet(pLb))
+ {
+ /* the packet is not from int net, need to pass it up to the host */
+ NdisMIndicateReceivePacket(pNetFltIf->u.s.WinIf.hMiniport, &pPacket, 1);
+ /* dereference NetFlt, WinIf will be dereferenced on Packet return */
+ vboxNetFltWinDereferenceNetFlt(pNetFltIf);
+ break;
+ }
+ }
+#endif
+ else
+ {
+ /* 2. enqueue */
+ /* use the same packet info to put the packet in the processing packet queue */
+ PVBOXNETFLT_PKTRSVD_MP pRecvInfo = (PVBOXNETFLT_PKTRSVD_MP)pPacket->MiniportReserved;
+
+ VBOXNETFLT_LBVERIFY(pNetFltIf, pPacket);
+
+ pRecvInfo->pOrigPacket = NULL;
+ pRecvInfo->pBufToFree = NULL;
+
+ NdisGetPacketFlags(pPacket) = 0;
+# ifdef VBOXNETFLT_NO_PACKET_QUEUE
+ if (vboxNetFltWinPostIntnet(pNetFltIf, pPacket, 0))
+ {
+ /* drop it */
+ vboxNetFltWinFreeSGNdisPacket(pPacket, true);
+ vboxNetFltWinDereferenceWinIf(pNetFltIf);
+ }
+ else
+ {
+ NdisMIndicateReceivePacket(pNetFltIf->u.s.WinIf.hMiniport, &pPacket, 1);
+ }
+ vboxNetFltWinDereferenceNetFlt(pNetFltIf);
+ break;
+# else
+ Status = vboxNetFltWinQuEnqueuePacket(pNetFltIf, pPacket, PACKET_MINE);
+ if (Status == NDIS_STATUS_SUCCESS)
+ {
+ break;
+ }
+ AssertFailed();
+# endif
+ }
+ }
+ else
+ {
+ AssertFailed();
+ }
+ /* we are here because of error either in data transfer or in enqueueing the packet */
+ vboxNetFltWinFreeSGNdisPacket(pPacket, true);
+ vboxNetFltWinDereferenceNetFlt(pNetFltIf);
+ vboxNetFltWinDereferenceWinIf(pNetFltIf);
+ } while (0);
+
+ return true;
+}
+
+static VOID vboxNetFltWinPtTransferDataComplete(IN NDIS_HANDLE hProtocolBindingContext,
+ IN PNDIS_PACKET pPacket,
+ IN NDIS_STATUS Status,
+ IN UINT cbTransferred)
+{
+ PVBOXNETFLTINS pNetFlt = (PVBOXNETFLTINS)hProtocolBindingContext;
+ LogFlowFunc(("ENTER: pNetFlt (0x%p), pPacket (0x%p), Status (0x%x), cbTransfered (%d)\n", pNetFlt, pPacket, Status, cbTransferred));
+ if (!vboxNetFltWinPtTransferDataCompleteActive(pNetFlt, pPacket, Status))
+ {
+ if (pNetFlt->u.s.WinIf.hMiniport)
+ {
+ NdisMTransferDataComplete(pNetFlt->u.s.WinIf.hMiniport,
+ pPacket,
+ Status,
+ cbTransferred);
+ }
+
+ vboxNetFltWinDereferenceWinIf(pNetFlt);
+ }
+ /* else - all processing is done with vboxNetFltWinPtTransferDataCompleteActive already */
+
+ LogFlowFunc(("LEAVE: pNetFlt (0x%p), pPacket (0x%p), Status (0x%x), cbTransfered (%d)\n", pNetFlt, pPacket, Status, cbTransferred));
+}
+
+static INT vboxNetFltWinRecvPacketPassThru(PVBOXNETFLTINS pNetFlt, PNDIS_PACKET pPacket)
+{
+ Assert(KeGetCurrentIrql() == DISPATCH_LEVEL);
+
+ PNDIS_PACKET pMyPacket;
+ NDIS_STATUS Status = vboxNetFltWinPrepareRecvPacket(pNetFlt, pPacket, &pMyPacket, true);
+ /* the Status holds the current packet status it will be checked for NDIS_STATUS_RESOURCES later
+ * (see below) */
+ Assert(pMyPacket);
+ if (pMyPacket)
+ {
+ NdisMIndicateReceivePacket(pNetFlt->u.s.WinIf.hMiniport, &pMyPacket, 1);
+ if (Status == NDIS_STATUS_RESOURCES)
+ {
+ NdisDprFreePacket(pMyPacket);
+ return 0;
+ }
+
+ return 1;
+ }
+
+ return 0;
+}
+
+/**
+ * process the packet receive in a "passthru" mode
+ */
+static NDIS_STATUS vboxNetFltWinRecvPassThru(PVBOXNETFLTINS pNetFlt, PNDIS_PACKET pPacket)
+{
+ Assert(KeGetCurrentIrql() == DISPATCH_LEVEL);
+
+ NDIS_STATUS Status;
+ PNDIS_PACKET pMyPacket;
+
+ NdisDprAllocatePacket(&Status, &pMyPacket, pNetFlt->u.s.WinIf.hRecvPacketPool);
+ Assert(Status == NDIS_STATUS_SUCCESS);
+ if (Status == NDIS_STATUS_SUCCESS)
+ {
+ vboxNetFltWinCopyPacketInfoOnRecv(pMyPacket, pPacket, true /* force NDIS_STATUS_RESOURCES */);
+ Assert(NDIS_GET_PACKET_STATUS(pMyPacket) == NDIS_STATUS_RESOURCES);
+
+ NdisMIndicateReceivePacket(pNetFlt->u.s.WinIf.hMiniport, &pMyPacket, 1);
+
+ NdisDprFreePacket(pMyPacket);
+ }
+ return Status;
+}
+
+static VOID vboxNetFltWinRecvIndicatePassThru(PVBOXNETFLTINS pNetFlt, NDIS_HANDLE MacReceiveContext,
+ PVOID pHeaderBuffer, UINT cbHeaderBuffer, PVOID pLookAheadBuffer, UINT cbLookAheadBuffer, UINT cbPacket)
+{
+ /* Note: we're using KeGetCurrentProcessorNumber, which is not entirely correct in case
+ * we're running on 64bit win7+, which can handle > 64 CPUs, however since KeGetCurrentProcessorNumber
+ * always returns the number < than the number of CPUs in the first group, we're guaranteed to have CPU index < 64
+ * @todo: use KeGetCurrentProcessorNumberEx for Win7+ 64 and dynamically extended array */
+ ULONG Proc = KeGetCurrentProcessorNumber();
+ Assert(Proc < RT_ELEMENTS(pNetFlt->u.s.WinIf.abIndicateRxComplete));
+ pNetFlt->u.s.WinIf.abIndicateRxComplete[Proc] = TRUE;
+ switch (pNetFlt->u.s.WinIf.enmMedium)
+ {
+ case NdisMedium802_3:
+ case NdisMediumWan:
+ NdisMEthIndicateReceive(pNetFlt->u.s.WinIf.hMiniport,
+ MacReceiveContext,
+ (PCHAR)pHeaderBuffer,
+ cbHeaderBuffer,
+ pLookAheadBuffer,
+ cbLookAheadBuffer,
+ cbPacket);
+ break;
+ default:
+ AssertFailed();
+ break;
+ }
+}
+
+/**
+ * process the ProtocolReceive in an "active" mode
+ *
+ * @return NDIS_STATUS_SUCCESS - the packet is processed
+ * NDIS_STATUS_PENDING - the packet is being processed, we are waiting for the ProtocolTransferDataComplete to be called
+ * NDIS_STATUS_NOT_ACCEPTED - the packet is not needed - typically this is because this is a loopback packet
+ * NDIS_STATUS_FAILURE - packet processing failed
+ */
+static NDIS_STATUS vboxNetFltWinPtReceiveActive(PVBOXNETFLTINS pNetFlt, NDIS_HANDLE MacReceiveContext, PVOID pHeaderBuffer, UINT cbHeaderBuffer,
+ PVOID pLookaheadBuffer, UINT cbLookaheadBuffer, UINT cbPacket)
+{
+ NDIS_STATUS Status = NDIS_STATUS_SUCCESS;
+
+ do
+ {
+ if (cbHeaderBuffer != VBOXNETFLT_PACKET_ETHEADER_SIZE)
+ {
+ Status = NDIS_STATUS_NOT_ACCEPTED;
+ break;
+ }
+
+#ifndef DEBUG_NETFLT_RECV_TRANSFERDATA
+ if (cbPacket == cbLookaheadBuffer)
+ {
+ PINTNETSG pSG;
+ PUCHAR pRcvData;
+#ifndef VBOX_LOOPBACK_USEFLAGS
+ PNDIS_PACKET pLb;
+#endif
+
+ /* allocate SG buffer */
+ Status = vboxNetFltWinAllocSG(cbPacket + cbHeaderBuffer, &pSG);
+ if (Status != NDIS_STATUS_SUCCESS)
+ {
+ AssertFailed();
+ break;
+ }
+
+ pRcvData = (PUCHAR)pSG->aSegs[0].pv;
+
+ NdisMoveMappedMemory(pRcvData, pHeaderBuffer, cbHeaderBuffer);
+
+ NdisCopyLookaheadData(pRcvData+cbHeaderBuffer,
+ pLookaheadBuffer,
+ cbLookaheadBuffer,
+ pNetFlt->u.s.WinIf.fMacOptions);
+#ifndef VBOX_LOOPBACK_USEFLAGS
+ pLb = vboxNetFltWinLbSearchLoopBackBySG(pNetFlt, pSG, false);
+ if (pLb)
+ {
+#ifndef DEBUG_NETFLT_RECV_NOPACKET
+ /* should not be here */
+ AssertFailed();
+#endif
+ if (!vboxNetFltWinLbIsFromIntNet(pLb))
+ {
+ PNDIS_PACKET pMyPacket;
+ pMyPacket = vboxNetFltWinNdisPacketFromSG(pNetFlt, /* PVBOXNETFLTINS */
+ pSG, /* PINTNETSG */
+ pSG, /* PVOID pBufToFree */
+ false, /* bool bToWire */
+ false); /* bool bCopyMemory */
+ if (pMyPacket)
+ {
+ NdisMIndicateReceivePacket(pNetFlt->u.s.WinIf.hMiniport, &pMyPacket, 1);
+ /* dereference the NetFlt here & indicate SUCCESS, which would mean the caller would not do a dereference
+ * the WinIf dereference will be done on packet return */
+ vboxNetFltWinDereferenceNetFlt(pNetFlt);
+ Status = NDIS_STATUS_SUCCESS;
+ }
+ else
+ {
+ vboxNetFltWinMemFree(pSG);
+ Status = NDIS_STATUS_FAILURE;
+ }
+ }
+ else
+ {
+ vboxNetFltWinMemFree(pSG);
+ Status = NDIS_STATUS_NOT_ACCEPTED;
+ }
+ break;
+ }
+#endif
+ VBOXNETFLT_LBVERIFYSG(pNetFlt, pSG);
+
+ /* enqueue SG */
+# ifdef VBOXNETFLT_NO_PACKET_QUEUE
+ if (vboxNetFltWinPostIntnet(pNetFlt, pSG, VBOXNETFLT_PACKET_SG))
+ {
+ /* drop it */
+ vboxNetFltWinMemFree(pSG);
+ vboxNetFltWinDereferenceWinIf(pNetFlt);
+ }
+ else
+ {
+ PNDIS_PACKET pMyPacket = vboxNetFltWinNdisPacketFromSG(pNetFlt, /* PVBOXNETFLTINS */
+ pSG, /* PINTNETSG */
+ pSG, /* PVOID pBufToFree */
+ false, /* bool bToWire */
+ false); /* bool bCopyMemory */
+ Assert(pMyPacket);
+ if (pMyPacket)
+ {
+ NDIS_SET_PACKET_STATUS(pMyPacket, NDIS_STATUS_SUCCESS);
+
+ DBG_CHECK_PACKET_AND_SG(pMyPacket, pSG);
+
+ LogFlow(("non-ndis packet info, packet created (%p)\n", pMyPacket));
+ NdisMIndicateReceivePacket(pNetFlt->u.s.WinIf.hMiniport, &pMyPacket, 1);
+ }
+ else
+ {
+ vboxNetFltWinDereferenceWinIf(pNetFlt);
+ Status = NDIS_STATUS_RESOURCES;
+ }
+ }
+ vboxNetFltWinDereferenceNetFlt(pNetFlt);
+# else
+ Status = vboxNetFltWinQuEnqueuePacket(pNetFlt, pSG, PACKET_SG | PACKET_MINE);
+ if (Status != NDIS_STATUS_SUCCESS)
+ {
+ AssertFailed();
+ vboxNetFltWinMemFree(pSG);
+ break;
+ }
+# endif
+#endif
+ }
+ else
+ {
+ PNDIS_PACKET pPacket;
+ PNDIS_BUFFER pTransferBuffer;
+ PNDIS_BUFFER pOrigBuffer;
+ PUCHAR pMemBuf;
+ UINT cbBuf = cbPacket + cbHeaderBuffer;
+ UINT cbTransferred;
+
+ /* allocate NDIS Packet buffer */
+ NdisAllocatePacket(&Status, &pPacket, pNetFlt->u.s.WinIf.hRecvPacketPool);
+ if (Status != NDIS_STATUS_SUCCESS)
+ {
+ AssertFailed();
+ break;
+ }
+
+ VBOXNETFLT_OOB_INIT(pPacket);
+
+#ifdef VBOX_LOOPBACK_USEFLAGS
+ /* set "don't loopback" flags */
+ NdisGetPacketFlags(pPacket) = g_VBoxNetFltGlobalsWin.fPacketDontLoopBack;
+#else
+ NdisGetPacketFlags(pPacket) = 0;
+#endif
+
+ Status = vboxNetFltWinMemAlloc((PVOID*)(&pMemBuf), cbBuf);
+ if (Status != NDIS_STATUS_SUCCESS)
+ {
+ AssertFailed();
+ NdisFreePacket(pPacket);
+ break;
+ }
+ NdisAllocateBuffer(&Status, &pTransferBuffer, pNetFlt->u.s.WinIf.hRecvBufferPool, pMemBuf + cbHeaderBuffer, cbPacket);
+ if (Status != NDIS_STATUS_SUCCESS)
+ {
+ AssertFailed();
+ Status = NDIS_STATUS_FAILURE;
+ NdisFreePacket(pPacket);
+ vboxNetFltWinMemFree(pMemBuf);
+ break;
+ }
+
+ NdisAllocateBuffer(&Status, &pOrigBuffer, pNetFlt->u.s.WinIf.hRecvBufferPool, pMemBuf, cbBuf);
+ if (Status != NDIS_STATUS_SUCCESS)
+ {
+ AssertFailed();
+ Status = NDIS_STATUS_FAILURE;
+ NdisFreeBuffer(pTransferBuffer);
+ NdisFreePacket(pPacket);
+ vboxNetFltWinMemFree(pMemBuf);
+ break;
+ }
+
+ NdisChainBufferAtBack(pPacket, pTransferBuffer);
+
+ NdisMoveMappedMemory(pMemBuf, pHeaderBuffer, cbHeaderBuffer);
+
+ vboxNetFltWinPutPacketToList(&pNetFlt->u.s.WinIf.TransferDataList, pPacket, pOrigBuffer);
+
+#ifdef DEBUG_NETFLT_RECV_TRANSFERDATA
+ if (cbPacket == cbLookaheadBuffer)
+ {
+ NdisCopyLookaheadData(pMemBuf+cbHeaderBuffer,
+ pLookaheadBuffer,
+ cbLookaheadBuffer,
+ pNetFlt->u.s.WinIf.fMacOptions);
+ }
+ else
+#endif
+ {
+ Assert(cbPacket > cbLookaheadBuffer);
+
+ NdisTransferData(&Status, pNetFlt->u.s.WinIf.hBinding, MacReceiveContext,
+ 0, /* ByteOffset */
+ cbPacket, pPacket, &cbTransferred);
+ }
+
+ if (Status != NDIS_STATUS_PENDING)
+ {
+ vboxNetFltWinPtTransferDataComplete(pNetFlt, pPacket, Status, cbTransferred);
+ }
+ }
+ } while (0);
+
+ return Status;
+}
+
+static NDIS_STATUS vboxNetFltWinPtReceive(IN NDIS_HANDLE hProtocolBindingContext,
+ IN NDIS_HANDLE MacReceiveContext,
+ IN PVOID pHeaderBuffer,
+ IN UINT cbHeaderBuffer,
+ IN PVOID pLookAheadBuffer,
+ IN UINT cbLookAheadBuffer,
+ IN UINT cbPacket)
+{
+ PVBOXNETFLTINS pNetFlt = (PVBOXNETFLTINS)hProtocolBindingContext;
+ PNDIS_PACKET pPacket = NULL;
+ NDIS_STATUS Status = NDIS_STATUS_SUCCESS;
+ bool bNetFltActive;
+ bool fWinIfActive = vboxNetFltWinReferenceWinIfNetFlt(pNetFlt, &bNetFltActive);
+ const bool bPassThruActive = !bNetFltActive;
+
+ LogFlowFunc(("ENTER: pNetFlt (0x%p)\n", pNetFlt));
+
+ if (fWinIfActive)
+ {
+ do
+ {
+#ifndef DEBUG_NETFLT_RECV_NOPACKET
+ pPacket = NdisGetReceivedPacket(pNetFlt->u.s.WinIf.hBinding, MacReceiveContext);
+ if (pPacket)
+ {
+# ifndef VBOX_LOOPBACK_USEFLAGS
+ PNDIS_PACKET pLb = NULL;
+# else
+ if (vboxNetFltWinIsLoopedBackPacket(pPacket))
+ {
+ AssertFailed();
+ /* nothing else to do here, just return the packet */
+ //NdisReturnPackets(&pPacket, 1);
+ Status = NDIS_STATUS_NOT_ACCEPTED;
+ break;
+ }
+
+ VBOXNETFLT_LBVERIFY(pNetFlt, pPacket);
+# endif
+
+ if (bNetFltActive)
+ {
+# ifndef VBOX_LOOPBACK_USEFLAGS
+ pLb = vboxNetFltWinLbSearchLoopBack(pNetFlt, pPacket, false);
+ if (!pLb)
+# endif
+ {
+ VBOXNETFLT_LBVERIFY(pNetFlt, pPacket);
+
+# ifdef VBOXNETFLT_NO_PACKET_QUEUE
+ if (vboxNetFltWinPostIntnet(pNetFlt, pPacket, 0))
+ {
+ /* drop it */
+ break;
+ }
+# else
+ Status = vboxNetFltWinQuEnqueuePacket(pNetFlt, pPacket, PACKET_COPY);
+ Assert(Status == NDIS_STATUS_SUCCESS);
+ if (Status == NDIS_STATUS_SUCCESS)
+ {
+ //NdisReturnPackets(&pPacket, 1);
+ fWinIfActive = false;
+ bNetFltActive = false;
+ break;
+ }
+# endif
+ }
+# ifndef VBOX_LOOPBACK_USEFLAGS
+ else if (vboxNetFltWinLbIsFromIntNet(pLb))
+ {
+ /* nothing else to do here, just return the packet */
+ //NdisReturnPackets(&pPacket, 1);
+ Status = NDIS_STATUS_NOT_ACCEPTED;
+ break;
+ }
+ /* we are here because this is a looped back packet set not from intnet
+ * we will post it to the upper protocol */
+# endif
+ }
+
+ Assert(Status == STATUS_SUCCESS);
+ if (Status == STATUS_SUCCESS)
+ {
+# ifndef VBOX_LOOPBACK_USEFLAGS
+ Assert(!pLb || !vboxNetFltWinLbIsFromIntNet(pLb));
+# endif
+ Status = vboxNetFltWinRecvPassThru(pNetFlt, pPacket);
+ Assert(Status == STATUS_SUCCESS);
+ /* we are done with packet processing, and we will
+ * not receive packet return event for this packet,
+ * fWinIfActive should be true to ensure we release WinIf*/
+ Assert(fWinIfActive);
+ if (Status == STATUS_SUCCESS)
+ break;
+ }
+ else
+ {
+ /* intnet processing failed - fall back to no-packet mode */
+ Assert(bNetFltActive);
+ Assert(fWinIfActive);
+ }
+
+ }
+#endif /* #ifndef DEBUG_NETFLT_RECV_NOPACKET */
+
+ if (bNetFltActive)
+ {
+ Status = vboxNetFltWinPtReceiveActive(pNetFlt, MacReceiveContext, pHeaderBuffer, cbHeaderBuffer,
+ pLookAheadBuffer, cbLookAheadBuffer, cbPacket);
+ if (NT_SUCCESS(Status))
+ {
+ if (Status != NDIS_STATUS_NOT_ACCEPTED)
+ {
+ fWinIfActive = false;
+ bNetFltActive = false;
+ }
+ else
+ {
+#ifndef VBOX_LOOPBACK_USEFLAGS
+ /* this is a loopback packet, nothing to do here */
+#else
+ AssertFailed();
+ /* should not be here */
+#endif
+ }
+ break;
+ }
+ }
+
+ /* we are done with packet processing, and we will
+ * not receive packet return event for this packet,
+ * fWinIfActive should be true to ensure we release WinIf*/
+ Assert(fWinIfActive);
+
+ vboxNetFltWinRecvIndicatePassThru(pNetFlt, MacReceiveContext, pHeaderBuffer, cbHeaderBuffer, pLookAheadBuffer, cbLookAheadBuffer, cbPacket);
+ /* the status could contain an error value here in case the IntNet recv failed,
+ * ensure we return back success status */
+ Status = NDIS_STATUS_SUCCESS;
+
+ } while (0);
+
+ if (bNetFltActive)
+ {
+ vboxNetFltWinDereferenceNetFlt(pNetFlt);
+ }
+ else if (bPassThruActive)
+ {
+ vboxNetFltWinDereferenceModePassThru(pNetFlt);
+ }
+ if (fWinIfActive)
+ {
+ vboxNetFltWinDereferenceWinIf(pNetFlt);
+ }
+ }
+ else
+ {
+ Status = NDIS_STATUS_FAILURE;
+ }
+
+ LogFlowFunc(("LEAVE: pNetFlt (0x%p)\n", pNetFlt));
+
+ return Status;
+
+}
+
+static VOID vboxNetFltWinPtReceiveComplete(NDIS_HANDLE hProtocolBindingContext)
+{
+ PVBOXNETFLTINS pNetFlt = (PVBOXNETFLTINS)hProtocolBindingContext;
+ bool fNetFltActive;
+ bool fWinIfActive = vboxNetFltWinReferenceWinIfNetFlt(pNetFlt, &fNetFltActive);
+ NDIS_HANDLE hMiniport = pNetFlt->u.s.WinIf.hMiniport;
+ /* Note: we're using KeGetCurrentProcessorNumber, which is not entirely correct in case
+ * we're running on 64bit win7+, which can handle > 64 CPUs, however since KeGetCurrentProcessorNumber
+ * always returns the number < than the number of CPUs in the first group, we're guaranteed to have CPU index < 64
+ * @todo: use KeGetCurrentProcessorNumberEx for Win7+ 64 and dynamically extended array */
+ ULONG iProc = KeGetCurrentProcessorNumber();
+ Assert(iProc < RT_ELEMENTS(pNetFlt->u.s.WinIf.abIndicateRxComplete));
+
+ LogFlowFunc(("ENTER: pNetFlt (0x%p)\n", pNetFlt));
+
+ if (hMiniport != NULL && pNetFlt->u.s.WinIf.abIndicateRxComplete[iProc])
+ {
+ switch (pNetFlt->u.s.WinIf.enmMedium)
+ {
+ case NdisMedium802_3:
+ case NdisMediumWan:
+ NdisMEthIndicateReceiveComplete(hMiniport);
+ break;
+ default:
+ AssertFailed();
+ break;
+ }
+ }
+
+ pNetFlt->u.s.WinIf.abIndicateRxComplete[iProc] = FALSE;
+
+ if (fWinIfActive)
+ {
+ if (fNetFltActive)
+ vboxNetFltWinDereferenceNetFlt(pNetFlt);
+ else
+ vboxNetFltWinDereferenceModePassThru(pNetFlt);
+ vboxNetFltWinDereferenceWinIf(pNetFlt);
+ }
+
+ LogFlowFunc(("LEAVE: pNetFlt (0x%p)\n", pNetFlt));
+}
+
+static INT vboxNetFltWinPtReceivePacket(NDIS_HANDLE hProtocolBindingContext, PNDIS_PACKET pPacket)
+{
+ PVBOXNETFLTINS pNetFlt = (PVBOXNETFLTINS)hProtocolBindingContext;
+ INT cRefCount = 0;
+ bool bNetFltActive;
+ bool fWinIfActive = vboxNetFltWinReferenceWinIfNetFlt(pNetFlt, &bNetFltActive);
+ const bool bPassThruActive = !bNetFltActive;
+
+ LogFlowFunc(("ENTER: pNetFlt (0x%p)\n", pNetFlt));
+
+ if (fWinIfActive)
+ {
+ do
+ {
+#ifdef VBOX_LOOPBACK_USEFLAGS
+ if (vboxNetFltWinIsLoopedBackPacket(pPacket))
+ {
+ AssertFailed();
+ Log(("lb_rp"));
+
+ /* nothing else to do here, just return the packet */
+ cRefCount = 0;
+ //NdisReturnPackets(&pPacket, 1);
+ break;
+ }
+
+ VBOXNETFLT_LBVERIFY(pNetFlt, pPacket);
+#endif
+
+ if (bNetFltActive)
+ {
+#ifndef VBOX_LOOPBACK_USEFLAGS
+ PNDIS_PACKET pLb = vboxNetFltWinLbSearchLoopBack(pNetFlt, pPacket, false);
+ if (!pLb)
+#endif
+ {
+#ifndef VBOXNETFLT_NO_PACKET_QUEUE
+ NDIS_STATUS fStatus;
+#endif
+ bool fResources = NDIS_GET_PACKET_STATUS(pPacket) == NDIS_STATUS_RESOURCES; NOREF(fResources);
+
+ VBOXNETFLT_LBVERIFY(pNetFlt, pPacket);
+#ifdef DEBUG_misha
+ /** @todo remove this assert.
+ * this is a temporary assert for debugging purposes:
+ * we're probably doing something wrong with the packets if the miniport reports NDIS_STATUS_RESOURCES */
+ Assert(!fResources);
+#endif
+
+#ifdef VBOXNETFLT_NO_PACKET_QUEUE
+ if (vboxNetFltWinPostIntnet(pNetFlt, pPacket, 0))
+ {
+ /* drop it */
+ cRefCount = 0;
+ break;
+ }
+
+#else
+ fStatus = vboxNetFltWinQuEnqueuePacket(pNetFlt, pPacket, fResources ? PACKET_COPY : 0);
+ if (fStatus == NDIS_STATUS_SUCCESS)
+ {
+ bNetFltActive = false;
+ fWinIfActive = false;
+ if (fResources)
+ {
+ cRefCount = 0;
+ //NdisReturnPackets(&pPacket, 1);
+ }
+ else
+ cRefCount = 1;
+ break;
+ }
+ else
+ {
+ AssertFailed();
+ }
+#endif
+ }
+#ifndef VBOX_LOOPBACK_USEFLAGS
+ else if (vboxNetFltWinLbIsFromIntNet(pLb))
+ {
+ /* the packet is from intnet, it has already been set to the host,
+ * no need for loopng it back to the host again */
+ /* nothing else to do here, just return the packet */
+ cRefCount = 0;
+ //NdisReturnPackets(&pPacket, 1);
+ break;
+ }
+#endif
+ }
+
+ cRefCount = vboxNetFltWinRecvPacketPassThru(pNetFlt, pPacket);
+ if (cRefCount)
+ {
+ Assert(cRefCount == 1);
+ fWinIfActive = false;
+ }
+
+ } while (FALSE);
+
+ if (bNetFltActive)
+ {
+ vboxNetFltWinDereferenceNetFlt(pNetFlt);
+ }
+ else if (bPassThruActive)
+ {
+ vboxNetFltWinDereferenceModePassThru(pNetFlt);
+ }
+ if (fWinIfActive)
+ {
+ vboxNetFltWinDereferenceWinIf(pNetFlt);
+ }
+ }
+ else
+ {
+ cRefCount = 0;
+ //NdisReturnPackets(&pPacket, 1);
+ }
+
+ LogFlowFunc(("LEAVE: pNetFlt (0x%p), cRefCount (%d)\n", pNetFlt, cRefCount));
+
+ return cRefCount;
+}
+
+DECLHIDDEN(bool) vboxNetFltWinPtCloseInterface(PVBOXNETFLTINS pNetFlt, PNDIS_STATUS pStatus)
+{
+ RTSpinlockAcquire(pNetFlt->hSpinlock);
+
+ if (pNetFlt->u.s.WinIf.StateFlags.fInterfaceClosing)
+ {
+ RTSpinlockRelease(pNetFlt->hSpinlock);
+ AssertFailed();
+ return false;
+ }
+ if (pNetFlt->u.s.WinIf.hBinding == NULL)
+ {
+ RTSpinlockRelease(pNetFlt->hSpinlock);
+ AssertFailed();
+ return false;
+ }
+
+ pNetFlt->u.s.WinIf.StateFlags.fInterfaceClosing = TRUE;
+ RTSpinlockRelease(pNetFlt->hSpinlock);
+
+ NdisResetEvent(&pNetFlt->u.s.WinIf.OpenCloseEvent);
+ NdisCloseAdapter(pStatus, pNetFlt->u.s.WinIf.hBinding);
+ if (*pStatus == NDIS_STATUS_PENDING)
+ {
+ NdisWaitEvent(&pNetFlt->u.s.WinIf.OpenCloseEvent, 0);
+ *pStatus = pNetFlt->u.s.WinIf.OpenCloseStatus;
+ }
+
+ Assert (*pStatus == NDIS_STATUS_SUCCESS);
+
+ pNetFlt->u.s.WinIf.hBinding = NULL;
+
+ return true;
+}
+
+static NDIS_STATUS vboxNetFltWinPtPnPSetPower(PVBOXNETFLTINS pNetFlt, NDIS_DEVICE_POWER_STATE enmPowerState)
+{
+ NDIS_DEVICE_POWER_STATE enmPrevPowerState = vboxNetFltWinGetPowerState(&pNetFlt->u.s.WinIf.PtState);
+
+ RTSpinlockAcquire(pNetFlt->hSpinlock);
+
+ vboxNetFltWinSetPowerState(&pNetFlt->u.s.WinIf.PtState, enmPowerState);
+
+ if (vboxNetFltWinGetPowerState(&pNetFlt->u.s.WinIf.PtState) > NdisDeviceStateD0)
+ {
+ if (enmPrevPowerState == NdisDeviceStateD0)
+ {
+ pNetFlt->u.s.WinIf.StateFlags.fStandBy = TRUE;
+ }
+ RTSpinlockRelease(pNetFlt->hSpinlock);
+ vboxNetFltWinPtRequestsWaitComplete(pNetFlt);
+ vboxNetFltWinWaitDereference(&pNetFlt->u.s.WinIf.MpState);
+ vboxNetFltWinWaitDereference(&pNetFlt->u.s.WinIf.PtState);
+
+ /* check packet pool is empty */
+ UINT cPPUsage = NdisPacketPoolUsage(pNetFlt->u.s.WinIf.hSendPacketPool);
+ Assert(cPPUsage == 0);
+ cPPUsage = NdisPacketPoolUsage(pNetFlt->u.s.WinIf.hRecvPacketPool);
+ Assert(cPPUsage == 0);
+ /* for debugging only, ignore the err in release */
+ NOREF(cPPUsage);
+
+ Assert(!pNetFlt->u.s.WinIf.StateFlags.fRequestInfo);
+ }
+ else
+ {
+ if (enmPrevPowerState > NdisDeviceStateD0)
+ {
+ pNetFlt->u.s.WinIf.StateFlags.fStandBy = FALSE;
+ }
+
+ if (pNetFlt->u.s.WinIf.StateFlags.fRequestInfo & VBOXNDISREQUEST_QUEUED)
+ {
+ pNetFlt->u.s.WinIf.StateFlags.fRequestInfo = VBOXNDISREQUEST_INPROGRESS;
+ RTSpinlockRelease(pNetFlt->hSpinlock);
+
+ vboxNetFltWinMpRequestPost(pNetFlt);
+ }
+ else
+ {
+ RTSpinlockRelease(pNetFlt->hSpinlock);
+ }
+ }
+
+ return NDIS_STATUS_SUCCESS;
+}
+
+
+static NDIS_STATUS vboxNetFltWinPtPnPEvent(IN NDIS_HANDLE hProtocolBindingContext, IN PNET_PNP_EVENT pNetPnPEvent)
+{
+ PVBOXNETFLTINS pNetFlt = (PVBOXNETFLTINS)hProtocolBindingContext;
+
+ LogFlowFunc(("ENTER: pNetFlt (0x%p), NetEvent (%d)\n", pNetFlt, pNetPnPEvent->NetEvent));
+
+ switch (pNetPnPEvent->NetEvent)
+ {
+ case NetEventSetPower:
+ {
+ NDIS_DEVICE_POWER_STATE enmPowerState = *((PNDIS_DEVICE_POWER_STATE)pNetPnPEvent->Buffer);
+ NDIS_STATUS rcNdis = vboxNetFltWinPtPnPSetPower(pNetFlt, enmPowerState);
+ LogFlowFunc(("LEAVE: pNetFlt (0x%p), NetEvent (%d), rcNdis=%#x\n", pNetFlt, pNetPnPEvent->NetEvent, rcNdis));
+ return rcNdis;
+ }
+
+ case NetEventReconfigure:
+ {
+ if (!pNetFlt)
+ {
+ NdisReEnumerateProtocolBindings(g_VBoxNetFltGlobalsWin.Pt.hProtocol);
+ }
+ }
+ /** @todo r=bird: Is the fall thru intentional?? */
+ default:
+ LogFlowFunc(("LEAVE: pNetFlt (0x%p), NetEvent (%d)\n", pNetFlt, pNetPnPEvent->NetEvent));
+ return NDIS_STATUS_SUCCESS;
+ }
+
+}
+
+#ifdef __cplusplus
+# define PTCHARS_40(_p) ((_p).Ndis40Chars)
+#else
+# define PTCHARS_40(_p) (_p)
+#endif
+
+/**
+ * register the protocol edge
+ */
+DECLHIDDEN(NDIS_STATUS) vboxNetFltWinPtRegister(PVBOXNETFLTGLOBALS_PT pGlobalsPt, PDRIVER_OBJECT pDriverObject,
+ PUNICODE_STRING pRegistryPathStr)
+{
+ RT_NOREF2(pDriverObject, pRegistryPathStr);
+ NDIS_PROTOCOL_CHARACTERISTICS PtChars;
+ NDIS_STRING NameStr;
+
+ NdisInitUnicodeString(&NameStr, VBOXNETFLT_NAME_PROTOCOL);
+
+ NdisZeroMemory(&PtChars, sizeof (PtChars));
+ PTCHARS_40(PtChars).MajorNdisVersion = VBOXNETFLT_VERSION_PT_NDIS_MAJOR;
+ PTCHARS_40(PtChars).MinorNdisVersion = VBOXNETFLT_VERSION_PT_NDIS_MINOR;
+
+ PTCHARS_40(PtChars).Name = NameStr;
+ PTCHARS_40(PtChars).OpenAdapterCompleteHandler = vboxNetFltWinPtOpenAdapterComplete;
+ PTCHARS_40(PtChars).CloseAdapterCompleteHandler = vboxNetFltWinPtCloseAdapterComplete;
+ PTCHARS_40(PtChars).SendCompleteHandler = vboxNetFltWinPtSendComplete;
+ PTCHARS_40(PtChars).TransferDataCompleteHandler = vboxNetFltWinPtTransferDataComplete;
+ PTCHARS_40(PtChars).ResetCompleteHandler = vboxNetFltWinPtResetComplete;
+ PTCHARS_40(PtChars).RequestCompleteHandler = vboxNetFltWinPtRequestComplete;
+ PTCHARS_40(PtChars).ReceiveHandler = vboxNetFltWinPtReceive;
+ PTCHARS_40(PtChars).ReceiveCompleteHandler = vboxNetFltWinPtReceiveComplete;
+ PTCHARS_40(PtChars).StatusHandler = vboxNetFltWinPtStatus;
+ PTCHARS_40(PtChars).StatusCompleteHandler = vboxNetFltWinPtStatusComplete;
+ PTCHARS_40(PtChars).BindAdapterHandler = vboxNetFltWinPtBindAdapter;
+ PTCHARS_40(PtChars).UnbindAdapterHandler = vboxNetFltWinPtUnbindAdapter;
+ PTCHARS_40(PtChars).UnloadHandler = vboxNetFltWinPtUnloadProtocol;
+#if !defined(DEBUG_NETFLT_RECV)
+ PTCHARS_40(PtChars).ReceivePacketHandler = vboxNetFltWinPtReceivePacket;
+#endif
+ PTCHARS_40(PtChars).PnPEventHandler = vboxNetFltWinPtPnPEvent;
+
+ NDIS_STATUS Status;
+ NdisRegisterProtocol(&Status, &pGlobalsPt->hProtocol, &PtChars, sizeof (PtChars));
+ Assert(Status == STATUS_SUCCESS);
+ return Status;
+}
+
+/**
+ * deregister the protocol edge
+ */
+DECLHIDDEN(NDIS_STATUS) vboxNetFltWinPtDeregister(PVBOXNETFLTGLOBALS_PT pGlobalsPt)
+{
+ if (!pGlobalsPt->hProtocol)
+ return NDIS_STATUS_SUCCESS;
+
+ NDIS_STATUS Status;
+
+ NdisDeregisterProtocol(&Status, pGlobalsPt->hProtocol);
+ Assert (Status == NDIS_STATUS_SUCCESS);
+ if (Status == NDIS_STATUS_SUCCESS)
+ {
+ NdisZeroMemory(pGlobalsPt, sizeof (*pGlobalsPt));
+ }
+ return Status;
+}
diff --git a/src/VBox/HostDrivers/VBoxNetFlt/win/drv/VBoxNetFltP-win.h b/src/VBox/HostDrivers/VBoxNetFlt/win/drv/VBoxNetFltP-win.h
new file mode 100644
index 00000000..11910676
--- /dev/null
+++ b/src/VBox/HostDrivers/VBoxNetFlt/win/drv/VBoxNetFltP-win.h
@@ -0,0 +1,52 @@
+/* $Id: VBoxNetFltP-win.h $ */
+/** @file
+ * VBoxNetFltP-win.h - Bridged Networking Driver, Windows Specific Code.
+ * Protocol edge API
+ */
+/*
+ * Copyright (C) 2011-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+#ifndef VBOX_INCLUDED_SRC_VBoxNetFlt_win_drv_VBoxNetFltP_win_h
+#define VBOX_INCLUDED_SRC_VBoxNetFlt_win_drv_VBoxNetFltP_win_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+#ifdef VBOXNETADP
+# error "No protocol edge"
+#endif
+DECLHIDDEN(NDIS_STATUS) vboxNetFltWinPtRegister(PVBOXNETFLTGLOBALS_PT pGlobalsPt, PDRIVER_OBJECT pDriverObject, PUNICODE_STRING pRegistryPathStr);
+DECLHIDDEN(NDIS_STATUS) vboxNetFltWinPtDeregister(PVBOXNETFLTGLOBALS_PT pGlobalsPt);
+DECLHIDDEN(NDIS_STATUS) vboxNetFltWinPtDoUnbinding(PVBOXNETFLTINS pNetFlt, bool bOnUnbind);
+DECLHIDDEN(VOID) vboxNetFltWinPtRequestComplete(NDIS_HANDLE hContext, PNDIS_REQUEST pNdisRequest, NDIS_STATUS Status);
+DECLHIDDEN(bool) vboxNetFltWinPtCloseInterface(PVBOXNETFLTINS pNetFlt, PNDIS_STATUS pStatus);
+DECLHIDDEN(NDIS_STATUS) vboxNetFltWinPtDoBinding(PVBOXNETFLTINS pThis, PNDIS_STRING pOurDeviceName, PNDIS_STRING pBindToDeviceName);
+#endif /* !VBOX_INCLUDED_SRC_VBoxNetFlt_win_drv_VBoxNetFltP_win_h */
diff --git a/src/VBox/HostDrivers/VBoxNetFlt/win/drv/VBoxNetFltRt-win.cpp b/src/VBox/HostDrivers/VBoxNetFlt/win/drv/VBoxNetFltRt-win.cpp
new file mode 100644
index 00000000..44c7338c
--- /dev/null
+++ b/src/VBox/HostDrivers/VBoxNetFlt/win/drv/VBoxNetFltRt-win.cpp
@@ -0,0 +1,3650 @@
+/* $Id: VBoxNetFltRt-win.cpp $ */
+/** @file
+ * VBoxNetFltRt-win.cpp - Bridged Networking Driver, Windows Specific Runtime Code.
+ */
+
+/*
+ * Copyright (C) 2011-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include "VBoxNetFltCmn-win.h"
+#include <VBox/intnetinline.h>
+#include <iprt/thread.h>
+
+#include <iprt/nt/tdikrnl.h>
+#include <mstcpip.h>
+
+
+/*********************************************************************************************************************************
+* Structures and Typedefs *
+*********************************************************************************************************************************/
+/** represents the job element of the job queue
+ * see comments for VBOXNETFLT_JOB_QUEUE */
+typedef struct VBOXNETFLT_JOB
+{
+ /** link in the job queue */
+ LIST_ENTRY ListEntry;
+ /** job function to be executed */
+ PFNVBOXNETFLT_JOB_ROUTINE pfnRoutine;
+ /** parameter to be passed to the job function */
+ PVOID pContext;
+ /** event that will be fired on job completion */
+ KEVENT CompletionEvent;
+ /** true if the job manager should use the completion even for completion indication, false-otherwise*/
+ bool bUseCompletionEvent;
+} VBOXNETFLT_JOB, *PVBOXNETFLT_JOB;
+
+/**
+ * represents the queue of jobs processed by the worker thread
+ *
+ * we use the thread to process tasks which are required to be done at passive level
+ * our callbacks may be called at APC level by IntNet, there are some tasks that we can not create at APC,
+ * e.g. thread creation. This is why we schedule such jobs to the worker thread working at passive level
+ */
+typedef struct VBOXNETFLT_JOB_QUEUE
+{
+ /* jobs */
+ LIST_ENTRY Jobs;
+ /* we are using ExInterlocked..List functions to access the jobs list */
+ KSPIN_LOCK Lock;
+ /** this event is used to initiate a job worker thread kill */
+ KEVENT KillEvent;
+ /** this event is used to notify a worker thread that jobs are added to the queue */
+ KEVENT NotifyEvent;
+ /** worker thread */
+ PKTHREAD pThread;
+} VBOXNETFLT_JOB_QUEUE, *PVBOXNETFLT_JOB_QUEUE;
+
+typedef struct _CREATE_INSTANCE_CONTEXT
+{
+#ifndef VBOXNETADP
+ PNDIS_STRING pOurName;
+ PNDIS_STRING pBindToName;
+#else
+ NDIS_HANDLE hMiniportAdapter;
+ NDIS_HANDLE hWrapperConfigurationContext;
+#endif
+ NDIS_STATUS Status;
+} CREATE_INSTANCE_CONTEXT, *PCREATE_INSTANCE_CONTEXT;
+
+/*contexts used for our jobs */
+/* Attach context */
+typedef struct _ATTACH_INFO
+{
+ PVBOXNETFLTINS pNetFltIf;
+ PCREATE_INSTANCE_CONTEXT pCreateContext;
+ bool fRediscovery;
+ int Status;
+} ATTACH_INFO, *PATTACH_INFO;
+
+/* general worker context */
+typedef struct _WORKER_INFO
+{
+ PVBOXNETFLTINS pNetFltIf;
+ int Status;
+} WORKER_INFO, *PWORKER_INFO;
+
+/* idc initialization */
+typedef struct _INIT_IDC_INFO
+{
+ VBOXNETFLT_JOB Job;
+ bool bInitialized;
+ volatile bool bStop;
+ volatile int rc;
+ KEVENT hCompletionEvent;
+} INIT_IDC_INFO, *PINIT_IDC_INFO;
+
+
+/*********************************************************************************************************************************
+* Global Variables *
+*********************************************************************************************************************************/
+/** global job queue. some operations are required to be done at passive level, e.g. thread creation, adapter bind/unbind initiation,
+ * while IntNet typically calls us APC_LEVEL, so we just create a system thread in our DriverEntry and enqueue the jobs to that thread */
+static VBOXNETFLT_JOB_QUEUE g_VBoxJobQueue;
+volatile static bool g_bVBoxIdcInitialized;
+INIT_IDC_INFO g_VBoxInitIdcInfo;
+
+/**
+ * The (common) global data.
+ */
+static VBOXNETFLTGLOBALS g_VBoxNetFltGlobals;
+/* win-specific global data */
+VBOXNETFLTGLOBALS_WIN g_VBoxNetFltGlobalsWin = {{{0}}};
+
+
+/*********************************************************************************************************************************
+* Defined Constants And Macros *
+*********************************************************************************************************************************/
+#define LIST_ENTRY_2_JOB(pListEntry) \
+ ( (PVBOXNETFLT_JOB)((uint8_t *)(pListEntry) - RT_UOFFSETOF(VBOXNETFLT_JOB, ListEntry)) )
+
+
+/*********************************************************************************************************************************
+* Internal Functions *
+*********************************************************************************************************************************/
+static int vboxNetFltWinAttachToInterface(PVBOXNETFLTINS pThis, void * pContext, bool fRediscovery);
+static int vboxNetFltWinConnectIt(PVBOXNETFLTINS pThis);
+static int vboxNetFltWinFiniIdc();
+static void vboxNetFltWinFiniNetFltBase();
+static int vboxNetFltWinInitNetFltBase();
+static int vboxNetFltWinFiniNetFlt();
+static int vboxNetFltWinStartInitIdcProbing();
+static int vboxNetFltWinStopInitIdcProbing();
+
+
+
+/** makes the current thread to sleep for the given number of miliseconds */
+DECLHIDDEN(void) vboxNetFltWinSleep(ULONG milis)
+{
+ RTThreadSleep(milis);
+}
+
+/** wait for the given device to be dereferenced */
+DECLHIDDEN(void) vboxNetFltWinWaitDereference(PVBOXNETFLT_WINIF_DEVICE pState)
+{
+#ifdef DEBUG
+ uint64_t StartNanoTS = RTTimeSystemNanoTS();
+ uint64_t CurNanoTS;
+#endif
+ Assert(KeGetCurrentIrql() < DISPATCH_LEVEL);
+
+ while (ASMAtomicUoReadU32((volatile uint32_t *)&pState->cReferences))
+ {
+ vboxNetFltWinSleep(2);
+#ifdef DEBUG
+ CurNanoTS = RTTimeSystemNanoTS();
+ if (CurNanoTS - StartNanoTS > 20000000)
+ {
+ LogRel(("device not idle"));
+ AssertFailed();
+// break;
+ }
+#endif
+ }
+}
+
+/**
+ * mem functions
+ */
+/* allocates and zeroes the nonpaged memory of a given size */
+DECLHIDDEN(NDIS_STATUS) vboxNetFltWinMemAlloc(PVOID *ppvMemBuf, UINT cbLength)
+{
+#ifdef DEBUG_NETFLT_USE_EXALLOC
+ *ppvMemBuf = ExAllocatePoolWithTag(NonPagedPool, cbLength, VBOXNETFLT_MEM_TAG);
+ if (*ppvMemBuf)
+ {
+ NdisZeroMemory(*ppvMemBuf, cbLength);
+ return NDIS_STATUS_SUCCESS;
+ }
+ return NDIS_STATUS_FAILURE;
+#else
+ NDIS_STATUS fStatus = NdisAllocateMemoryWithTag(ppvMemBuf, cbLength, VBOXNETFLT_MEM_TAG);
+ if (fStatus == NDIS_STATUS_SUCCESS)
+ NdisZeroMemory(*ppvMemBuf, cbLength);
+ return fStatus;
+#endif
+}
+
+/* frees memory allocated with vboxNetFltWinMemAlloc */
+DECLHIDDEN(void) vboxNetFltWinMemFree(PVOID pvMemBuf)
+{
+#ifdef DEBUG_NETFLT_USE_EXALLOC
+ ExFreePool(pvMemBuf);
+#else
+ NdisFreeMemory(pvMemBuf, 0, 0);
+#endif
+}
+
+#ifndef VBOXNETFLT_NO_PACKET_QUEUE
+
+/* initializes packet info pool and allocates the cSize packet infos for the pool */
+static NDIS_STATUS vboxNetFltWinPpAllocatePacketInfoPool(PVBOXNETFLT_PACKET_INFO_POOL pPool, UINT cSize)
+{
+ UINT cbBufSize = sizeof(PACKET_INFO)*cSize;
+ PACKET_INFO * pPacketInfos;
+ NDIS_STATUS fStatus;
+ UINT i;
+
+ Assert(cSize > 0);
+
+ INIT_INTERLOCKED_PACKET_QUEUE(&pPool->Queue);
+
+ fStatus = vboxNetFltWinMemAlloc((PVOID*)&pPacketInfos, cbBufSize);
+
+ if (fStatus == NDIS_STATUS_SUCCESS)
+ {
+ PVBOXNETFLTPACKET_INFO pInfo;
+ pPool->pBuffer = pPacketInfos;
+
+ for (i = 0; i < cSize; i++)
+ {
+ pInfo = &pPacketInfos[i];
+ vboxNetFltWinQuEnqueueTail(&pPool->Queue.Queue, pInfo);
+ pInfo->pPool = pPool;
+ }
+ }
+ else
+ {
+ AssertFailed();
+ }
+
+ return fStatus;
+}
+
+/* frees the packet info pool */
+VOID vboxNetFltWinPpFreePacketInfoPool(PVBOXNETFLT_PACKET_INFO_POOL pPool)
+{
+ vboxNetFltWinMemFree(pPool->pBuffer);
+
+ FINI_INTERLOCKED_PACKET_QUEUE(&pPool->Queue)
+}
+
+#endif
+
+/**
+ * copies one string to another. in case the destination string size is not enough to hold the complete source string
+ * does nothing and returns NDIS_STATUS_RESOURCES .
+ */
+DECLHIDDEN(NDIS_STATUS) vboxNetFltWinCopyString(PNDIS_STRING pDst, PNDIS_STRING pSrc)
+{
+ NDIS_STATUS Status = NDIS_STATUS_SUCCESS;
+
+ if (pDst != pSrc)
+ {
+ if (pDst->MaximumLength < pSrc->Length)
+ {
+ AssertFailed();
+ Status = NDIS_STATUS_RESOURCES;
+ }
+ else
+ {
+ pDst->Length = pSrc->Length;
+
+ if (pDst->Buffer != pSrc->Buffer)
+ {
+ NdisMoveMemory(pDst->Buffer, pSrc->Buffer, pSrc->Length);
+ }
+ }
+ }
+ return Status;
+}
+
+/************************************************************************************
+ * PINTNETSG pSG manipulation functions
+ ************************************************************************************/
+
+/* moves the contents of the given NDIS_BUFFER and all other buffers chained to it to the PINTNETSG
+ * the PINTNETSG is expected to contain one segment whose bugger is large enough to maintain
+ * the contents of the given NDIS_BUFFER and all other buffers chained to it */
+static NDIS_STATUS vboxNetFltWinNdisBufferMoveToSG0(PNDIS_BUFFER pBuffer, PINTNETSG pSG)
+{
+ PINTNETSEG paSeg;
+ uint8_t * ptr;
+ PVOID pVirtualAddress;
+ UINT cbCurrentLength;
+ NDIS_STATUS fStatus = NDIS_STATUS_SUCCESS;
+
+ Assert(pSG->cSegsAlloc == 1);
+
+ paSeg = pSG->aSegs;
+ ptr = (uint8_t*)paSeg->pv;
+ paSeg->cb = 0;
+ paSeg->Phys = NIL_RTHCPHYS;
+ pSG->cbTotal = 0;
+
+ Assert(paSeg->pv);
+
+ while (pBuffer)
+ {
+ NdisQueryBufferSafe(pBuffer, &pVirtualAddress, &cbCurrentLength, NormalPagePriority);
+
+ if (!pVirtualAddress)
+ {
+ fStatus = NDIS_STATUS_FAILURE;
+ break;
+ }
+
+ pSG->cbTotal += cbCurrentLength;
+ paSeg->cb += cbCurrentLength;
+ NdisMoveMemory(ptr, pVirtualAddress, cbCurrentLength);
+ ptr += cbCurrentLength;
+
+ NdisGetNextBuffer(pBuffer, &pBuffer);
+ }
+
+ if (fStatus == NDIS_STATUS_SUCCESS)
+ {
+ pSG->cSegsUsed = 1;
+ Assert(pSG->cbTotal == paSeg->cb);
+ }
+ return fStatus;
+}
+
+/* converts the PNDIS_BUFFER to PINTNETSG by making the PINTNETSG segments to point to the memory buffers the
+ * ndis buffer(s) point to (as opposed to vboxNetFltWinNdisBufferMoveToSG0 which copies the memory from ndis buffers(s) to PINTNETSG) */
+static NDIS_STATUS vboxNetFltWinNdisBuffersToSG(PNDIS_BUFFER pBuffer, PINTNETSG pSG)
+{
+ UINT cSegs = 0;
+ NDIS_STATUS Status = NDIS_STATUS_SUCCESS;
+ PVOID pVirtualAddress;
+ UINT cbCurrentLength;
+
+ while (pBuffer)
+ {
+ NdisQueryBufferSafe(pBuffer, &pVirtualAddress, &cbCurrentLength, NormalPagePriority);
+
+ if (!pVirtualAddress)
+ {
+ Status = NDIS_STATUS_FAILURE;
+ break;
+ }
+
+ pSG->cbTotal += cbCurrentLength;
+ pSG->aSegs[cSegs].cb = cbCurrentLength;
+ pSG->aSegs[cSegs].pv = pVirtualAddress;
+ pSG->aSegs[cSegs].Phys = NIL_RTHCPHYS;
+ cSegs++;
+
+ NdisGetNextBuffer(pBuffer, &pBuffer);
+ }
+
+ AssertFatal(cSegs <= pSG->cSegsAlloc);
+
+ if (Status == NDIS_STATUS_SUCCESS)
+ {
+ pSG->cSegsUsed = cSegs;
+ }
+
+ return Status;
+}
+
+static void vboxNetFltWinDeleteSG(PINTNETSG pSG)
+{
+ vboxNetFltWinMemFree(pSG);
+}
+
+static PINTNETSG vboxNetFltWinCreateSG(uint32_t cSegs)
+{
+ PINTNETSG pSG;
+ NTSTATUS Status = vboxNetFltWinMemAlloc((PVOID*)&pSG, RT_UOFFSETOF_DYN(INTNETSG, aSegs[cSegs]));
+ if (Status == STATUS_SUCCESS)
+ {
+ IntNetSgInitTempSegs(pSG, 0 /*cbTotal*/, cSegs, 0 /*cSegsUsed*/);
+ return pSG;
+ }
+
+ return NULL;
+}
+
+/************************************************************************************
+ * packet queue functions
+ ************************************************************************************/
+#ifndef VBOXNETFLT_NO_PACKET_QUEUE
+#if !defined(VBOXNETADP)
+static NDIS_STATUS vboxNetFltWinQuPostPacket(PVBOXNETFLTINS pNetFlt, PNDIS_PACKET pPacket, PINTNETSG pSG, uint32_t fFlags
+# ifdef DEBUG_NETFLT_PACKETS
+ , PNDIS_PACKET pTmpPacket
+# endif
+ )
+{
+ NDIS_STATUS Status;
+ PNDIS_PACKET pMyPacket;
+ bool bSrcHost = fFlags & PACKET_SRC_HOST;
+
+ LogFlow(("posting packet back to driver stack..\n"));
+
+ if (!pPacket)
+ {
+ /* INTNETSG was in the packet queue, create a new NdisPacket from INTNETSG*/
+ pMyPacket = vboxNetFltWinNdisPacketFromSG(pNetFlt,
+ pSG, /* PINTNETSG */
+ pSG, /* PVOID pBufToFree */
+ bSrcHost, /* bool bToWire */
+ false); /* bool bCopyMemory */
+
+ Assert(pMyPacket);
+
+ NDIS_SET_PACKET_STATUS(pMyPacket, NDIS_STATUS_SUCCESS);
+
+ DBG_CHECK_PACKET_AND_SG(pMyPacket, pSG);
+
+#ifdef DEBUG_NETFLT_PACKETS
+ Assert(pTmpPacket);
+
+ DBG_CHECK_PACKET_AND_SG(pTmpPacket, pSG);
+
+ DBG_CHECK_PACKETS(pTmpPacket, pMyPacket);
+#endif
+
+ LogFlow(("non-ndis packet info, packet created (%p)\n", pMyPacket));
+ }
+ else
+ {
+ /* NDIS_PACKET was in the packet queue */
+ DBG_CHECK_PACKET_AND_SG(pPacket, pSG);
+
+ if (!(fFlags & PACKET_MINE))
+ {
+ /* the packet is the one that was passed to us in send/receive callback
+ * According to the DDK, we can not post it further,
+ * instead we should allocate our own packet.
+ * So, allocate our own packet (pMyPacket) and copy the packet info there */
+ if (bSrcHost)
+ {
+ Status = vboxNetFltWinPrepareSendPacket(pNetFlt, pPacket, &pMyPacket/*, true*/);
+ LogFlow(("packet from wire, packet created (%p)\n", pMyPacket));
+ }
+ else
+ {
+ Status = vboxNetFltWinPrepareRecvPacket(pNetFlt, pPacket, &pMyPacket, false);
+ LogFlow(("packet from wire, packet created (%p)\n", pMyPacket));
+ }
+ }
+ else
+ {
+ /* the packet enqueued is ours, simply assign pMyPacket and zero pPacket */
+ pMyPacket = pPacket;
+ pPacket = NULL;
+ }
+ Assert(pMyPacket);
+ }
+
+ if (pMyPacket)
+ {
+ /* we have successfully initialized our packet, post it to the host or to the wire */
+ if (bSrcHost)
+ {
+#if defined(DEBUG_NETFLT_PACKETS) || !defined(VBOX_LOOPBACK_USEFLAGS)
+ vboxNetFltWinLbPutSendPacket(pNetFlt, pMyPacket, false /* bFromIntNet */);
+#endif
+ NdisSend(&Status, pNetFlt->u.s.hBinding, pMyPacket);
+
+ if (Status != NDIS_STATUS_PENDING)
+ {
+#if defined(DEBUG_NETFLT_PACKETS) || !defined(VBOX_LOOPBACK_USEFLAGS)
+ /* the status is NOT pending, complete the packet */
+ bool bTmp = vboxNetFltWinLbRemoveSendPacket(pNetFlt, pMyPacket);
+ Assert(bTmp);
+#endif
+ if (pPacket)
+ {
+ LogFlow(("status is not pending, completing packet (%p)\n", pPacket));
+
+ NdisIMCopySendCompletePerPacketInfo (pPacket, pMyPacket);
+
+ NdisFreePacket(pMyPacket);
+ }
+ else
+ {
+ /* should never be here since the PINTNETSG is stored only when the underlying miniport
+ * indicates NDIS_STATUS_RESOURCES, we should never have this when processing
+ * the "from-host" packets */
+ AssertFailed();
+ LogFlow(("status is not pending, freeing myPacket (%p)\n", pMyPacket));
+ vboxNetFltWinFreeSGNdisPacket(pMyPacket, false);
+ }
+ }
+ }
+ else
+ {
+ NdisMIndicateReceivePacket(pNetFlt->u.s.hMiniport, &pMyPacket, 1);
+
+ Status = NDIS_STATUS_PENDING;
+ /* the packet receive completion is always indicated via MiniportReturnPacket */
+ }
+ }
+ else
+ {
+ /*we failed to create our packet */
+ AssertFailed();
+ Status = NDIS_STATUS_FAILURE;
+ }
+
+ return Status;
+}
+#endif
+
+static bool vboxNetFltWinQuProcessInfo(PVBOXNETFLTINS pNetFltIf, PPACKET_QUEUE_WORKER pWorker, PVOID pvPacket, const UINT fFlags)
+#else
+DECLHIDDEN(bool) vboxNetFltWinPostIntnet(PVBOXNETFLTINS pNetFltIf, PVOID pvPacket, const UINT fFlags)
+#endif
+{
+ PNDIS_PACKET pPacket = NULL;
+ PINTNETSG pSG = NULL;
+ NDIS_STATUS Status;
+#ifndef VBOXNETADP
+ bool bSrcHost;
+ bool bDropIt;
+# ifndef VBOXNETFLT_NO_PACKET_QUEUE
+ bool bPending;
+# endif
+#endif
+#ifdef VBOXNETFLT_NO_PACKET_QUEUE
+ bool bDeleteSG = false;
+#endif
+#ifdef DEBUG_NETFLT_PACKETS
+ /* packet used for matching */
+ PNDIS_PACKET pTmpPacket = NULL;
+#endif
+
+#ifndef VBOXNETADP
+ bSrcHost = (fFlags & VBOXNETFLT_PACKET_SRC_HOST) != 0;
+#endif
+
+ /* we first need to obtain the INTNETSG to be passed to intnet */
+
+ /* the queue may contain two "types" of packets:
+ * the NDIS_PACKET and the INTNETSG.
+ * I.e. on send/receive we typically enqueue the NDIS_PACKET passed to us by ndis,
+ * however in case our ProtocolReceive is called or the packet's status is set to NDIS_STSTUS_RESOURCES
+ * in ProtocolReceivePacket, we must return the packet immediately on ProtocolReceive*** exit
+ * In this case we allocate the INTNETSG, copy the ndis packet data there and enqueue it.
+ * In this case the packet info flags has the VBOXNETFLT_PACKET_SG fag set
+ *
+ * Besides that the NDIS_PACKET contained in the queue could be either the one passed to us in our send/receive callback
+ * or the one created by us. The latter is possible in case our ProtocolReceive callback is called and we call NdisTransferData
+ * in this case we need to allocate the packet the data to be transferred to.
+ * If the enqueued packet is the one allocated by us the VBOXNETFLT_PACKET_MINE flag is set
+ * */
+ if ((fFlags & VBOXNETFLT_PACKET_SG) == 0)
+ {
+ /* we have NDIS_PACKET enqueued, we need to convert it to INTNETSG to be passed to intnet */
+ PNDIS_BUFFER pCurrentBuffer = NULL;
+ UINT cBufferCount;
+ UINT cbPacketLength;
+
+ pPacket = (PNDIS_PACKET)pvPacket;
+
+ LogFlow(("ndis packet info, packet (%p)\n", pPacket));
+
+ LogFlow(("preparing pSG"));
+ NdisQueryPacket(pPacket, NULL, &cBufferCount, &pCurrentBuffer, &cbPacketLength);
+ Assert(cBufferCount);
+
+#ifdef VBOXNETFLT_NO_PACKET_QUEUE
+ pSG = vboxNetFltWinCreateSG(cBufferCount);
+#else
+ /* we can not allocate the INTNETSG on stack since in this case we may get stack overflow
+ * somewhere outside of our driver (3 pages of system thread stack does not seem to be enough)
+ *
+ * since we have a "serialized" packet processing, i.e. all packets are being processed and passed
+ * to intnet by this thread, we just use one previously allocated INTNETSG which is stored in PVBOXNETFLTINS */
+ pSG = pWorker->pSG;
+
+ if (cBufferCount > pSG->cSegsAlloc)
+ {
+ pSG = vboxNetFltWinCreateSG(cBufferCount + 2);
+ if (pSG)
+ {
+ vboxNetFltWinDeleteSG(pWorker->pSG);
+ pWorker->pSG = pSG;
+ }
+ else
+ {
+ LogRel(("Failed to reallocate the pSG\n"));
+ }
+ }
+#endif
+
+ if (pSG)
+ {
+#ifdef VBOXNETFLT_NO_PACKET_QUEUE
+ bDeleteSG = true;
+#endif
+ /* reinitialize */
+ IntNetSgInitTempSegs(pSG, 0 /*cbTotal*/, pSG->cSegsAlloc, 0 /*cSegsUsed*/);
+
+ /* convert the ndis buffers to INTNETSG */
+ Status = vboxNetFltWinNdisBuffersToSG(pCurrentBuffer, pSG);
+ if (Status != NDIS_STATUS_SUCCESS)
+ {
+ pSG = NULL;
+ }
+ else
+ {
+ DBG_CHECK_PACKET_AND_SG(pPacket, pSG);
+ }
+ }
+ }
+ else
+ {
+ /* we have the INTNETSG enqueued. (see the above comment explaining why/when this may happen)
+ * just use the INTNETSG to pass it to intnet */
+#ifndef VBOXNETADP
+ /* the PINTNETSG is stored only when the underlying miniport
+ * indicates NDIS_STATUS_RESOURCES, we should never have this when processing
+ * the "from-host" packedts */
+ Assert(!bSrcHost);
+#endif
+ pSG = (PINTNETSG)pvPacket;
+
+ LogFlow(("not ndis packet info, pSG (%p)\n", pSG));
+ }
+
+#ifdef DEBUG_NETFLT_PACKETS
+ if (!pPacket && !pTmpPacket)
+ {
+ /* create tmp packet that woud be used for matching */
+ pTmpPacket = vboxNetFltWinNdisPacketFromSG(pNetFltIf,
+ pSG, /* PINTNETSG */
+ pSG, /* PVOID pBufToFree */
+ bSrcHost, /* bool bToWire */
+ true); /* bool bCopyMemory */
+
+ NDIS_SET_PACKET_STATUS(pTmpPacket, NDIS_STATUS_SUCCESS);
+
+ DBG_CHECK_PACKET_AND_SG(pTmpPacket, pSG);
+
+ Assert(pTmpPacket);
+ }
+#endif
+ do
+ {
+#ifndef VBOXNETADP
+ /* the pSG was successfully initialized, post it to the netFlt*/
+ bDropIt = pSG ? pNetFltIf->pSwitchPort->pfnRecv(pNetFltIf->pSwitchPort, NULL /* pvIf */, pSG,
+ bSrcHost ? INTNETTRUNKDIR_HOST : INTNETTRUNKDIR_WIRE
+ )
+ : false;
+#else
+ if (pSG)
+ {
+ pNetFltIf->pSwitchPort->pfnRecv(pNetFltIf->pSwitchPort, NULL /* pvIf */, pSG, INTNETTRUNKDIR_HOST);
+ STATISTIC_INCREASE(pNetFltIf->u.s.WinIf.cTxSuccess);
+ }
+ else
+ {
+ STATISTIC_INCREASE(pNetFltIf->u.s.WinIf.cTxError);
+ }
+#endif
+
+#ifndef VBOXNETFLT_NO_PACKET_QUEUE
+
+# if !defined(VBOXNETADP)
+ if (!bDropIt)
+ {
+ Status = vboxNetFltWinQuPostPacket(pNetFltIf, pPacket, pSG, fFlags
+# ifdef DEBUG_NETFLT_PACKETS
+ , pTmpPacket
+# endif
+ );
+
+ if (Status == NDIS_STATUS_PENDING)
+ {
+ /* we will process packet completion in the completion routine */
+ bPending = true;
+ break;
+ }
+ }
+ else
+# endif
+ {
+ Status = NDIS_STATUS_SUCCESS;
+ }
+
+ /* drop it */
+ if (pPacket)
+ {
+ if (!(fFlags & PACKET_MINE))
+ {
+# if !defined(VBOXNETADP)
+ /* complete the packets */
+ if (fFlags & PACKET_SRC_HOST)
+ {
+# endif
+/* NDIS_SET_PACKET_STATUS(pPacket, Status); */
+ NdisMSendComplete(pNetFltIf->u.s.hMiniport, pPacket, Status);
+# if !defined(VBOXNETADP)
+ }
+ else
+ {
+# endif
+# ifndef VBOXNETADP
+ NdisReturnPackets(&pPacket, 1);
+# endif
+# if !defined(VBOXNETADP)
+ }
+# endif
+ }
+ else
+ {
+ Assert(!(fFlags & PACKET_SRC_HOST));
+ vboxNetFltWinFreeSGNdisPacket(pPacket, true);
+ }
+ }
+ else
+ {
+ Assert(pSG);
+ vboxNetFltWinMemFree(pSG);
+ }
+# ifndef VBOXNETADP
+ bPending = false;
+# endif
+ } while (0);
+
+#ifdef DEBUG_NETFLT_PACKETS
+ if (pTmpPacket)
+ {
+ vboxNetFltWinFreeSGNdisPacket(pTmpPacket, true);
+ }
+#endif
+
+#ifndef VBOXNETADP
+ return bPending;
+#else
+ return false;
+#endif
+#else /* #ifdef VBOXNETFLT_NO_PACKET_QUEUE */
+ } while (0);
+
+ if (bDeleteSG)
+ vboxNetFltWinMemFree(pSG);
+
+# ifndef VBOXNETADP
+ return bDropIt;
+# else
+ return true;
+# endif
+#endif
+}
+#ifndef VBOXNETFLT_NO_PACKET_QUEUE
+/*
+ * thread start function for the thread which processes the packets enqueued in our send and receive callbacks called by ndis
+ *
+ * ndis calls us at DISPATCH_LEVEL, while IntNet is using kernel functions which require Irql<DISPATCH_LEVEL
+ * this is why we can not immediately post packets to IntNet from our sen/receive callbacks
+ * instead we put the incoming packets to the queue and maintain the system thread running at passive level
+ * which processes the queue and posts the packets to IntNet, and further to the host or to the wire.
+ */
+static VOID vboxNetFltWinQuPacketQueueWorkerThreadProc(PVBOXNETFLTINS pNetFltIf)
+{
+ bool fResume = true;
+ NTSTATUS fStatus;
+ PPACKET_QUEUE_WORKER pWorker = &pNetFltIf->u.s.PacketQueueWorker;
+
+ PVOID apEvents[] = {
+ (PVOID)&pWorker->KillEvent,
+ (PVOID)&pWorker->NotifyEvent
+ };
+
+ while (fResume)
+ {
+ uint32_t cNumProcessed;
+ uint32_t cNumPostedToHostWire;
+
+ fStatus = KeWaitForMultipleObjects(RT_ELEMENTS(apEvents), apEvents, WaitAny, Executive, KernelMode, FALSE, NULL, NULL);
+ if (!NT_SUCCESS(fStatus) || fStatus == STATUS_WAIT_0)
+ {
+ /* "kill" event was set
+ * will process queued packets and exit */
+ fResume = false;
+ }
+
+ LogFlow(("processing vboxNetFltWinQuPacketQueueWorkerThreadProc\n"));
+
+ cNumProcessed = 0;
+ cNumPostedToHostWire = 0;
+
+ do
+ {
+ PVBOXNETFLTPACKET_INFO pInfo;
+
+#ifdef DEBUG_NETFLT_PACKETS
+ /* packet used for matching */
+ PNDIS_PACKET pTmpPacket = NULL;
+#endif
+
+ /** @todo FIXME: !!! the better approach for performance would be to dequeue all packets at once
+ * and then go through all dequeued packets
+ * the same should be done for enqueue !!! */
+ pInfo = vboxNetFltWinQuInterlockedDequeueHead(&pWorker->PacketQueue);
+
+ if (!pInfo)
+ {
+ break;
+ }
+
+ LogFlow(("found info (0x%p)\n", pInfo));
+
+ if (vboxNetFltWinQuProcessInfo(pNetFltIf, pWorker, pInfo->pPacket, pInfo->fFlags))
+ {
+ cNumPostedToHostWire++;
+ }
+
+ vboxNetFltWinPpFreePacketInfo(pInfo);
+
+ cNumProcessed++;
+ } while (TRUE);
+
+ if (cNumProcessed)
+ {
+ vboxNetFltWinDecReferenceNetFlt(pNetFltIf, cNumProcessed);
+
+ Assert(cNumProcessed >= cNumPostedToHostWire);
+
+ if (cNumProcessed != cNumPostedToHostWire)
+ {
+ vboxNetFltWinDecReferenceWinIf(pNetFltIf, cNumProcessed - cNumPostedToHostWire);
+ }
+ }
+ }
+
+ PsTerminateSystemThread(STATUS_SUCCESS);
+}
+#endif
+/**
+ * thread start function for the job processing thread
+ *
+ * see comments for PVBOXNETFLT_JOB_QUEUE
+ */
+static VOID vboxNetFltWinJobWorkerThreadProc(PVBOXNETFLT_JOB_QUEUE pQueue)
+{
+ bool fResume = true;
+ NTSTATUS Status;
+
+ PVOID apEvents[] = {
+ (PVOID)&pQueue->KillEvent,
+ (PVOID)&pQueue->NotifyEvent,
+ };
+
+ do
+ {
+ Status = KeWaitForMultipleObjects(RT_ELEMENTS(apEvents), apEvents, WaitAny, Executive, KernelMode, FALSE, NULL, NULL);
+ Assert(NT_SUCCESS(Status));
+ if (!NT_SUCCESS(Status) || Status == STATUS_WAIT_0)
+ {
+ /* will process queued jobs and exit */
+ Assert(Status == STATUS_WAIT_0);
+ fResume = false;
+ }
+
+ do
+ {
+ PLIST_ENTRY pJobEntry = ExInterlockedRemoveHeadList(&pQueue->Jobs, &pQueue->Lock);
+ PVBOXNETFLT_JOB pJob;
+
+ if (!pJobEntry)
+ break;
+
+ pJob = LIST_ENTRY_2_JOB(pJobEntry);
+
+ Assert(KeGetCurrentIrql() == PASSIVE_LEVEL);
+ pJob->pfnRoutine(pJob->pContext);
+ Assert(KeGetCurrentIrql() == PASSIVE_LEVEL);
+
+ if (pJob->bUseCompletionEvent)
+ {
+ KeSetEvent(&pJob->CompletionEvent, 1, FALSE);
+ }
+ } while (TRUE);
+ } while (fResume);
+
+ Assert(Status == STATUS_WAIT_0);
+
+ PsTerminateSystemThread(STATUS_SUCCESS);
+}
+
+/**
+ * enqueues the job to the job queue to be processed by the job worker thread
+ * see comments for PVBOXNETFLT_JOB_QUEUE
+ */
+static VOID vboxNetFltWinJobEnqueueJob(PVBOXNETFLT_JOB_QUEUE pQueue, PVBOXNETFLT_JOB pJob, bool bEnqueueHead)
+{
+ if (bEnqueueHead)
+ {
+ ExInterlockedInsertHeadList(&pQueue->Jobs, &pJob->ListEntry, &pQueue->Lock);
+ }
+ else
+ {
+ ExInterlockedInsertTailList(&pQueue->Jobs, &pJob->ListEntry, &pQueue->Lock);
+ }
+
+ KeSetEvent(&pQueue->NotifyEvent, 1, FALSE);
+}
+
+DECLINLINE(VOID) vboxNetFltWinJobInit(PVBOXNETFLT_JOB pJob, PFNVBOXNETFLT_JOB_ROUTINE pfnRoutine, PVOID pContext, bool bUseEvent)
+{
+ pJob->pfnRoutine = pfnRoutine;
+ pJob->pContext = pContext;
+ pJob->bUseCompletionEvent = bUseEvent;
+ if (bUseEvent)
+ KeInitializeEvent(&pJob->CompletionEvent, NotificationEvent, FALSE);
+}
+
+/**
+ * enqueues the job to the job queue to be processed by the job worker thread and
+ * blocks until the job is done
+ * see comments for PVBOXNETFLT_JOB_QUEUE
+ */
+static VOID vboxNetFltWinJobSynchExec(PVBOXNETFLT_JOB_QUEUE pQueue, PFNVBOXNETFLT_JOB_ROUTINE pfnRoutine, PVOID pContext)
+{
+ VBOXNETFLT_JOB Job;
+
+ Assert(KeGetCurrentIrql() < DISPATCH_LEVEL);
+
+ vboxNetFltWinJobInit(&Job, pfnRoutine, pContext, true);
+
+ vboxNetFltWinJobEnqueueJob(pQueue, &Job, false);
+
+ KeWaitForSingleObject(&Job.CompletionEvent, Executive, KernelMode, FALSE, NULL);
+}
+
+/**
+ * enqueues the job to be processed by the job worker thread at passive level and
+ * blocks until the job is done
+ */
+DECLHIDDEN(VOID) vboxNetFltWinJobSynchExecAtPassive(PFNVBOXNETFLT_JOB_ROUTINE pfnRoutine, PVOID pContext)
+{
+ vboxNetFltWinJobSynchExec(&g_VBoxJobQueue, pfnRoutine, pContext);
+}
+
+/**
+ * helper function used for system thread creation
+ */
+static NTSTATUS vboxNetFltWinQuCreateSystemThread(PKTHREAD *ppThread, PKSTART_ROUTINE pfnStartRoutine, PVOID pvStartContext)
+{
+ OBJECT_ATTRIBUTES ObjectAttributes;
+ Assert(KeGetCurrentIrql() == PASSIVE_LEVEL);
+
+ InitializeObjectAttributes(&ObjectAttributes, NULL, OBJ_KERNEL_HANDLE, NULL, NULL);
+
+ HANDLE hThread;
+ NTSTATUS Status = PsCreateSystemThread(&hThread, THREAD_ALL_ACCESS, &ObjectAttributes, NULL, NULL, (PKSTART_ROUTINE)pfnStartRoutine, pvStartContext);
+ Assert(Status == STATUS_SUCCESS);
+ if (Status == STATUS_SUCCESS)
+ {
+ Status = ObReferenceObjectByHandle(hThread, THREAD_ALL_ACCESS, NULL, KernelMode, (PVOID*)ppThread, NULL);
+ Assert(Status == STATUS_SUCCESS);
+ ZwClose(hThread);
+ if (Status == STATUS_SUCCESS)
+ {
+ return STATUS_SUCCESS;
+ }
+
+ /** @todo how would we fail in this case ?*/
+ }
+ return Status;
+}
+
+/**
+ * initialize the job queue
+ * see comments for PVBOXNETFLT_JOB_QUEUE
+ */
+static NTSTATUS vboxNetFltWinJobInitQueue(PVBOXNETFLT_JOB_QUEUE pQueue)
+{
+ NTSTATUS fStatus;
+
+ Assert(KeGetCurrentIrql() == PASSIVE_LEVEL);
+
+ NdisZeroMemory(pQueue, sizeof(VBOXNETFLT_JOB_QUEUE));
+
+ KeInitializeEvent(&pQueue->KillEvent, NotificationEvent, FALSE);
+
+ KeInitializeEvent(&pQueue->NotifyEvent, SynchronizationEvent, FALSE);
+
+ InitializeListHead(&pQueue->Jobs);
+
+ fStatus = vboxNetFltWinQuCreateSystemThread(&pQueue->pThread, (PKSTART_ROUTINE)vboxNetFltWinJobWorkerThreadProc, pQueue);
+ if (fStatus != STATUS_SUCCESS)
+ {
+ pQueue->pThread = NULL;
+ }
+ else
+ {
+ Assert(pQueue->pThread);
+ }
+
+ return fStatus;
+}
+
+/**
+ * deinitialize the job queue
+ * see comments for PVBOXNETFLT_JOB_QUEUE
+ */
+static void vboxNetFltWinJobFiniQueue(PVBOXNETFLT_JOB_QUEUE pQueue)
+{
+ Assert(KeGetCurrentIrql() == PASSIVE_LEVEL);
+
+ if (pQueue->pThread)
+ {
+ KeSetEvent(&pQueue->KillEvent, 0, FALSE);
+
+ KeWaitForSingleObject(pQueue->pThread, Executive,
+ KernelMode, FALSE, NULL);
+ }
+}
+
+#ifndef VBOXNETFLT_NO_PACKET_QUEUE
+
+/**
+ * initializes the packet queue
+ * */
+DECLHIDDEN(NTSTATUS) vboxNetFltWinQuInitPacketQueue(PVBOXNETFLTINS pInstance)
+{
+ NTSTATUS Status;
+ PPACKET_QUEUE_WORKER pWorker = &pInstance->u.s.PacketQueueWorker;
+
+ AssertFatal(!pWorker->pSG);
+
+ Assert(KeGetCurrentIrql() == PASSIVE_LEVEL);
+
+ KeInitializeEvent(&pWorker->KillEvent, NotificationEvent, FALSE);
+
+ KeInitializeEvent(&pWorker->NotifyEvent, SynchronizationEvent, FALSE);
+
+ INIT_INTERLOCKED_PACKET_QUEUE(&pWorker->PacketQueue);
+
+ do
+ {
+ Status = vboxNetFltWinPpAllocatePacketInfoPool(&pWorker->PacketInfoPool, VBOXNETFLT_PACKET_INFO_POOL_SIZE);
+
+ if (Status == NDIS_STATUS_SUCCESS)
+ {
+ pWorker->pSG = vboxNetFltWinCreateSG(PACKET_QUEUE_SG_SEGS_ALLOC);
+ if (!pWorker->pSG)
+ {
+ Status = STATUS_INSUFFICIENT_RESOURCES;
+ break;
+ }
+
+ Status = vboxNetFltWinQuCreateSystemThread(&pWorker->pThread, (PKSTART_ROUTINE)vboxNetFltWinQuPacketQueueWorkerThreadProc, pInstance);
+ if (Status != STATUS_SUCCESS)
+ {
+ vboxNetFltWinPpFreePacketInfoPool(&pWorker->PacketInfoPool);
+ vboxNetFltWinMemFree(pWorker->pSG);
+ pWorker->pSG = NULL;
+ break;
+ }
+ }
+
+ } while (0);
+
+ return Status;
+}
+
+/*
+ * deletes the packet queue
+ */
+DECLHIDDEN(void) vboxNetFltWinQuFiniPacketQueue(PVBOXNETFLTINS pInstance)
+{
+ PINTNETSG pSG;
+ PPACKET_QUEUE_WORKER pWorker = &pInstance->u.s.PacketQueueWorker;
+ Assert(KeGetCurrentIrql() < DISPATCH_LEVEL);
+
+ /* using the pPacketQueueSG as an indicator that the packet queue is initialized */
+ RTSpinlockAcquire((pInstance)->hSpinlock);
+ if (pWorker->pSG)
+ {
+ pSG = pWorker->pSG;
+ pWorker->pSG = NULL;
+ RTSpinlockRelease((pInstance)->hSpinlock);
+ KeSetEvent(&pWorker->KillEvent, 0, FALSE);
+
+ KeWaitForSingleObject(pWorker->pThread, Executive,
+ KernelMode, FALSE, NULL);
+
+ vboxNetFltWinPpFreePacketInfoPool(&pWorker->PacketInfoPool);
+
+ vboxNetFltWinDeleteSG(pSG);
+
+ FINI_INTERLOCKED_PACKET_QUEUE(&pWorker->PacketQueue);
+ }
+ else
+ {
+ RTSpinlockRelease((pInstance)->hSpinlock);
+ }
+}
+
+#endif
+
+/*
+ * creates the INTNETSG containing one segment pointing to the buffer of size cbBufSize
+ * the INTNETSG created should be cleaned with vboxNetFltWinMemFree
+ */
+DECLHIDDEN(NDIS_STATUS) vboxNetFltWinAllocSG(UINT cbPacket, PINTNETSG *ppSG)
+{
+ NDIS_STATUS Status;
+ PINTNETSG pSG;
+
+ /* allocation:
+ * 1. SG_PACKET - with one aSegs pointing to
+ * 2. buffer of cbPacket containing the entire packet */
+ AssertCompileSizeAlignment(INTNETSG, sizeof(PVOID));
+ Status = vboxNetFltWinMemAlloc((PVOID*)&pSG, cbPacket + sizeof(INTNETSG));
+ if (Status == NDIS_STATUS_SUCCESS)
+ {
+ IntNetSgInitTemp(pSG, pSG + 1, cbPacket);
+ LogFlow(("pSG created (%p)\n", pSG));
+ *ppSG = pSG;
+ }
+ return Status;
+}
+
+#ifndef VBOXNETFLT_NO_PACKET_QUEUE
+/**
+ * put the packet info to the queue
+ */
+DECLINLINE(void) vboxNetFltWinQuEnqueueInfo(PVBOXNETFLTPACKET_QUEUE_WORKER pWorker, PVBOXNETFLTPACKET_INFO pInfo)
+{
+ vboxNetFltWinQuInterlockedEnqueueTail(&pWorker->PacketQueue, pInfo);
+
+ KeSetEvent(&pWorker->NotifyEvent, IO_NETWORK_INCREMENT, FALSE);
+}
+
+/**
+ * puts the packet to the queue
+ *
+ * @return NDIST_STATUS_SUCCESS iff the packet was enqueued successfully
+ * and error status otherwise.
+ * NOTE: that the success status does NOT mean that the packet processing is completed, but only that it was enqueued successfully
+ * the packet can be returned to the caller protocol/moniport only in case the bReleasePacket was set to true (in this case the copy of the packet was enqueued)
+ * or if vboxNetFltWinQuEnqueuePacket failed, i.e. the packet was NOT enqueued
+ */
+DECLHIDDEN(NDIS_STATUS) vboxNetFltWinQuEnqueuePacket(PVBOXNETFLTINS pInstance, PVOID pPacket, const UINT fPacketFlags)
+{
+ PVBOXNETFLT_PACKET_INFO pInfo;
+ PVBOXNETFLT_PACKET_QUEUE_WORKER pWorker = &pInstance->u.s.PacketQueueWorker;
+ NDIS_STATUS fStatus = NDIS_STATUS_SUCCESS;
+
+ do
+ {
+ if (fPacketFlags & PACKET_COPY)
+ {
+ PNDIS_BUFFER pBuffer = NULL;
+ UINT cBufferCount;
+ UINT uBytesCopied = 0;
+ UINT cbPacketLength;
+ PINTNETSG pSG;
+
+ /* the packet is Ndis packet */
+ Assert(!(fPacketFlags & PACKET_SG));
+ Assert(!(fPacketFlags & PACKET_MINE));
+
+ NdisQueryPacket((PNDIS_PACKET)pPacket,
+ NULL,
+ &cBufferCount,
+ &pBuffer,
+ &cbPacketLength);
+
+
+ Assert(cBufferCount);
+
+ fStatus = vboxNetFltWinAllocSG(cbPacketLength, &pSG);
+ if (fStatus != NDIS_STATUS_SUCCESS)
+ {
+ AssertFailed();
+ break;
+ }
+
+ pInfo = vboxNetFltWinPpAllocPacketInfo(&pWorker->PacketInfoPool);
+
+ if (!pInfo)
+ {
+ AssertFailed();
+ /** @todo what status to set? */
+ fStatus = NDIS_STATUS_FAILURE;
+ vboxNetFltWinMemFree(pSG);
+ break;
+ }
+
+ Assert(pInfo->pPool);
+
+ /* the packet we are queueing is SG, add PACKET_SG to flags */
+ SET_FLAGS_TO_INFO(pInfo, fPacketFlags | PACKET_SG);
+ SET_PACKET_TO_INFO(pInfo, pSG);
+
+ fStatus = vboxNetFltWinNdisBufferMoveToSG0(pBuffer, pSG);
+ if (fStatus != NDIS_STATUS_SUCCESS)
+ {
+ AssertFailed();
+ vboxNetFltWinPpFreePacketInfo(pInfo);
+ vboxNetFltWinMemFree(pSG);
+ break;
+ }
+
+ DBG_CHECK_PACKET_AND_SG((PNDIS_PACKET)pPacket, pSG);
+ }
+ else
+ {
+ pInfo = vboxNetFltWinPpAllocPacketInfo(&pWorker->PacketInfoPool);
+
+ if (!pInfo)
+ {
+ AssertFailed();
+ /** @todo what status to set? */
+ fStatus = NDIS_STATUS_FAILURE;
+ break;
+ }
+
+ Assert(pInfo->pPool);
+
+ SET_FLAGS_TO_INFO(pInfo, fPacketFlags);
+ SET_PACKET_TO_INFO(pInfo, pPacket);
+ }
+
+ vboxNetFltWinQuEnqueueInfo(pWorker, pInfo);
+
+ } while (0);
+
+ return fStatus;
+}
+#endif
+
+
+/*
+ * netflt
+ */
+#ifndef VBOXNETADP
+static NDIS_STATUS vboxNetFltWinSynchNdisRequest(PVBOXNETFLTINS pNetFlt, PNDIS_REQUEST pRequest)
+{
+ int rc;
+
+ Assert(KeGetCurrentIrql() < DISPATCH_LEVEL);
+
+ /* 1. serialize */
+ rc = RTSemFastMutexRequest(pNetFlt->u.s.WinIf.hSynchRequestMutex); AssertRC(rc);
+ if (RT_SUCCESS(rc))
+ {
+ NDIS_STATUS fRequestStatus = NDIS_STATUS_SUCCESS;
+
+ /* 2. set pNetFlt->u.s.pSynchRequest */
+ Assert(!pNetFlt->u.s.WinIf.pSynchRequest);
+ pNetFlt->u.s.WinIf.pSynchRequest = pRequest;
+
+ /* 3. call NdisRequest */
+ NdisRequest(&fRequestStatus, pNetFlt->u.s.WinIf.hBinding, pRequest);
+
+ if (fRequestStatus == NDIS_STATUS_PENDING)
+ {
+ /* 3.1 if pending wait and assign the resulting status */
+ KeWaitForSingleObject(&pNetFlt->u.s.WinIf.hSynchCompletionEvent, Executive,
+ KernelMode, FALSE, NULL);
+
+ fRequestStatus = pNetFlt->u.s.WinIf.SynchCompletionStatus;
+ }
+
+ /* 4. clear the pNetFlt->u.s.pSynchRequest */
+ pNetFlt->u.s.WinIf.pSynchRequest = NULL;
+
+ RTSemFastMutexRelease(pNetFlt->u.s.WinIf.hSynchRequestMutex); AssertRC(rc);
+ return fRequestStatus;
+ }
+ return NDIS_STATUS_FAILURE;
+}
+
+
+DECLHIDDEN(NDIS_STATUS) vboxNetFltWinGetMacAddress(PVBOXNETFLTINS pNetFlt, PRTMAC pMac)
+{
+ NDIS_REQUEST request;
+ NDIS_STATUS status;
+ request.RequestType = NdisRequestQueryInformation;
+ request.DATA.QUERY_INFORMATION.InformationBuffer = pMac;
+ request.DATA.QUERY_INFORMATION.InformationBufferLength = sizeof(RTMAC);
+ request.DATA.QUERY_INFORMATION.Oid = OID_802_3_CURRENT_ADDRESS;
+ status = vboxNetFltWinSynchNdisRequest(pNetFlt, &request);
+ if (status != NDIS_STATUS_SUCCESS)
+ {
+ /** @todo */
+ AssertFailed();
+ }
+
+ return status;
+
+}
+
+DECLHIDDEN(NDIS_STATUS) vboxNetFltWinQueryPhysicalMedium(PVBOXNETFLTINS pNetFlt, NDIS_PHYSICAL_MEDIUM * pMedium)
+{
+ NDIS_REQUEST Request;
+ NDIS_STATUS Status;
+ Request.RequestType = NdisRequestQueryInformation;
+ Request.DATA.QUERY_INFORMATION.InformationBuffer = pMedium;
+ Request.DATA.QUERY_INFORMATION.InformationBufferLength = sizeof(NDIS_PHYSICAL_MEDIUM);
+ Request.DATA.QUERY_INFORMATION.Oid = OID_GEN_PHYSICAL_MEDIUM;
+ Status = vboxNetFltWinSynchNdisRequest(pNetFlt, &Request);
+ if (Status != NDIS_STATUS_SUCCESS)
+ {
+ if (Status == NDIS_STATUS_NOT_SUPPORTED || Status == NDIS_STATUS_NOT_RECOGNIZED || Status == NDIS_STATUS_INVALID_OID)
+ {
+ Status = NDIS_STATUS_NOT_SUPPORTED;
+ }
+ else
+ {
+ LogRel(("OID_GEN_PHYSICAL_MEDIUM failed: Status (0x%x)", Status));
+ AssertFailed();
+ }
+ }
+ return Status;
+}
+
+DECLHIDDEN(bool) vboxNetFltWinIsPromiscuous(PVBOXNETFLTINS pNetFlt)
+{
+ /** @todo r=bird: This is too slow and is probably returning the wrong
+ * information. What we're interested in is whether someone besides us
+ * has put the interface into promiscuous mode. */
+ NDIS_REQUEST request;
+ NDIS_STATUS status;
+ ULONG filter;
+ Assert(VBOXNETFLT_PROMISCUOUS_SUPPORTED(pNetFlt));
+ request.RequestType = NdisRequestQueryInformation;
+ request.DATA.QUERY_INFORMATION.InformationBuffer = &filter;
+ request.DATA.QUERY_INFORMATION.InformationBufferLength = sizeof(filter);
+ request.DATA.QUERY_INFORMATION.Oid = OID_GEN_CURRENT_PACKET_FILTER;
+ status = vboxNetFltWinSynchNdisRequest(pNetFlt, &request);
+ if (status != NDIS_STATUS_SUCCESS)
+ {
+ /** @todo */
+ AssertFailed();
+ return false;
+ }
+ return (filter & NDIS_PACKET_TYPE_PROMISCUOUS) != 0;
+}
+
+DECLHIDDEN(NDIS_STATUS) vboxNetFltWinSetPromiscuous(PVBOXNETFLTINS pNetFlt, bool bYes)
+{
+/** @todo Need to report changes to the switch via:
+ * pThis->pSwitchPort->pfnReportPromiscuousMode(pThis->pSwitchPort, fPromisc);
+ */
+ Assert(VBOXNETFLT_PROMISCUOUS_SUPPORTED(pNetFlt));
+ if (VBOXNETFLT_PROMISCUOUS_SUPPORTED(pNetFlt))
+ {
+ NDIS_REQUEST Request;
+ NDIS_STATUS fStatus;
+ ULONG fFilter;
+ ULONG fExpectedFilter;
+ ULONG fOurFilter;
+ Request.RequestType = NdisRequestQueryInformation;
+ Request.DATA.QUERY_INFORMATION.InformationBuffer = &fFilter;
+ Request.DATA.QUERY_INFORMATION.InformationBufferLength = sizeof(fFilter);
+ Request.DATA.QUERY_INFORMATION.Oid = OID_GEN_CURRENT_PACKET_FILTER;
+ fStatus = vboxNetFltWinSynchNdisRequest(pNetFlt, &Request);
+ if (fStatus != NDIS_STATUS_SUCCESS)
+ {
+ /** @todo */
+ AssertFailed();
+ return fStatus;
+ }
+
+ if (!pNetFlt->u.s.WinIf.StateFlags.fUpperProtSetFilterInitialized)
+ {
+ /* the cache was not initialized yet, initiate it with the current filter value */
+ pNetFlt->u.s.WinIf.fUpperProtocolSetFilter = fFilter;
+ pNetFlt->u.s.WinIf.StateFlags.fUpperProtSetFilterInitialized = TRUE;
+ }
+
+
+ if (bYes)
+ {
+ fExpectedFilter = NDIS_PACKET_TYPE_PROMISCUOUS;
+ fOurFilter = NDIS_PACKET_TYPE_PROMISCUOUS;
+ }
+ else
+ {
+ fExpectedFilter = pNetFlt->u.s.WinIf.fUpperProtocolSetFilter;
+ fOurFilter = 0;
+ }
+
+ if (fExpectedFilter != fFilter)
+ {
+ Request.RequestType = NdisRequestSetInformation;
+ Request.DATA.SET_INFORMATION.InformationBuffer = &fExpectedFilter;
+ Request.DATA.SET_INFORMATION.InformationBufferLength = sizeof(fExpectedFilter);
+ Request.DATA.SET_INFORMATION.Oid = OID_GEN_CURRENT_PACKET_FILTER;
+ fStatus = vboxNetFltWinSynchNdisRequest(pNetFlt, &Request);
+ if (fStatus != NDIS_STATUS_SUCCESS)
+ {
+ /** @todo */
+ AssertFailed();
+ return fStatus;
+ }
+ }
+ pNetFlt->u.s.WinIf.fOurSetFilter = fOurFilter;
+ return fStatus;
+ }
+ return NDIS_STATUS_NOT_SUPPORTED;
+}
+
+#else /* VBOXNETADP */
+
+/**
+ * Generates a new unique MAC address based on our vendor ID
+ */
+DECLHIDDEN(void) vboxNetFltWinGenerateMACAddress(RTMAC *pMac)
+{
+ /* temporary use a time info */
+ uint64_t NanoTS = RTTimeSystemNanoTS();
+ pMac->au8[0] = (uint8_t)((VBOXNETADP_VENDOR_ID >> 16) & 0xff);
+ pMac->au8[1] = (uint8_t)((VBOXNETADP_VENDOR_ID >> 8) & 0xff);
+ pMac->au8[2] = (uint8_t)(VBOXNETADP_VENDOR_ID & 0xff);
+ pMac->au8[3] = (uint8_t)(NanoTS & 0xff0000);
+ pMac->au16[2] = (uint16_t)(NanoTS & 0xffff);
+}
+
+DECLHIDDEN(int) vboxNetFltWinMAC2NdisString(RTMAC *pMac, PNDIS_STRING pNdisString)
+{
+ static const char s_achDigits[17] = "0123456789abcdef";
+ PWSTR pString;
+
+ /* validate parameters */
+ AssertPtrReturn(pMac, VERR_INVALID_PARAMETER);
+ AssertPtrReturn(pNdisString, VERR_INVALID_PARAMETER);
+ AssertReturn(pNdisString->MaximumLength >= 13*sizeof(pNdisString->Buffer[0]), VERR_INVALID_PARAMETER);
+
+ pString = pNdisString->Buffer;
+
+ for (int i = 0; i < 6; i++)
+ {
+ uint8_t u8 = pMac->au8[i];
+ pString[0] = s_achDigits[(u8 >> 4) & 0xf];
+ pString[1] = s_achDigits[(u8/*>>0*/)& 0xf];
+ pString += 2;
+ }
+
+ pNdisString->Length = 12*sizeof(pNdisString->Buffer[0]);
+
+ *pString = L'\0';
+
+ return VINF_SUCCESS;
+}
+
+static int vboxNetFltWinWchar2Byte(WCHAR c, uint8_t *pb)
+{
+ if (c >= L'A' && c <= L'F')
+ *pb = (c - L'A') + 10;
+ else if (c >= L'a' && c <= L'f')
+ *pb = (c - L'a') + 10;
+ else if (c >= L'0' && c <= L'9')
+ *pb = (c - L'0');
+ else
+ return VERR_INVALID_PARAMETER;
+ return VINF_SUCCESS;
+}
+
+DECLHIDDEN(int) vboxNetFltWinMACFromNdisString(RTMAC *pMac, PNDIS_STRING pNdisString)
+{
+
+ /* validate parameters */
+ AssertPtrReturn(pMac, VERR_INVALID_PARAMETER);
+ AssertPtrReturn(pNdisString, VERR_INVALID_PARAMETER);
+ AssertReturn(pNdisString->Length >= 12*sizeof(pNdisString->Buffer[0]), VERR_INVALID_PARAMETER);
+
+ int rc = VINF_SUCCESS;
+ PWSTR pString = pNdisString->Buffer;
+ for (int i = 0; i < 6; i++)
+ {
+ uint8_t v1, v2;
+ rc = vboxNetFltWinWchar2Byte(pString[0], &v1);
+ if (RT_FAILURE(rc))
+ break;
+
+ rc = vboxNetFltWinWchar2Byte(pString[1], &v2);
+ if (RT_FAILURE(rc))
+ break;
+
+ pMac->au8[i] = (v1 << 4) | v2;
+
+ pString += 2;
+ }
+
+ return rc;
+}
+
+#endif /* VBOXNETADP */
+
+/**
+ * creates a NDIS_PACKET from the PINTNETSG
+ */
+DECLHIDDEN(PNDIS_PACKET) vboxNetFltWinNdisPacketFromSG(PVBOXNETFLTINS pNetFlt, PINTNETSG pSG, PVOID pBufToFree, bool bToWire, bool bCopyMemory)
+{
+ NDIS_STATUS fStatus;
+ PNDIS_PACKET pPacket;
+
+ Assert(pSG->aSegs[0].pv);
+ Assert(pSG->cbTotal >= VBOXNETFLT_PACKET_ETHEADER_SIZE);
+
+/** @todo Hrmpf, how can we fix this assumption? I fear this'll cause data
+ * corruption and maybe even BSODs ... */
+ AssertReturn(pSG->cSegsUsed == 1 || bCopyMemory, NULL);
+
+#ifdef VBOXNETADP
+ NdisAllocatePacket(&fStatus, &pPacket, pNetFlt->u.s.WinIf.hRecvPacketPool);
+#else
+ NdisAllocatePacket(&fStatus, &pPacket, bToWire ? pNetFlt->u.s.WinIf.hSendPacketPool : pNetFlt->u.s.WinIf.hRecvPacketPool);
+#endif
+ if (fStatus == NDIS_STATUS_SUCCESS)
+ {
+ PNDIS_BUFFER pBuffer;
+ PVOID pvMemBuf;
+
+ /** @todo generally we do not always need to zero-initialize the complete OOB data here, reinitialize only when/what we need,
+ * however we DO need to reset the status for the packets we indicate via NdisMIndicateReceivePacket to avoid packet loss
+ * in case the status contains NDIS_STATUS_RESOURCES */
+ VBOXNETFLT_OOB_INIT(pPacket);
+
+ if (bCopyMemory)
+ {
+ fStatus = vboxNetFltWinMemAlloc(&pvMemBuf, pSG->cbTotal);
+ Assert(fStatus == NDIS_STATUS_SUCCESS);
+ if (fStatus == NDIS_STATUS_SUCCESS)
+ IntNetSgRead(pSG, pvMemBuf);
+ }
+ else
+ {
+ pvMemBuf = pSG->aSegs[0].pv;
+ }
+ if (fStatus == NDIS_STATUS_SUCCESS)
+ {
+#ifdef VBOXNETADP
+ NdisAllocateBuffer(&fStatus, &pBuffer,
+ pNetFlt->u.s.WinIf.hRecvBufferPool,
+ pvMemBuf,
+ pSG->cbTotal);
+#else
+ NdisAllocateBuffer(&fStatus, &pBuffer,
+ bToWire ? pNetFlt->u.s.WinIf.hSendBufferPool : pNetFlt->u.s.WinIf.hRecvBufferPool,
+ pvMemBuf,
+ pSG->cbTotal);
+#endif
+
+ if (fStatus == NDIS_STATUS_SUCCESS)
+ {
+ NdisChainBufferAtBack(pPacket, pBuffer);
+
+ if (bToWire)
+ {
+ PVBOXNETFLT_PKTRSVD_PT pSendInfo = (PVBOXNETFLT_PKTRSVD_PT)pPacket->ProtocolReserved;
+ pSendInfo->pOrigPacket = NULL;
+ pSendInfo->pBufToFree = pBufToFree;
+#ifdef VBOX_LOOPBACK_USEFLAGS
+ /* set "don't loopback" flags */
+ NdisGetPacketFlags(pPacket) = g_VBoxNetFltGlobalsWin.fPacketDontLoopBack;
+#else
+ NdisGetPacketFlags(pPacket) = 0;
+#endif
+ }
+ else
+ {
+ PVBOXNETFLT_PKTRSVD_MP pRecvInfo = (PVBOXNETFLT_PKTRSVD_MP)pPacket->MiniportReserved;
+ pRecvInfo->pOrigPacket = NULL;
+ pRecvInfo->pBufToFree = pBufToFree;
+
+ /* we must set the header size on receive */
+ NDIS_SET_PACKET_HEADER_SIZE(pPacket, VBOXNETFLT_PACKET_ETHEADER_SIZE);
+ /* NdisAllocatePacket zero-initializes the OOB data,
+ * but keeps the packet flags, clean them here */
+ NdisGetPacketFlags(pPacket) = 0;
+ }
+ /** @todo set out of bound data */
+ }
+ else
+ {
+ AssertFailed();
+ if (bCopyMemory)
+ {
+ vboxNetFltWinMemFree(pvMemBuf);
+ }
+ NdisFreePacket(pPacket);
+ pPacket = NULL;
+ }
+ }
+ else
+ {
+ AssertFailed();
+ NdisFreePacket(pPacket);
+ pPacket = NULL;
+ }
+ }
+ else
+ {
+ pPacket = NULL;
+ }
+
+ DBG_CHECK_PACKET_AND_SG(pPacket, pSG);
+
+ return pPacket;
+}
+
+/*
+ * frees NDIS_PACKET created with vboxNetFltWinNdisPacketFromSG
+ */
+DECLHIDDEN(void) vboxNetFltWinFreeSGNdisPacket(PNDIS_PACKET pPacket, bool bFreeMem)
+{
+ UINT cBufCount;
+ PNDIS_BUFFER pFirstBuffer;
+ UINT uTotalPacketLength;
+ PNDIS_BUFFER pBuffer;
+
+ NdisQueryPacket(pPacket, NULL, &cBufCount, &pFirstBuffer, &uTotalPacketLength);
+
+ Assert(cBufCount == 1);
+
+ do
+ {
+ NdisUnchainBufferAtBack(pPacket, &pBuffer);
+ if (pBuffer != NULL)
+ {
+ PVOID pvMemBuf;
+ UINT cbLength;
+
+ NdisQueryBufferSafe(pBuffer, &pvMemBuf, &cbLength, NormalPagePriority);
+ NdisFreeBuffer(pBuffer);
+ if (bFreeMem)
+ {
+ vboxNetFltWinMemFree(pvMemBuf);
+ }
+ }
+ else
+ {
+ break;
+ }
+ } while (true);
+
+ NdisFreePacket(pPacket);
+}
+
+#if !defined(VBOXNETADP)
+static void vboxNetFltWinAssociateMiniportProtocol(PVBOXNETFLTGLOBALS_WIN pGlobalsWin)
+{
+ NdisIMAssociateMiniport(pGlobalsWin->Mp.hMiniport, pGlobalsWin->Pt.hProtocol);
+}
+#endif
+
+/*
+ * NetFlt driver unload function
+ */
+DECLHIDDEN(VOID) vboxNetFltWinUnload(IN PDRIVER_OBJECT DriverObject)
+{
+ int rc;
+ UNREFERENCED_PARAMETER(DriverObject);
+
+ LogFlowFunc(("ENTER: DO (0x%x)\n", DriverObject));
+
+ rc = vboxNetFltWinFiniIdc();
+ if (RT_FAILURE(rc))
+ {
+ /** @todo we can not prevent driver unload here */
+ AssertFailed();
+
+ LogFlowFunc(("vboxNetFltWinFiniIdc - failed, busy.\n"));
+ }
+
+ vboxNetFltWinJobFiniQueue(&g_VBoxJobQueue);
+#ifndef VBOXNETADP
+ vboxNetFltWinPtDeregister(&g_VBoxNetFltGlobalsWin.Pt);
+#endif
+
+ vboxNetFltWinMpDeregister(&g_VBoxNetFltGlobalsWin.Mp);
+
+#ifndef VBOXNETADP
+ NdisFreeSpinLock(&g_VBoxNetFltGlobalsWin.lockFilters);
+#endif /* VBOXNETADP */
+
+ LogFlow(("LEAVE: DO (0x%x)\n", DriverObject));
+
+ vboxNetFltWinFiniNetFltBase();
+ /* don't use logging or any RT after de-init */
+}
+
+RT_C_DECLS_BEGIN
+
+NTSTATUS DriverEntry(IN PDRIVER_OBJECT DriverObject, IN PUNICODE_STRING RegistryPath);
+
+RT_C_DECLS_END
+
+NTSTATUS DriverEntry(IN PDRIVER_OBJECT DriverObject, IN PUNICODE_STRING RegistryPath)
+{
+ NDIS_STATUS Status = NDIS_STATUS_SUCCESS;
+ int rc;
+
+ /* the idc registration is initiated via IOCTL since our driver
+ * can be loaded when the VBoxDrv is not in case we are a Ndis IM driver */
+ rc = vboxNetFltWinInitNetFltBase();
+ AssertRC(rc);
+ if (RT_SUCCESS(rc))
+ {
+ Status = vboxNetFltWinJobInitQueue(&g_VBoxJobQueue);
+ Assert(Status == STATUS_SUCCESS);
+ if (Status == STATUS_SUCCESS)
+ {
+ ULONG MjVersion;
+ ULONG MnVersion;
+
+ /* note: we do it after we initialize the Job Queue */
+ vboxNetFltWinStartInitIdcProbing();
+
+ NdisZeroMemory(&g_VBoxNetFltGlobalsWin, sizeof (g_VBoxNetFltGlobalsWin));
+ KeInitializeEvent(&g_VBoxNetFltGlobalsWin.SynchEvent, SynchronizationEvent, TRUE /* signalled*/);
+
+ PsGetVersion(&MjVersion, &MnVersion,
+ NULL, /* PULONG BuildNumber OPTIONAL */
+ NULL /* PUNICODE_STRING CSDVersion OPTIONAL */
+ );
+
+ g_VBoxNetFltGlobalsWin.fPacketDontLoopBack = NDIS_FLAGS_DONT_LOOPBACK;
+
+ if (MjVersion == 5 && MnVersion == 0)
+ {
+ /* this is Win2k, we don't support it actually, but just in case */
+ g_VBoxNetFltGlobalsWin.fPacketDontLoopBack |= NDIS_FLAGS_SKIP_LOOPBACK_W2K;
+ }
+
+ g_VBoxNetFltGlobalsWin.fPacketIsLoopedBack = NDIS_FLAGS_IS_LOOPBACK_PACKET;
+
+#ifndef VBOXNETADP
+ RTListInit(&g_VBoxNetFltGlobalsWin.listFilters);
+ NdisAllocateSpinLock(&g_VBoxNetFltGlobalsWin.lockFilters);
+#endif
+
+ Status = vboxNetFltWinMpRegister(&g_VBoxNetFltGlobalsWin.Mp, DriverObject, RegistryPath);
+ Assert(Status == STATUS_SUCCESS);
+ if (Status == NDIS_STATUS_SUCCESS)
+ {
+#ifndef VBOXNETADP
+ Status = vboxNetFltWinPtRegister(&g_VBoxNetFltGlobalsWin.Pt, DriverObject, RegistryPath);
+ Assert(Status == STATUS_SUCCESS);
+ if (Status == NDIS_STATUS_SUCCESS)
+#endif
+ {
+#ifndef VBOXNETADP
+ vboxNetFltWinAssociateMiniportProtocol(&g_VBoxNetFltGlobalsWin);
+#endif
+ return STATUS_SUCCESS;
+
+//#ifndef VBOXNETADP
+// vboxNetFltWinPtDeregister(&g_VBoxNetFltGlobalsWin.Pt);
+//#endif
+ }
+#ifndef VBOXNETADP /* unreachable for VBOXNETADP because of the above return */
+ vboxNetFltWinMpDeregister(&g_VBoxNetFltGlobalsWin.Mp);
+# ifndef VBOXNETADP
+ NdisFreeSpinLock(&g_VBoxNetFltGlobalsWin.lockFilters);
+# endif
+#endif
+ }
+ vboxNetFltWinJobFiniQueue(&g_VBoxJobQueue);
+ }
+ vboxNetFltWinFiniNetFlt();
+ }
+ else
+ {
+ Status = NDIS_STATUS_FAILURE;
+ }
+
+ return Status;
+}
+
+#ifndef VBOXNETADP
+/**
+ * creates and initializes the packet to be sent to the underlying miniport given a packet posted to our miniport edge
+ * according to DDK docs we must create our own packet rather than posting the one passed to us
+ */
+DECLHIDDEN(NDIS_STATUS) vboxNetFltWinPrepareSendPacket(PVBOXNETFLTINS pNetFlt, PNDIS_PACKET pPacket, PNDIS_PACKET *ppMyPacket)
+{
+ NDIS_STATUS Status;
+
+ NdisAllocatePacket(&Status, ppMyPacket, pNetFlt->u.s.WinIf.hSendPacketPool);
+
+ if (Status == NDIS_STATUS_SUCCESS)
+ {
+ PVBOXNETFLT_PKTRSVD_PT pSendInfo = (PVBOXNETFLT_PKTRSVD_PT)((*ppMyPacket)->ProtocolReserved);
+ pSendInfo->pOrigPacket = pPacket;
+ pSendInfo->pBufToFree = NULL;
+ /* the rest will be filled on send */
+
+ vboxNetFltWinCopyPacketInfoOnSend(*ppMyPacket, pPacket);
+
+#ifdef VBOX_LOOPBACK_USEFLAGS
+ NdisGetPacketFlags(*ppMyPacket) |= g_VBoxNetFltGlobalsWin.fPacketDontLoopBack;
+#endif
+ }
+ else
+ {
+ *ppMyPacket = NULL;
+ }
+
+ return Status;
+}
+
+/**
+ * creates and initializes the packet to be sent to the upperlying protocol given a packet indicated to our protocol edge
+ * according to DDK docs we must create our own packet rather than posting the one passed to us
+ */
+DECLHIDDEN(NDIS_STATUS) vboxNetFltWinPrepareRecvPacket(PVBOXNETFLTINS pNetFlt, PNDIS_PACKET pPacket, PNDIS_PACKET *ppMyPacket, bool bDpr)
+{
+ NDIS_STATUS Status;
+
+ if (bDpr)
+ {
+ Assert(KeGetCurrentIrql() == DISPATCH_LEVEL);
+ NdisDprAllocatePacket(&Status, ppMyPacket, pNetFlt->u.s.WinIf.hRecvPacketPool);
+ }
+ else
+ {
+ NdisAllocatePacket(&Status, ppMyPacket, pNetFlt->u.s.WinIf.hRecvPacketPool);
+ }
+
+ if (Status == NDIS_STATUS_SUCCESS)
+ {
+ PVBOXNETFLT_PKTRSVD_MP pRecvInfo = (PVBOXNETFLT_PKTRSVD_MP)((*ppMyPacket)->MiniportReserved);
+ pRecvInfo->pOrigPacket = pPacket;
+ pRecvInfo->pBufToFree = NULL;
+
+ Status = vboxNetFltWinCopyPacketInfoOnRecv(*ppMyPacket, pPacket, false);
+ }
+ else
+ {
+ *ppMyPacket = NULL;
+ }
+ return Status;
+}
+#endif
+/**
+ * initializes the VBOXNETFLTINS (our context structure) and binds to the given adapter
+ */
+#if defined(VBOXNETADP)
+DECLHIDDEN(NDIS_STATUS) vboxNetFltWinPtInitBind(PVBOXNETFLTINS *ppNetFlt, NDIS_HANDLE hMiniportAdapter, PNDIS_STRING pBindToMiniportName /* actually this is our miniport name*/, NDIS_HANDLE hWrapperConfigurationContext)
+#else
+DECLHIDDEN(NDIS_STATUS) vboxNetFltWinPtInitBind(PVBOXNETFLTINS *ppNetFlt, PNDIS_STRING pOurMiniportName, PNDIS_STRING pBindToMiniportName)
+#endif
+{
+ NDIS_STATUS Status;
+ do
+ {
+ ANSI_STRING AnsiString;
+ int rc;
+ PVBOXNETFLTINS pInstance;
+ USHORT cbAnsiName = pBindToMiniportName->Length;/* the length is is bytes ; *2 ;RtlUnicodeStringToAnsiSize(pBindToMiniportName)*/
+ CREATE_INSTANCE_CONTEXT Context;
+
+# ifndef VBOXNETADP
+ Context.pOurName = pOurMiniportName;
+ Context.pBindToName = pBindToMiniportName;
+# else
+ Context.hMiniportAdapter = hMiniportAdapter;
+ Context.hWrapperConfigurationContext = hWrapperConfigurationContext;
+# endif
+ Context.Status = NDIS_STATUS_SUCCESS;
+
+ AnsiString.Buffer = 0; /* will be allocated by RtlUnicodeStringToAnsiString */
+ AnsiString.Length = 0;
+ AnsiString.MaximumLength = cbAnsiName;
+
+ Assert(KeGetCurrentIrql() == PASSIVE_LEVEL);
+
+ Status = RtlUnicodeStringToAnsiString(&AnsiString, pBindToMiniportName, true);
+
+ if (Status != STATUS_SUCCESS)
+ {
+ break;
+ }
+
+ rc = vboxNetFltSearchCreateInstance(&g_VBoxNetFltGlobals, AnsiString.Buffer, &pInstance, &Context);
+ RtlFreeAnsiString(&AnsiString);
+ if (RT_FAILURE(rc))
+ {
+ AssertFailed();
+ Status = Context.Status != NDIS_STATUS_SUCCESS ? Context.Status : NDIS_STATUS_FAILURE;
+ break;
+ }
+
+ Assert(pInstance);
+
+ if (rc == VINF_ALREADY_INITIALIZED)
+ {
+ /* the case when our adapter was unbound while IntNet was connected to it */
+ /* the instance remains valid until IntNet disconnects from it, we simply search and re-use it*/
+ rc = vboxNetFltWinAttachToInterface(pInstance, &Context, true);
+ if (RT_FAILURE(rc))
+ {
+ AssertFailed();
+ Status = Context.Status != NDIS_STATUS_SUCCESS ? Context.Status : NDIS_STATUS_FAILURE;
+ /* release netflt */
+ vboxNetFltRelease(pInstance, false);
+
+ break;
+ }
+ }
+
+ *ppNetFlt = pInstance;
+
+ } while (FALSE);
+
+ return Status;
+}
+/*
+ * deinitializes the VBOXNETFLTWIN
+ */
+DECLHIDDEN(VOID) vboxNetFltWinPtFiniWinIf(PVBOXNETFLTWIN pWinIf)
+{
+#ifndef VBOXNETADP
+ int rc;
+#endif
+
+ LogFlowFunc(("ENTER: pWinIf 0x%p\n", pWinIf));
+
+ Assert(KeGetCurrentIrql() == PASSIVE_LEVEL);
+#ifndef VBOXNETADP
+ if (pWinIf->MpDeviceName.Buffer)
+ {
+ vboxNetFltWinMemFree(pWinIf->MpDeviceName.Buffer);
+ }
+
+ FINI_INTERLOCKED_SINGLE_LIST(&pWinIf->TransferDataList);
+# if defined(DEBUG_NETFLT_LOOPBACK) || !defined(VBOX_LOOPBACK_USEFLAGS)
+ FINI_INTERLOCKED_SINGLE_LIST(&pWinIf->SendPacketQueue);
+# endif
+ NdisFreeBufferPool(pWinIf->hSendBufferPool);
+ NdisFreePacketPool(pWinIf->hSendPacketPool);
+ rc = RTSemFastMutexDestroy(pWinIf->hSynchRequestMutex); AssertRC(rc);
+#endif
+
+ /* NOTE: NULL is a valid handle */
+ NdisFreeBufferPool(pWinIf->hRecvBufferPool);
+ NdisFreePacketPool(pWinIf->hRecvPacketPool);
+
+ LogFlowFunc(("LEAVE: pWinIf 0x%p\n", pWinIf));
+}
+
+#ifndef VBOXNETADP
+DECLHIDDEN(NDIS_STATUS) vboxNetFltWinPtInitWinIf(PVBOXNETFLTWIN pWinIf, IN PNDIS_STRING pOurDeviceName)
+#else
+DECLHIDDEN(NDIS_STATUS) vboxNetFltWinPtInitWinIf(PVBOXNETFLTWIN pWinIf)
+#endif
+{
+ NDIS_STATUS Status = NDIS_STATUS_SUCCESS;
+#ifndef VBOXNETADP
+ int rc;
+#endif
+
+ LogFlowFunc(("ENTER: pWinIf 0x%p\n", pWinIf));
+
+ Assert(KeGetCurrentIrql() == PASSIVE_LEVEL);
+
+ NdisZeroMemory(pWinIf, sizeof (VBOXNETFLTWIN));
+ NdisAllocatePacketPoolEx(&Status, &pWinIf->hRecvPacketPool,
+ VBOXNETFLT_PACKET_POOL_SIZE_NORMAL,
+ VBOXNETFLT_PACKET_POOL_SIZE_OVERFLOW,
+ PROTOCOL_RESERVED_SIZE_IN_PACKET);
+ Assert(Status == NDIS_STATUS_SUCCESS);
+ if (Status == NDIS_STATUS_SUCCESS)
+ {
+ /* NOTE: NULL is a valid handle !!! */
+ NdisAllocateBufferPool(&Status, &pWinIf->hRecvBufferPool, VBOXNETFLT_BUFFER_POOL_SIZE_RX);
+ Assert(Status == NDIS_STATUS_SUCCESS);
+ if (Status == NDIS_STATUS_SUCCESS)
+ {
+ pWinIf->MpState.PowerState = NdisDeviceStateD3;
+ vboxNetFltWinSetOpState(&pWinIf->MpState, kVBoxNetDevOpState_Deinitialized);
+#ifndef VBOXNETADP
+ pWinIf->PtState.PowerState = NdisDeviceStateD3;
+ vboxNetFltWinSetOpState(&pWinIf->PtState, kVBoxNetDevOpState_Deinitialized);
+
+ NdisAllocateBufferPool(&Status,
+ &pWinIf->hSendBufferPool,
+ VBOXNETFLT_BUFFER_POOL_SIZE_TX);
+ Assert(Status == NDIS_STATUS_SUCCESS);
+ if (Status == NDIS_STATUS_SUCCESS)
+ {
+ INIT_INTERLOCKED_SINGLE_LIST(&pWinIf->TransferDataList);
+
+# if defined(DEBUG_NETFLT_LOOPBACK) || !defined(VBOX_LOOPBACK_USEFLAGS)
+ INIT_INTERLOCKED_SINGLE_LIST(&pWinIf->SendPacketQueue);
+# endif
+ NdisInitializeEvent(&pWinIf->OpenCloseEvent);
+
+ KeInitializeEvent(&pWinIf->hSynchCompletionEvent, SynchronizationEvent, FALSE);
+
+ NdisInitializeEvent(&pWinIf->MpInitCompleteEvent);
+
+ NdisAllocatePacketPoolEx(&Status, &pWinIf->hSendPacketPool,
+ VBOXNETFLT_PACKET_POOL_SIZE_NORMAL,
+ VBOXNETFLT_PACKET_POOL_SIZE_OVERFLOW,
+ sizeof (PVBOXNETFLT_PKTRSVD_PT));
+ Assert(Status == NDIS_STATUS_SUCCESS);
+ if (Status == NDIS_STATUS_SUCCESS)
+ {
+ rc = RTSemFastMutexCreate(&pWinIf->hSynchRequestMutex);
+ AssertRC(rc);
+ if (RT_SUCCESS(rc))
+ {
+ Status = vboxNetFltWinMemAlloc((PVOID*)&pWinIf->MpDeviceName.Buffer, pOurDeviceName->Length);
+ Assert(Status == NDIS_STATUS_SUCCESS);
+ if (Status == NDIS_STATUS_SUCCESS)
+ {
+ pWinIf->MpDeviceName.MaximumLength = pOurDeviceName->Length;
+ pWinIf->MpDeviceName.Length = 0;
+ Status = vboxNetFltWinCopyString(&pWinIf->MpDeviceName, pOurDeviceName);
+#endif
+ return NDIS_STATUS_SUCCESS;
+#ifndef VBOXNETADP
+ // unreachable: vboxNetFltWinMemFree(pWinIf->MpDeviceName.Buffer);
+ }
+ RTSemFastMutexDestroy(pWinIf->hSynchRequestMutex);
+ }
+ else
+ Status = NDIS_STATUS_FAILURE;
+ NdisFreePacketPool(pWinIf->hSendPacketPool);
+ }
+ NdisFreeBufferPool(pWinIf->hSendBufferPool);
+ }
+ NdisFreeBufferPool(pWinIf->hRecvBufferPool);
+#endif
+ }
+ NdisFreePacketPool(pWinIf->hRecvPacketPool);
+ }
+
+ LogFlowFunc(("LEAVE: pWinIf 0x%p, Status 0x%x\n", pWinIf, Status));
+
+ return Status;
+}
+
+/**
+ * match packets
+ */
+#define NEXT_LIST_ENTRY(_Entry) ((_Entry)->Flink)
+#define PREV_LIST_ENTRY(_Entry) ((_Entry)->Blink)
+#define FIRST_LIST_ENTRY NEXT_LIST_ENTRY
+#define LAST_LIST_ENTRY PREV_LIST_ENTRY
+
+#define MIN(_a, _b) ((_a) < (_b) ? (_a) : (_b))
+
+#ifndef VBOXNETADP
+
+#ifdef DEBUG_misha
+
+RTMAC g_vboxNetFltWinVerifyMACBroadcast = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
+RTMAC g_vboxNetFltWinVerifyMACGuest = {0x08, 0x00, 0x27, 0x01, 0x02, 0x03};
+
+DECLHIDDEN(PRTNETETHERHDR) vboxNetFltWinGetEthHdr(PNDIS_PACKET pPacket)
+{
+ UINT cBufCount1;
+ PNDIS_BUFFER pBuffer1;
+ UINT uTotalPacketLength1;
+ RTNETETHERHDR* pEth;
+ UINT cbLength1 = 0;
+ UINT i = 0;
+
+ NdisQueryPacket(pPacket, NULL, &cBufCount1, &pBuffer1, &uTotalPacketLength1);
+
+ Assert(pBuffer1);
+ Assert(uTotalPacketLength1 >= VBOXNETFLT_PACKET_ETHEADER_SIZE);
+ if (uTotalPacketLength1 < VBOXNETFLT_PACKET_ETHEADER_SIZE)
+ return NULL;
+
+ NdisQueryBufferSafe(pBuffer1, &pEth, &cbLength1, NormalPagePriority);
+ Assert(cbLength1 >= VBOXNETFLT_PACKET_ETHEADER_SIZE);
+ if (cbLength1 < VBOXNETFLT_PACKET_ETHEADER_SIZE)
+ return NULL;
+
+ return pEth;
+}
+
+DECLHIDDEN(PRTNETETHERHDR) vboxNetFltWinGetEthHdrSG(PINTNETSG pSG)
+{
+ Assert(pSG->cSegsUsed);
+ Assert(pSG->cSegsAlloc >= pSG->cSegsUsed);
+ Assert(pSG->aSegs[0].cb >= VBOXNETFLT_PACKET_ETHEADER_SIZE);
+
+ if (!pSG->cSegsUsed)
+ return NULL;
+
+ if (pSG->aSegs[0].cb < VBOXNETFLT_PACKET_ETHEADER_SIZE)
+ return NULL;
+
+ return (PRTNETETHERHDR)pSG->aSegs[0].pv;
+}
+
+DECLHIDDEN(bool) vboxNetFltWinCheckMACs(PNDIS_PACKET pPacket, PRTMAC pDst, PRTMAC pSrc)
+{
+ PRTNETETHERHDR pHdr = vboxNetFltWinGetEthHdr(pPacket);
+ Assert(pHdr);
+
+ if (!pHdr)
+ return false;
+
+ if (pDst && memcmp(pDst, &pHdr->DstMac, sizeof(RTMAC)))
+ return false;
+
+ if (pSrc && memcmp(pSrc, &pHdr->SrcMac, sizeof(RTMAC)))
+ return false;
+
+ return true;
+}
+
+DECLHIDDEN(bool) vboxNetFltWinCheckMACsSG(PINTNETSG pSG, PRTMAC pDst, PRTMAC pSrc)
+{
+ PRTNETETHERHDR pHdr = vboxNetFltWinGetEthHdrSG(pSG);
+ Assert(pHdr);
+
+ if (!pHdr)
+ return false;
+
+ if (pDst && memcmp(pDst, &pHdr->DstMac, sizeof(RTMAC)))
+ return false;
+
+ if (pSrc && memcmp(pSrc, &pHdr->SrcMac, sizeof(RTMAC)))
+ return false;
+
+ return true;
+}
+#endif
+
+# if !defined(VBOX_LOOPBACK_USEFLAGS) || defined(DEBUG_NETFLT_PACKETS)
+/*
+ * answers whether the two given packets match based on the packet length and the first cbMatch bytes of the packets
+ * if cbMatch < 0 matches complete packets.
+ */
+DECLHIDDEN(bool) vboxNetFltWinMatchPackets(PNDIS_PACKET pPacket1, PNDIS_PACKET pPacket2, const INT cbMatch)
+{
+ UINT cBufCount1;
+ PNDIS_BUFFER pBuffer1;
+ UINT uTotalPacketLength1;
+ uint8_t *pbMemBuf1 = NULL;
+ UINT cbLength1 = 0;
+
+ UINT cBufCount2;
+ PNDIS_BUFFER pBuffer2;
+ UINT uTotalPacketLength2;
+ uint8_t *pbMemBuf2 = NULL;
+ UINT cbLength2 = 0;
+ bool bMatch = true;
+
+#ifdef DEBUG_NETFLT_PACKETS
+ bool bCompleteMatch = false;
+#endif
+
+ NdisQueryPacket(pPacket1, NULL, &cBufCount1, &pBuffer1, &uTotalPacketLength1);
+ NdisQueryPacket(pPacket2, NULL, &cBufCount2, &pBuffer2, &uTotalPacketLength2);
+
+ Assert(pBuffer1);
+ Assert(pBuffer2);
+
+ if (uTotalPacketLength1 != uTotalPacketLength2)
+ {
+ bMatch = false;
+ }
+ else
+ {
+ UINT ucbLength2Match = 0;
+ UINT ucbMatch;
+ if (cbMatch < 0 || (UINT)cbMatch > uTotalPacketLength1)
+ {
+ /* NOTE: assuming uTotalPacketLength1 == uTotalPacketLength2*/
+ ucbMatch = uTotalPacketLength1;
+#ifdef DEBUG_NETFLT_PACKETS
+ bCompleteMatch = true;
+#endif
+ }
+ else
+ {
+ ucbMatch = (UINT)cbMatch;
+ }
+
+ for (;;)
+ {
+ if (!cbLength1)
+ {
+ NdisQueryBufferSafe(pBuffer1, &pbMemBuf1, &cbLength1, NormalPagePriority);
+ NdisGetNextBuffer(pBuffer1, &pBuffer1);
+ }
+ else
+ {
+ Assert(pbMemBuf1);
+ Assert(ucbLength2Match);
+ pbMemBuf1 += ucbLength2Match;
+ }
+
+ if (!cbLength2)
+ {
+ NdisQueryBufferSafe(pBuffer2, &pbMemBuf2, &cbLength2, NormalPagePriority);
+ NdisGetNextBuffer(pBuffer2, &pBuffer2);
+ }
+ else
+ {
+ Assert(pbMemBuf2);
+ Assert(ucbLength2Match);
+ pbMemBuf2 += ucbLength2Match;
+ }
+
+ ucbLength2Match = MIN(ucbMatch, cbLength1);
+ ucbLength2Match = MIN(ucbLength2Match, cbLength2);
+
+ if (memcmp(pbMemBuf1, pbMemBuf2, ucbLength2Match))
+ {
+ bMatch = false;
+ break;
+ }
+
+ ucbMatch -= ucbLength2Match;
+ if (!ucbMatch)
+ break;
+
+ cbLength1 -= ucbLength2Match;
+ cbLength2 -= ucbLength2Match;
+ }
+ }
+
+#ifdef DEBUG_NETFLT_PACKETS
+ if (bMatch && !bCompleteMatch)
+ {
+ /* check that the packets fully match */
+ DBG_CHECK_PACKETS(pPacket1, pPacket2);
+ }
+#endif
+
+ return bMatch;
+}
+
+/*
+ * answers whether the ndis packet and PINTNETSG match based on the packet length and the first cbMatch bytes of the packet and PINTNETSG
+ * if cbMatch < 0 matches complete packets.
+ */
+DECLHIDDEN(bool) vboxNetFltWinMatchPacketAndSG(PNDIS_PACKET pPacket, PINTNETSG pSG, const INT cbMatch)
+{
+ UINT cBufCount1;
+ PNDIS_BUFFER pBuffer1;
+ UINT uTotalPacketLength1;
+ uint8_t *pbMemBuf1 = NULL;
+ UINT cbLength1 = 0;
+ UINT uTotalPacketLength2 = pSG->cbTotal;
+ uint8_t *pbMemBuf2 = NULL;
+ UINT cbLength2 = 0;
+ bool bMatch = true;
+ bool bCompleteMatch = false;
+ UINT i = 0;
+
+ NdisQueryPacket(pPacket, NULL, &cBufCount1, &pBuffer1, &uTotalPacketLength1);
+
+ Assert(pBuffer1);
+ Assert(pSG->cSegsUsed);
+ Assert(pSG->cSegsAlloc >= pSG->cSegsUsed);
+
+ if (uTotalPacketLength1 != uTotalPacketLength2)
+ {
+ AssertFailed();
+ bMatch = false;
+ }
+ else
+ {
+ UINT ucbLength2Match = 0;
+ UINT ucbMatch;
+
+ if (cbMatch < 0 || (UINT)cbMatch > uTotalPacketLength1)
+ {
+ /* NOTE: assuming uTotalPacketLength1 == uTotalPacketLength2*/
+ ucbMatch = uTotalPacketLength1;
+ bCompleteMatch = true;
+ }
+ else
+ {
+ ucbMatch = (UINT)cbMatch;
+ }
+
+ for (;;)
+ {
+ if (!cbLength1)
+ {
+ NdisQueryBufferSafe(pBuffer1, &pbMemBuf1, &cbLength1, NormalPagePriority);
+ NdisGetNextBuffer(pBuffer1, &pBuffer1);
+ }
+ else
+ {
+ Assert(pbMemBuf1);
+ Assert(ucbLength2Match);
+ pbMemBuf1 += ucbLength2Match;
+ }
+
+ if (!cbLength2)
+ {
+ Assert(i < pSG->cSegsUsed);
+ pbMemBuf2 = (uint8_t*)pSG->aSegs[i].pv;
+ cbLength2 = pSG->aSegs[i].cb;
+ i++;
+ }
+ else
+ {
+ Assert(pbMemBuf2);
+ Assert(ucbLength2Match);
+ pbMemBuf2 += ucbLength2Match;
+ }
+
+ ucbLength2Match = MIN(ucbMatch, cbLength1);
+ ucbLength2Match = MIN(ucbLength2Match, cbLength2);
+
+ if (memcmp(pbMemBuf1, pbMemBuf2, ucbLength2Match))
+ {
+ bMatch = false;
+ AssertFailed();
+ break;
+ }
+
+ ucbMatch -= ucbLength2Match;
+ if (!ucbMatch)
+ break;
+
+ cbLength1 -= ucbLength2Match;
+ cbLength2 -= ucbLength2Match;
+ }
+ }
+
+ if (bMatch && !bCompleteMatch)
+ {
+ /* check that the packets fully match */
+ DBG_CHECK_PACKET_AND_SG(pPacket, pSG);
+ }
+ return bMatch;
+}
+
+# if 0
+/*
+ * answers whether the two PINTNETSGs match based on the packet length and the first cbMatch bytes of the PINTNETSG
+ * if cbMatch < 0 matches complete packets.
+ */
+static bool vboxNetFltWinMatchSGs(PINTNETSG pSG1, PINTNETSG pSG2, const INT cbMatch)
+{
+ UINT uTotalPacketLength1 = pSG1->cbTotal;
+ PVOID pbMemBuf1 = NULL;
+ UINT cbLength1 = 0;
+ UINT i1 = 0;
+ UINT uTotalPacketLength2 = pSG2->cbTotal;
+ PVOID pbMemBuf2 = NULL;
+ UINT cbLength2 = 0;
+
+ bool bMatch = true;
+ bool bCompleteMatch = false;
+ UINT i2 = 0;
+
+ Assert(pSG1->cSegsUsed);
+ Assert(pSG2->cSegsUsed);
+ Assert(pSG1->cSegsAlloc >= pSG1->cSegsUsed);
+ Assert(pSG2->cSegsAlloc >= pSG2->cSegsUsed);
+
+ if (uTotalPacketLength1 != uTotalPacketLength2)
+ {
+ AssertFailed();
+ bMatch = false;
+ }
+ else
+ {
+ UINT ucbMatch;
+ if (cbMatch < 0 || (UINT)cbMatch > uTotalPacketLength1)
+ {
+ /* NOTE: assuming uTotalPacketLength1 == uTotalPacketLength2*/
+ ucbMatch = uTotalPacketLength1;
+ bCompleteMatch = true;
+ }
+ else
+ {
+ ucbMatch = (UINT)cbMatch;
+ }
+
+ do
+ {
+ UINT ucbLength2Match;
+ if (!cbLength1)
+ {
+ Assert(i1 < pSG1->cSegsUsed);
+ pbMemBuf1 = pSG1->aSegs[i1].pv;
+ cbLength1 = pSG1->aSegs[i1].cb;
+ i1++;
+ }
+
+ if (!cbLength2)
+ {
+ Assert(i2 < pSG2->cSegsUsed);
+ pbMemBuf2 = pSG2->aSegs[i2].pv;
+ cbLength2 = pSG2->aSegs[i2].cb;
+ i2++;
+ }
+
+ ucbLength2Match = MIN(ucbMatch, cbLength1);
+ ucbLength2Match = MIN(ucbLength2Match, cbLength2);
+
+ if (memcmp(pbMemBuf1, pbMemBuf2, ucbLength2Match))
+ {
+ bMatch = false;
+ AssertFailed();
+ break;
+ }
+ ucbMatch -= ucbLength2Match;
+ cbLength1 -= ucbLength2Match;
+ cbLength2 -= ucbLength2Match;
+ } while (ucbMatch);
+ }
+
+ if (bMatch && !bCompleteMatch)
+ {
+ /* check that the packets fully match */
+ DBG_CHECK_SGS(pSG1, pSG2);
+ }
+ return bMatch;
+}
+# endif
+# endif
+#endif
+
+static void vboxNetFltWinFiniNetFltBase()
+{
+ do
+ {
+ vboxNetFltDeleteGlobals(&g_VBoxNetFltGlobals);
+
+ /*
+ * Undo the work done during start (in reverse order).
+ */
+ memset(&g_VBoxNetFltGlobals, 0, sizeof(g_VBoxNetFltGlobals));
+
+ RTLogDestroy(RTLogRelSetDefaultInstance(NULL));
+ RTLogDestroy(RTLogSetDefaultInstance(NULL));
+
+ RTR0Term();
+ } while (0);
+}
+
+/*
+ * Defines max timeout for waiting for driver unloading
+ * (3000 * 100 ms = 5 minutes)
+ */
+#define MAX_UNLOAD_PROBES 3000
+
+static int vboxNetFltWinFiniIdc()
+{
+ int rc;
+ int i;
+
+ vboxNetFltWinStopInitIdcProbing();
+
+ if (g_bVBoxIdcInitialized)
+ {
+ for (i = 0; (rc = vboxNetFltTryDeleteIdc(&g_VBoxNetFltGlobals)) == VERR_WRONG_ORDER
+ && i < MAX_UNLOAD_PROBES; i++)
+ {
+ RTThreadSleep(100);
+ }
+ if (i == MAX_UNLOAD_PROBES)
+ {
+ // seems something hungs in driver
+ LogFlow(("vboxNetFltWinFiniIdc - Can't delete Idc. pInH=%p cFRefs=%d fIDcOpen=%s",
+ g_VBoxNetFltGlobals.pInstanceHead, g_VBoxNetFltGlobals.cFactoryRefs,
+ g_VBoxNetFltGlobals.fIDCOpen ? "true" : "false"));
+ LogFlow(("vboxNetFltWinFiniIdc g_VBoxNetFltGlobalsWin cDvRefs=%d hDev=%x pDev=%p Mp=%x \n",
+ g_VBoxNetFltGlobalsWin.cDeviceRefs, g_VBoxNetFltGlobalsWin.hDevice,
+ g_VBoxNetFltGlobalsWin.pDevObj, g_VBoxNetFltGlobalsWin.Mp.hMiniport));
+ Assert(i == MAX_UNLOAD_PROBES);
+ return VERR_WRONG_ORDER;
+ }
+
+ if (RT_SUCCESS(rc))
+ {
+ g_bVBoxIdcInitialized = false;
+ }
+ }
+ else
+ {
+ rc = VINF_SUCCESS;
+ }
+ return rc;
+
+}
+
+static int vboxNetFltWinFiniNetFlt()
+{
+ int rc = vboxNetFltWinFiniIdc();
+ if (RT_SUCCESS(rc))
+ {
+ vboxNetFltWinFiniNetFltBase();
+ }
+ return rc;
+}
+
+/**
+ * base netflt initialization
+ */
+static int vboxNetFltWinInitNetFltBase()
+{
+ int rc;
+
+ do
+ {
+ Assert(!g_bVBoxIdcInitialized);
+
+ rc = RTR0Init(0);
+ if (!RT_SUCCESS(rc))
+ {
+ break;
+ }
+
+ memset(&g_VBoxNetFltGlobals, 0, sizeof(g_VBoxNetFltGlobals));
+ rc = vboxNetFltInitGlobals(&g_VBoxNetFltGlobals);
+ if (!RT_SUCCESS(rc))
+ {
+ RTR0Term();
+ break;
+ }
+ }while (0);
+
+ return rc;
+}
+
+/**
+ * initialize IDC
+ */
+static int vboxNetFltWinInitIdc()
+{
+ int rc;
+
+ do
+ {
+ if (g_bVBoxIdcInitialized)
+ {
+ rc = VINF_ALREADY_INITIALIZED;
+ break;
+ }
+
+ /*
+ * connect to the support driver.
+ *
+ * This will call back vboxNetFltOsOpenSupDrv (and maybe vboxNetFltOsCloseSupDrv)
+ * for establishing the connect to the support driver.
+ */
+ rc = vboxNetFltInitIdc(&g_VBoxNetFltGlobals);
+ if (!RT_SUCCESS(rc))
+ {
+ break;
+ }
+
+ g_bVBoxIdcInitialized = true;
+ } while (0);
+
+ return rc;
+}
+
+static VOID vboxNetFltWinInitIdcProbingWorker(PVOID pvContext)
+{
+ PINIT_IDC_INFO pInitIdcInfo = (PINIT_IDC_INFO)pvContext;
+ int rc = vboxNetFltWinInitIdc();
+ if (RT_FAILURE(rc))
+ {
+ bool bInterupted = ASMAtomicUoReadBool(&pInitIdcInfo->bStop);
+ if (!bInterupted)
+ {
+ RTThreadSleep(1000); /* 1 s */
+ bInterupted = ASMAtomicUoReadBool(&pInitIdcInfo->bStop);
+ if (!bInterupted)
+ {
+ vboxNetFltWinJobEnqueueJob(&g_VBoxJobQueue, &pInitIdcInfo->Job, false);
+ return;
+ }
+ }
+
+ /* it's interrupted */
+ rc = VERR_INTERRUPTED;
+ }
+
+ ASMAtomicUoWriteS32(&pInitIdcInfo->rc, rc);
+ KeSetEvent(&pInitIdcInfo->hCompletionEvent, 0, FALSE);
+}
+
+static int vboxNetFltWinStopInitIdcProbing()
+{
+ if (!g_VBoxInitIdcInfo.bInitialized)
+ return VERR_INVALID_STATE;
+
+ ASMAtomicUoWriteBool(&g_VBoxInitIdcInfo.bStop, true);
+ KeWaitForSingleObject(&g_VBoxInitIdcInfo.hCompletionEvent, Executive, KernelMode, FALSE, NULL);
+
+ return g_VBoxInitIdcInfo.rc;
+}
+
+static int vboxNetFltWinStartInitIdcProbing()
+{
+ Assert(!g_bVBoxIdcInitialized);
+ KeInitializeEvent(&g_VBoxInitIdcInfo.hCompletionEvent, NotificationEvent, FALSE);
+ g_VBoxInitIdcInfo.bStop = false;
+ g_VBoxInitIdcInfo.bInitialized = true;
+ vboxNetFltWinJobInit(&g_VBoxInitIdcInfo.Job, vboxNetFltWinInitIdcProbingWorker, &g_VBoxInitIdcInfo, false);
+ vboxNetFltWinJobEnqueueJob(&g_VBoxJobQueue, &g_VBoxInitIdcInfo.Job, false);
+ return VINF_SUCCESS;
+}
+
+static int vboxNetFltWinInitNetFlt()
+{
+ int rc;
+
+ do
+ {
+ rc = vboxNetFltWinInitNetFltBase();
+ if (RT_FAILURE(rc))
+ {
+ AssertFailed();
+ break;
+ }
+
+ /*
+ * connect to the support driver.
+ *
+ * This will call back vboxNetFltOsOpenSupDrv (and maybe vboxNetFltOsCloseSupDrv)
+ * for establishing the connect to the support driver.
+ */
+ rc = vboxNetFltWinInitIdc();
+ if (RT_FAILURE(rc))
+ {
+ AssertFailed();
+ vboxNetFltWinFiniNetFltBase();
+ break;
+ }
+ } while (0);
+
+ return rc;
+}
+
+/* detach*/
+static int vboxNetFltWinDeleteInstance(PVBOXNETFLTINS pThis)
+{
+ LogFlow(("vboxNetFltWinDeleteInstance: pThis=0x%p \n", pThis));
+
+ Assert(KeGetCurrentIrql() < DISPATCH_LEVEL);
+ Assert(pThis);
+ Assert(pThis->fDisconnectedFromHost);
+ Assert(!pThis->fRediscoveryPending);
+ Assert(pThis->enmTrunkState != INTNETTRUNKIFSTATE_ACTIVE);
+#ifndef VBOXNETADP
+ Assert(pThis->u.s.WinIf.PtState.OpState == kVBoxNetDevOpState_Deinitialized);
+ Assert(!pThis->u.s.WinIf.hBinding);
+#endif
+ Assert(pThis->u.s.WinIf.MpState.OpState == kVBoxNetDevOpState_Deinitialized);
+#ifndef VBOXNETFLT_NO_PACKET_QUEUE
+ Assert(!pThis->u.s.PacketQueueWorker.pSG);
+#endif
+
+ RTSemMutexDestroy(pThis->u.s.hWinIfMutex);
+
+ vboxNetFltWinDrvDereference();
+
+ return VINF_SUCCESS;
+}
+
+static NDIS_STATUS vboxNetFltWinDisconnectIt(PVBOXNETFLTINS pInstance)
+{
+#ifndef VBOXNETFLT_NO_PACKET_QUEUE
+ vboxNetFltWinQuFiniPacketQueue(pInstance);
+#else
+ RT_NOREF1(pInstance);
+#endif
+ return NDIS_STATUS_SUCCESS;
+}
+
+/* detach*/
+DECLHIDDEN(NDIS_STATUS) vboxNetFltWinDetachFromInterface(PVBOXNETFLTINS pNetFlt, bool bOnUnbind)
+{
+ NDIS_STATUS Status;
+ int rc;
+ LogFlowFunc(("ENTER: pThis=%0xp\n", pNetFlt));
+
+ Assert(KeGetCurrentIrql() < DISPATCH_LEVEL);
+ Assert(pNetFlt);
+
+ /* paranoia to ensure the instance is not removed while we're waiting on the mutex
+ * in case ndis does something unpredictable, e.g. calls our miniport halt independently
+ * from protocol unbind and concurrently with it*/
+ vboxNetFltRetain(pNetFlt, false);
+
+ rc = RTSemMutexRequest(pNetFlt->u.s.hWinIfMutex, RT_INDEFINITE_WAIT);
+ if (RT_SUCCESS(rc))
+ {
+ Assert(vboxNetFltWinGetWinIfState(pNetFlt) == kVBoxWinIfState_Connected);
+ Assert(vboxNetFltWinGetOpState(&pNetFlt->u.s.WinIf.MpState) == kVBoxNetDevOpState_Initialized);
+#ifndef VBOXNETADP
+ Assert(vboxNetFltWinGetOpState(&pNetFlt->u.s.WinIf.PtState) == kVBoxNetDevOpState_Initialized);
+#endif
+ if (vboxNetFltWinGetWinIfState(pNetFlt) == kVBoxWinIfState_Connected)
+ {
+ vboxNetFltWinSetWinIfState(pNetFlt, kVBoxWinIfState_Disconnecting);
+#ifndef VBOXNETADP
+ Status = vboxNetFltWinPtDoUnbinding(pNetFlt, bOnUnbind);
+#else
+ Status = vboxNetFltWinMpDoDeinitialization(pNetFlt);
+#endif
+ Assert(Status == NDIS_STATUS_SUCCESS);
+
+ vboxNetFltWinSetWinIfState(pNetFlt, kVBoxWinIfState_Disconnected);
+ Assert(vboxNetFltWinGetOpState(&pNetFlt->u.s.WinIf.MpState) == kVBoxNetDevOpState_Deinitialized);
+#ifndef VBOXNETADP
+ Assert(vboxNetFltWinGetOpState(&pNetFlt->u.s.WinIf.PtState) == kVBoxNetDevOpState_Deinitialized);
+#endif
+ vboxNetFltWinPtFiniWinIf(&pNetFlt->u.s.WinIf);
+
+ /* we're unbinding, make an unbind-related release */
+ vboxNetFltRelease(pNetFlt, false);
+ }
+ else
+ {
+ AssertBreakpoint();
+#ifndef VBOXNETADP
+ pNetFlt->u.s.WinIf.OpenCloseStatus = NDIS_STATUS_FAILURE;
+#endif
+ if (!bOnUnbind)
+ {
+ vboxNetFltWinSetOpState(&pNetFlt->u.s.WinIf.MpState, kVBoxNetDevOpState_Deinitialized);
+ }
+ Status = NDIS_STATUS_FAILURE;
+ }
+ RTSemMutexRelease(pNetFlt->u.s.hWinIfMutex);
+ }
+ else
+ {
+ AssertBreakpoint();
+ Status = NDIS_STATUS_FAILURE;
+ }
+
+ /* release for the retain we made before waining on the mutex */
+ vboxNetFltRelease(pNetFlt, false);
+
+ LogFlowFunc(("LEAVE: Status 0x%x\n", Status));
+
+ return Status;
+}
+
+
+/**
+ * Checks if the host (not us) has put the adapter in promiscuous mode.
+ *
+ * @returns true if promiscuous, false if not.
+ * @param pThis The instance.
+ */
+static bool vboxNetFltWinIsPromiscuous2(PVBOXNETFLTINS pThis)
+{
+#ifndef VBOXNETADP
+ if (VBOXNETFLT_PROMISCUOUS_SUPPORTED(pThis))
+ {
+ bool bPromiscuous;
+ if (!vboxNetFltWinReferenceWinIf(pThis))
+ return false;
+
+ bPromiscuous = (pThis->u.s.WinIf.fUpperProtocolSetFilter & NDIS_PACKET_TYPE_PROMISCUOUS) == NDIS_PACKET_TYPE_PROMISCUOUS;
+ /*vboxNetFltWinIsPromiscuous(pAdapt);*/
+
+ vboxNetFltWinDereferenceWinIf(pThis);
+ return bPromiscuous;
+ }
+ return false;
+#else
+ RT_NOREF1(pThis);
+ return true;
+#endif
+}
+
+
+/**
+ * Report the MAC address, promiscuous mode setting, GSO capabilities and
+ * no-preempt destinations to the internal network.
+ *
+ * Does nothing if we're not currently connected to an internal network.
+ *
+ * @param pThis The instance data.
+ */
+static void vboxNetFltWinReportStuff(PVBOXNETFLTINS pThis)
+{
+ /** @todo Keep these up to date, esp. the promiscuous mode bit. */
+ if (pThis->pSwitchPort
+ && vboxNetFltTryRetainBusyNotDisconnected(pThis))
+ {
+ pThis->pSwitchPort->pfnReportMacAddress(pThis->pSwitchPort, &pThis->u.s.MacAddr);
+ pThis->pSwitchPort->pfnReportPromiscuousMode(pThis->pSwitchPort,
+ vboxNetFltWinIsPromiscuous2(pThis));
+ pThis->pSwitchPort->pfnReportGsoCapabilities(pThis->pSwitchPort, 0,
+ INTNETTRUNKDIR_WIRE | INTNETTRUNKDIR_HOST);
+ /** @todo We should be able to do pfnXmit at DISPATCH_LEVEL... */
+ pThis->pSwitchPort->pfnReportNoPreemptDsts(pThis->pSwitchPort, 0 /* none */);
+ vboxNetFltRelease(pThis, true /*fBusy*/);
+ }
+}
+
+/**
+ * Worker for vboxNetFltWinAttachToInterface.
+ *
+ * @param pAttachInfo Structure for communicating with
+ * vboxNetFltWinAttachToInterface.
+ */
+static void vboxNetFltWinAttachToInterfaceWorker(PATTACH_INFO pAttachInfo)
+{
+ PVBOXNETFLTINS pThis = pAttachInfo->pNetFltIf;
+ NDIS_STATUS Status = NDIS_STATUS_SUCCESS;
+ int rc;
+
+ Assert(KeGetCurrentIrql() == PASSIVE_LEVEL);
+
+ /* to ensure we're not removed while we're here */
+ vboxNetFltRetain(pThis, false);
+
+ rc = RTSemMutexRequest(pThis->u.s.hWinIfMutex, RT_INDEFINITE_WAIT);
+ if (RT_SUCCESS(rc))
+ {
+ Assert(vboxNetFltWinGetWinIfState(pThis) == kVBoxWinIfState_Disconnected);
+ Assert(vboxNetFltWinGetOpState(&pThis->u.s.WinIf.MpState) == kVBoxNetDevOpState_Deinitialized);
+#ifndef VBOXNETADP
+ Assert(vboxNetFltWinGetOpState(&pThis->u.s.WinIf.PtState) == kVBoxNetDevOpState_Deinitialized);
+#endif
+ if (vboxNetFltWinGetWinIfState(pThis) == kVBoxWinIfState_Disconnected)
+ {
+ if (pAttachInfo->fRediscovery)
+ {
+ /* rediscovery means adaptor bind is performed while intnet is already using it
+ * i.e. adaptor was unbound while being used by intnet and now being bound back again */
+ Assert( ((VBOXNETFTLINSSTATE)ASMAtomicUoReadU32((uint32_t volatile *)&pThis->enmState))
+ == kVBoxNetFltInsState_Connected);
+ }
+#ifndef VBOXNETADP
+ Status = vboxNetFltWinPtInitWinIf(&pThis->u.s.WinIf, pAttachInfo->pCreateContext->pOurName);
+#else
+ Status = vboxNetFltWinPtInitWinIf(&pThis->u.s.WinIf);
+#endif
+ if (Status == NDIS_STATUS_SUCCESS)
+ {
+ vboxNetFltWinSetWinIfState(pThis, kVBoxWinIfState_Connecting);
+
+#ifndef VBOXNETADP
+ Status = vboxNetFltWinPtDoBinding(pThis, pAttachInfo->pCreateContext->pOurName, pAttachInfo->pCreateContext->pBindToName);
+#else
+ Status = vboxNetFltWinMpDoInitialization(pThis, pAttachInfo->pCreateContext->hMiniportAdapter, pAttachInfo->pCreateContext->hWrapperConfigurationContext);
+#endif
+ if (Status == NDIS_STATUS_SUCCESS)
+ {
+ if (!pAttachInfo->fRediscovery)
+ vboxNetFltWinDrvReference();
+#ifndef VBOXNETADP
+ if (pThis->u.s.WinIf.OpenCloseStatus == NDIS_STATUS_SUCCESS)
+#endif
+ {
+ vboxNetFltWinSetWinIfState(pThis, kVBoxWinIfState_Connected);
+#ifndef VBOXNETADP
+ Assert(vboxNetFltWinGetOpState(&pThis->u.s.WinIf.PtState) == kVBoxNetDevOpState_Initialized);
+#endif
+ /* 4. mark as connected */
+ RTSpinlockAcquire(pThis->hSpinlock);
+ ASMAtomicUoWriteBool(&pThis->fDisconnectedFromHost, false);
+ RTSpinlockRelease(pThis->hSpinlock);
+
+ pAttachInfo->Status = VINF_SUCCESS;
+ pAttachInfo->pCreateContext->Status = NDIS_STATUS_SUCCESS;
+
+ RTSemMutexRelease(pThis->u.s.hWinIfMutex);
+
+ vboxNetFltRelease(pThis, false);
+
+ /* 5. Report MAC address, promiscuousness and GSO capabilities. */
+ vboxNetFltWinReportStuff(pThis);
+
+ return;
+ }
+#ifndef VBOXNETADP /* unreachable for VBOXNETADP because of the return above */
+ AssertBreakpoint();
+
+ if (!pAttachInfo->fRediscovery)
+ {
+ vboxNetFltWinDrvDereference();
+ }
+# ifndef VBOXNETADP
+ vboxNetFltWinPtDoUnbinding(pThis, true);
+/*# else - unreachable
+ vboxNetFltWinMpDoDeinitialization(pThis); */
+# endif
+#endif
+ }
+ AssertBreakpoint();
+ vboxNetFltWinPtFiniWinIf(&pThis->u.s.WinIf);
+ }
+ AssertBreakpoint();
+ vboxNetFltWinSetWinIfState(pThis, kVBoxWinIfState_Disconnected);
+ Assert(vboxNetFltWinGetOpState(&pThis->u.s.WinIf.MpState) == kVBoxNetDevOpState_Deinitialized);
+#ifndef VBOXNETADP
+ Assert(vboxNetFltWinGetOpState(&pThis->u.s.WinIf.PtState) == kVBoxNetDevOpState_Deinitialized);
+#endif
+ }
+ AssertBreakpoint();
+
+ pAttachInfo->Status = VERR_GENERAL_FAILURE;
+ pAttachInfo->pCreateContext->Status = Status;
+ RTSemMutexRelease(pThis->u.s.hWinIfMutex);
+ }
+ else
+ {
+ AssertBreakpoint();
+ pAttachInfo->Status = rc;
+ }
+
+ vboxNetFltRelease(pThis, false);
+
+ return;
+}
+
+/**
+ * Common code for vboxNetFltOsInitInstance and
+ * vboxNetFltOsMaybeRediscovered.
+ *
+ * @returns IPRT status code.
+ * @param pThis The instance.
+ * @param fRediscovery True if vboxNetFltOsMaybeRediscovered is calling,
+ * false if it's vboxNetFltOsInitInstance.
+ */
+static int vboxNetFltWinAttachToInterface(PVBOXNETFLTINS pThis, void * pContext, bool fRediscovery)
+{
+ ATTACH_INFO Info;
+ Info.pNetFltIf = pThis;
+ Info.fRediscovery = fRediscovery;
+ Info.pCreateContext = (PCREATE_INSTANCE_CONTEXT)pContext;
+
+ vboxNetFltWinAttachToInterfaceWorker(&Info);
+
+ return Info.Status;
+}
+static NTSTATUS vboxNetFltWinPtDevDispatch(IN PDEVICE_OBJECT pDevObj, IN PIRP pIrp)
+{
+ RT_NOREF1(pDevObj);
+ PIO_STACK_LOCATION pIrpSl = IoGetCurrentIrpStackLocation(pIrp);;
+ NTSTATUS Status = STATUS_SUCCESS;
+
+ switch (pIrpSl->MajorFunction)
+ {
+ case IRP_MJ_DEVICE_CONTROL:
+ Status = STATUS_NOT_SUPPORTED;
+ break;
+ case IRP_MJ_CREATE:
+ case IRP_MJ_CLEANUP:
+ case IRP_MJ_CLOSE:
+ break;
+ default:
+ AssertFailed();
+ break;
+ }
+
+ pIrp->IoStatus.Status = Status;
+ IoCompleteRequest(pIrp, IO_NO_INCREMENT);
+
+ return Status;
+}
+
+static NDIS_STATUS vboxNetFltWinDevCreate(PVBOXNETFLTGLOBALS_WIN pGlobals)
+{
+ NDIS_STRING DevName, LinkName;
+ PDRIVER_DISPATCH aMajorFunctions[IRP_MJ_MAXIMUM_FUNCTION+1];
+ NdisInitUnicodeString(&DevName, VBOXNETFLT_NAME_DEVICE);
+ NdisInitUnicodeString(&LinkName, VBOXNETFLT_NAME_LINK);
+
+ Assert(!pGlobals->hDevice);
+ Assert(!pGlobals->pDevObj);
+ NdisZeroMemory(aMajorFunctions, sizeof (aMajorFunctions));
+ aMajorFunctions[IRP_MJ_CREATE] = vboxNetFltWinPtDevDispatch;
+ aMajorFunctions[IRP_MJ_CLEANUP] = vboxNetFltWinPtDevDispatch;
+ aMajorFunctions[IRP_MJ_CLOSE] = vboxNetFltWinPtDevDispatch;
+ aMajorFunctions[IRP_MJ_DEVICE_CONTROL] = vboxNetFltWinPtDevDispatch;
+
+ NDIS_STATUS Status = NdisMRegisterDevice(pGlobals->Mp.hNdisWrapper,
+ &DevName, &LinkName,
+ aMajorFunctions,
+ &pGlobals->pDevObj,
+ &pGlobals->hDevice);
+ Assert(Status == NDIS_STATUS_SUCCESS);
+ return Status;
+}
+
+static NDIS_STATUS vboxNetFltWinDevDestroy(PVBOXNETFLTGLOBALS_WIN pGlobals)
+{
+ Assert(pGlobals->hDevice);
+ Assert(pGlobals->pDevObj);
+ NDIS_STATUS Status = NdisMDeregisterDevice(pGlobals->hDevice);
+ Assert(Status == NDIS_STATUS_SUCCESS);
+ if (Status == NDIS_STATUS_SUCCESS)
+ {
+ pGlobals->hDevice = NULL;
+ pGlobals->pDevObj = NULL;
+ }
+ return Status;
+}
+
+static NDIS_STATUS vboxNetFltWinDevCreateReference(PVBOXNETFLTGLOBALS_WIN pGlobals)
+{
+ Assert(KeGetCurrentIrql() == PASSIVE_LEVEL);
+ NDIS_STATUS Status = KeWaitForSingleObject(&pGlobals->SynchEvent, Executive, KernelMode, FALSE, NULL);
+ Assert(Status == STATUS_SUCCESS);
+ if (Status == STATUS_SUCCESS)
+ {
+ Assert(pGlobals->cDeviceRefs >= 0);
+ if (++pGlobals->cDeviceRefs == 1)
+ {
+ Status = vboxNetFltWinDevCreate(pGlobals);
+ if (Status == NDIS_STATUS_SUCCESS)
+ {
+ ObReferenceObject(pGlobals->pDevObj);
+ }
+ }
+ else
+ {
+ Status = NDIS_STATUS_SUCCESS;
+ }
+ KeSetEvent(&pGlobals->SynchEvent, 0, FALSE);
+ }
+ else
+ {
+ /* should never happen actually */
+ AssertFailed();
+ Status = NDIS_STATUS_FAILURE;
+ }
+ return Status;
+}
+
+static NDIS_STATUS vboxNetFltWinDevDereference(PVBOXNETFLTGLOBALS_WIN pGlobals)
+{
+ Assert(KeGetCurrentIrql() == PASSIVE_LEVEL);
+ NDIS_STATUS Status = KeWaitForSingleObject(&pGlobals->SynchEvent, Executive, KernelMode, FALSE, NULL);
+ Assert(Status == STATUS_SUCCESS);
+ if (Status == STATUS_SUCCESS)
+ {
+ Assert(pGlobals->cDeviceRefs > 0);
+ if (!(--pGlobals->cDeviceRefs))
+ {
+ ObDereferenceObject(pGlobals->pDevObj);
+ Status = vboxNetFltWinDevDestroy(pGlobals);
+ }
+ else
+ {
+ Status = NDIS_STATUS_SUCCESS;
+ }
+ KeSetEvent(&pGlobals->SynchEvent, 0, FALSE);
+ }
+ else
+ {
+ /* should never happen actually */
+ AssertFailed();
+ Status = NDIS_STATUS_FAILURE;
+ }
+ return Status;
+}
+
+/* reference the driver module to prevent driver unload */
+DECLHIDDEN(void) vboxNetFltWinDrvReference()
+{
+ vboxNetFltWinDevCreateReference(&g_VBoxNetFltGlobalsWin);
+}
+
+/* dereference the driver module to prevent driver unload */
+DECLHIDDEN(void) vboxNetFltWinDrvDereference()
+{
+ vboxNetFltWinDevDereference(&g_VBoxNetFltGlobalsWin);
+}
+
+/*
+ *
+ * The OS specific interface definition
+ *
+ */
+
+
+bool vboxNetFltOsMaybeRediscovered(PVBOXNETFLTINS pThis)
+{
+ /* AttachToInterface true if disconnected */
+ return !ASMAtomicUoReadBool(&pThis->fDisconnectedFromHost);
+}
+
+int vboxNetFltPortOsXmit(PVBOXNETFLTINS pThis, void *pvIfData, PINTNETSG pSG, uint32_t fDst)
+{
+ RT_NOREF1(pvIfData);
+ int rc = VINF_SUCCESS;
+ uint32_t cRefs = 0;
+#ifndef VBOXNETADP
+ if (fDst & INTNETTRUNKDIR_WIRE)
+ cRefs++;
+ if (fDst & INTNETTRUNKDIR_HOST)
+ cRefs++;
+#else
+ if ((fDst & INTNETTRUNKDIR_WIRE) || (fDst & INTNETTRUNKDIR_HOST))
+ cRefs = 1;
+#endif
+
+ AssertReturn(cRefs, VINF_SUCCESS);
+
+ if (!vboxNetFltWinIncReferenceWinIf(pThis, cRefs))
+ {
+ return VERR_GENERAL_FAILURE;
+ }
+#ifndef VBOXNETADP
+ if (fDst & INTNETTRUNKDIR_WIRE)
+ {
+ PNDIS_PACKET pPacket;
+
+ pPacket = vboxNetFltWinNdisPacketFromSG(pThis, pSG, NULL /*pBufToFree*/,
+ true /*fToWire*/, true /*fCopyMemory*/);
+
+ if (pPacket)
+ {
+ NDIS_STATUS fStatus;
+
+#ifndef VBOX_LOOPBACK_USEFLAGS
+ /* force "don't loopback" flags to prevent loopback branch invocation in any case
+ * to avoid ndis misbehave */
+ NdisGetPacketFlags(pPacket) |= g_VBoxNetFltGlobalsWin.fPacketDontLoopBack;
+#else
+ /* this is done by default in vboxNetFltWinNdisPacketFromSG */
+#endif
+
+#if defined(DEBUG_NETFLT_PACKETS) || !defined(VBOX_LOOPBACK_USEFLAGS)
+ vboxNetFltWinLbPutSendPacket(pThis, pPacket, true /* bFromIntNet */);
+#endif
+ NdisSend(&fStatus, pThis->u.s.WinIf.hBinding, pPacket);
+ if (fStatus != NDIS_STATUS_PENDING)
+ {
+#if defined(DEBUG_NETFLT_PACKETS) || !defined(VBOX_LOOPBACK_USEFLAGS)
+ /* the status is NOT pending, complete the packet */
+ bool fTmp = vboxNetFltWinLbRemoveSendPacket(pThis, pPacket);
+ Assert(fTmp); NOREF(fTmp);
+#endif
+ if (!NT_SUCCESS(fStatus))
+ rc = VERR_GENERAL_FAILURE; /** @todo convert status to VERR_xxx */
+
+ vboxNetFltWinFreeSGNdisPacket(pPacket, true);
+ }
+ else
+ {
+ /* pending, dereference on packet complete */
+ cRefs--;
+ }
+ }
+ else
+ {
+ AssertFailed();
+ rc = VERR_NO_MEMORY;
+ }
+ }
+#endif
+
+#ifndef VBOXNETADP
+ if (fDst & INTNETTRUNKDIR_HOST)
+#else
+ if (cRefs)
+#endif
+ {
+ PNDIS_PACKET pPacket = vboxNetFltWinNdisPacketFromSG(pThis, pSG, NULL /*pBufToFree*/,
+ false /*fToWire*/, true /*fCopyMemory*/);
+ if (pPacket)
+ {
+ NdisMIndicateReceivePacket(pThis->u.s.WinIf.hMiniport, &pPacket, 1);
+ cRefs--;
+#ifdef VBOXNETADP
+ STATISTIC_INCREASE(pThis->u.s.WinIf.cRxSuccess);
+#endif
+ }
+ else
+ {
+ AssertFailed();
+#ifdef VBOXNETADP
+ STATISTIC_INCREASE(pThis->u.s.WinIf.cRxError);
+#endif
+ rc = VERR_NO_MEMORY;
+ }
+ }
+
+ Assert(cRefs <= 2);
+
+ if (cRefs)
+ {
+ vboxNetFltWinDecReferenceWinIf(pThis, cRefs);
+ }
+
+ return rc;
+}
+
+void vboxNetFltPortOsSetActive(PVBOXNETFLTINS pThis, bool fActive)
+{
+#ifndef VBOXNETADP
+ NDIS_STATUS Status;
+#endif
+ /* we first wait for all pending ops to complete
+ * this might include all packets queued for processing */
+ for (;;)
+ {
+ if (fActive)
+ {
+ if (!pThis->u.s.cModePassThruRefs)
+ {
+ break;
+ }
+ }
+ else
+ {
+ if (!pThis->u.s.cModeNetFltRefs)
+ {
+ break;
+ }
+ }
+ vboxNetFltWinSleep(2);
+ }
+
+ if (!vboxNetFltWinReferenceWinIf(pThis))
+ return;
+#ifndef VBOXNETADP
+
+ if (fActive)
+ {
+#ifdef DEBUG_misha
+ NDIS_PHYSICAL_MEDIUM PhMedium;
+ bool bPromiscSupported;
+
+ Status = vboxNetFltWinQueryPhysicalMedium(pThis, &PhMedium);
+ if (Status != NDIS_STATUS_SUCCESS)
+ {
+
+ LogRel(("vboxNetFltWinQueryPhysicalMedium failed, Status (0x%x), setting medium to NdisPhysicalMediumUnspecified\n", Status));
+ Assert(Status == NDIS_STATUS_NOT_SUPPORTED);
+ if (Status != NDIS_STATUS_NOT_SUPPORTED)
+ {
+ LogRel(("vboxNetFltWinQueryPhysicalMedium failed, Status (0x%x), setting medium to NdisPhysicalMediumUnspecified\n", Status));
+ }
+ PhMedium = NdisPhysicalMediumUnspecified;
+ }
+ else
+ {
+ LogRel(("(SUCCESS) vboxNetFltWinQueryPhysicalMedium SUCCESS\n"));
+ }
+
+ bPromiscSupported = (!(PhMedium == NdisPhysicalMediumWirelessWan
+ || PhMedium == NdisPhysicalMediumWirelessLan
+ || PhMedium == NdisPhysicalMediumNative802_11
+ || PhMedium == NdisPhysicalMediumBluetooth
+ /*|| PhMedium == NdisPhysicalMediumWiMax */
+ ));
+
+ Assert(bPromiscSupported == VBOXNETFLT_PROMISCUOUS_SUPPORTED(pThis));
+#endif
+ }
+
+ if (VBOXNETFLT_PROMISCUOUS_SUPPORTED(pThis))
+ {
+ Status = vboxNetFltWinSetPromiscuous(pThis, fActive);
+ if (Status != NDIS_STATUS_SUCCESS)
+ {
+ LogRel(("vboxNetFltWinSetPromiscuous failed, Status (0x%x), fActive (%d)\n", Status, fActive));
+ AssertFailed();
+ }
+ }
+#else
+# ifdef VBOXNETADP_REPORT_DISCONNECTED
+ if (fActive)
+ {
+ NdisMIndicateStatus(pThis->u.s.WinIf.hMiniport,
+ NDIS_STATUS_MEDIA_CONNECT,
+ (PVOID)NULL,
+ 0);
+ }
+ else
+ {
+ NdisMIndicateStatus(pThis->u.s.WinIf.hMiniport,
+ NDIS_STATUS_MEDIA_DISCONNECT,
+ (PVOID)NULL,
+ 0);
+ }
+#else
+ if (fActive)
+ {
+ /* indicate status change to make the ip settings be re-picked for dhcp */
+ NdisMIndicateStatus(pThis->u.s.WinIf.hMiniport,
+ NDIS_STATUS_MEDIA_DISCONNECT,
+ (PVOID)NULL,
+ 0);
+
+ NdisMIndicateStatus(pThis->u.s.WinIf.hMiniport,
+ NDIS_STATUS_MEDIA_CONNECT,
+ (PVOID)NULL,
+ 0);
+ }
+# endif
+#endif
+ vboxNetFltWinDereferenceWinIf(pThis);
+
+ return;
+}
+
+#ifndef VBOXNETADP
+
+DECLINLINE(bool) vboxNetFltWinIsAddrLinkLocal4(PCRTNETADDRIPV4 pAddr)
+{
+ return (pAddr->s.Lo == 0xfea9); /* 169.254 */
+}
+
+DECLINLINE(bool) vboxNetFltWinIsAddrLinkLocal6(PCRTNETADDRIPV6 pAddr)
+{
+ return ((pAddr->au8[0] == 0xfe) && ((pAddr->au8[1] & 0xc0) == 0x80));
+}
+
+void vboxNetFltWinNotifyHostAddress(PTA_ADDRESS pAddress, bool fAdded)
+{
+ void *pvAddr = NULL;
+ INTNETADDRTYPE enmAddrType = kIntNetAddrType_Invalid;
+
+ LogFlow(("==>vboxNetFltWinNotifyHostAddress: AddrType=%d %s\n",
+ pAddress->AddressType, fAdded ? "added" : "deleted"));
+ if (pAddress->AddressType == TDI_ADDRESS_TYPE_IP)
+ {
+ PTDI_ADDRESS_IP pTdiAddrIp = (PTDI_ADDRESS_IP)pAddress->Address;
+ /*
+ * Note that we do not get loopback addresses here. If we did we should
+ * have checked and ignored them too.
+ */
+ if (!vboxNetFltWinIsAddrLinkLocal4((PCRTNETADDRIPV4)(&pTdiAddrIp->in_addr)))
+ {
+ pvAddr = &pTdiAddrIp->in_addr;
+ enmAddrType = kIntNetAddrType_IPv4;
+ }
+ else
+ Log2(("vboxNetFltWinNotifyHostAddress: ignoring link-local address %RTnaipv4\n",
+ pTdiAddrIp->in_addr));
+ }
+ else if (pAddress->AddressType == TDI_ADDRESS_TYPE_IP6)
+ {
+ PTDI_ADDRESS_IP6 pTdiAddrIp6 = (PTDI_ADDRESS_IP6)pAddress->Address;
+ if (!vboxNetFltWinIsAddrLinkLocal6((PCRTNETADDRIPV6)(pTdiAddrIp6->sin6_addr)))
+ {
+ pvAddr = pTdiAddrIp6->sin6_addr;
+ enmAddrType = kIntNetAddrType_IPv6;
+ }
+ else
+ Log2(("vboxNetFltWinNotifyHostAddress: ignoring link-local address %RTnaipv6\n",
+ pTdiAddrIp6->sin6_addr));
+ }
+ else
+ {
+ Log2(("vboxNetFltWinNotifyHostAddress: ignoring irrelevant address type %d\n",
+ pAddress->AddressType));
+ LogFlow(("<==vboxNetFltWinNotifyHostAddress\n"));
+ return;
+ }
+ if (pvAddr)
+ {
+ NdisAcquireSpinLock(&g_VBoxNetFltGlobalsWin.lockFilters);
+ /* At this point the list must contain at least one element. */
+ PVBOXNETFLTINS pInstance = NULL;
+ PVBOXNETFLTWIN pFilter;
+ RTListForEach(&g_VBoxNetFltGlobalsWin.listFilters, pFilter, VBOXNETFLTWIN, node)
+ {
+ pInstance = RT_FROM_MEMBER(pFilter, VBOXNETFLTINS, u.s.WinIf);
+ if (vboxNetFltWinReferenceWinIf(pInstance))
+ {
+ if (pInstance->pSwitchPort && pInstance->pSwitchPort->pfnNotifyHostAddress)
+ break;
+ vboxNetFltWinDereferenceWinIf(pInstance);
+ }
+ else
+ Log2(("vboxNetFltWinNotifyHostAddress: failed to retain filter instance %p\n", pInstance));
+ pInstance = NULL;
+ }
+ NdisReleaseSpinLock(&g_VBoxNetFltGlobalsWin.lockFilters);
+ if (pInstance)
+ {
+ if (enmAddrType == kIntNetAddrType_IPv4)
+ Log2(("vboxNetFltWin%sAddressHandler: %RTnaipv4\n",
+ fAdded ? "Add" : "Del", *(PCRTNETADDRIPV4)pvAddr));
+ else
+ Log2(("vboxNetFltWin%sAddressHandler: %RTnaipv6\n",
+ fAdded ? "Add" : "Del", pvAddr));
+ pInstance->pSwitchPort->pfnNotifyHostAddress(pInstance->pSwitchPort, fAdded,
+ enmAddrType, pvAddr);
+ vboxNetFltWinDereferenceWinIf(pInstance);
+ }
+ else
+ Log2(("vboxNetFltWinNotifyHostAddress: no filters require notification\n"));
+ }
+ LogFlow(("<==vboxNetFltWinNotifyHostAddress\n"));
+}
+
+void vboxNetFltWinAddAddressHandler(PTA_ADDRESS Address,
+ PUNICODE_STRING DeviceName,
+ PTDI_PNP_CONTEXT Context)
+{
+ RT_NOREF2(DeviceName, Context);
+ vboxNetFltWinNotifyHostAddress(Address, true);
+}
+
+void vboxNetFltWinDelAddressHandler(PTA_ADDRESS Address,
+ PUNICODE_STRING DeviceName,
+ PTDI_PNP_CONTEXT Context)
+{
+ RT_NOREF2(DeviceName, Context);
+ vboxNetFltWinNotifyHostAddress(Address, false);
+}
+
+void vboxNetFltWinRegisterIpAddrNotifier(PVBOXNETFLTINS pThis)
+{
+ LogFlow(("==>vboxNetFltWinRegisterIpAddrNotifier: instance=%p pThis->pSwitchPort=%p pThis->pSwitchPort->pfnNotifyHostAddress=%p\n",
+ pThis, pThis->pSwitchPort, pThis->pSwitchPort ? pThis->pSwitchPort->pfnNotifyHostAddress : NULL));
+ if (pThis->pSwitchPort && pThis->pSwitchPort->pfnNotifyHostAddress)
+ {
+ NdisAcquireSpinLock(&g_VBoxNetFltGlobalsWin.lockFilters);
+ bool fRegisterHandlers = RTListIsEmpty(&g_VBoxNetFltGlobalsWin.listFilters);
+ RTListPrepend(&g_VBoxNetFltGlobalsWin.listFilters, &pThis->u.s.WinIf.node);
+ NdisReleaseSpinLock(&g_VBoxNetFltGlobalsWin.lockFilters);
+
+ if (fRegisterHandlers)
+ {
+ TDI_CLIENT_INTERFACE_INFO Info;
+ UNICODE_STRING ClientName = RTL_CONSTANT_STRING(L"VBoxNetFlt");
+ memset(&Info, 0, sizeof(Info));
+ Info.MajorTdiVersion = 2;
+ Info.MinorTdiVersion = 0;
+ Info.ClientName = &ClientName;
+ Info.AddAddressHandlerV2 = vboxNetFltWinAddAddressHandler;
+ Info.DelAddressHandlerV2 = vboxNetFltWinDelAddressHandler;
+ Assert(!g_VBoxNetFltGlobalsWin.hNotifier);
+ NTSTATUS Status = TdiRegisterPnPHandlers(&Info, sizeof(Info), &g_VBoxNetFltGlobalsWin.hNotifier);
+ Log2(("vboxNetFltWinRegisterIpAddrNotifier: TdiRegisterPnPHandlers returned %d\n", Status)); NOREF(Status);
+ }
+ else
+ Log2(("vboxNetFltWinRegisterIpAddrNotifier: already registed\n"));
+ }
+ else
+ Log2(("vboxNetFltWinRegisterIpAddrNotifier: this instance does not require notifications, ignoring...\n"));
+ LogFlow(("<==vboxNetFltWinRegisterIpAddrNotifier: notifier=%p\n", g_VBoxNetFltGlobalsWin.hNotifier));
+}
+
+void vboxNetFltWinUnregisterIpAddrNotifier(PVBOXNETFLTINS pThis)
+{
+ LogFlow(("==>vboxNetFltWinUnregisterIpAddrNotifier: notifier=%p\n", g_VBoxNetFltGlobalsWin.hNotifier));
+ if (pThis->pSwitchPort && pThis->pSwitchPort->pfnNotifyHostAddress)
+ {
+ NdisAcquireSpinLock(&g_VBoxNetFltGlobalsWin.lockFilters);
+ /* At this point the list must contain at least one element. */
+ Assert(!RTListIsEmpty(&g_VBoxNetFltGlobalsWin.listFilters));
+ RTListNodeRemove(&pThis->u.s.WinIf.node);
+ HANDLE hNotifier = NULL;
+ if (RTListIsEmpty(&g_VBoxNetFltGlobalsWin.listFilters))
+ {
+ /*
+ * The list has become empty, so we need to deregister handlers. We
+ * grab hNotifier and reset it while still holding the lock. This
+ * guaranties that we won't interfere with setting it in
+ * vboxNetFltWinRegisterIpAddrNotifier(). It is inconceivable that
+ * vboxNetFltWinUnregisterIpAddrNotifier() will be called for the
+ * same filter instance while it is still being processed by
+ * vboxNetFltWinRegisterIpAddrNotifier(). This would require trunk
+ * destruction in the middle of its creation. It is possible that
+ * vboxNetFltWinUnregisterIpAddrNotifier() is called for another
+ * filter instance, but in such case we won't even get here as the
+ * list won't be empty.
+ */
+ hNotifier = g_VBoxNetFltGlobalsWin.hNotifier;
+ g_VBoxNetFltGlobalsWin.hNotifier = NULL;
+ }
+ NdisReleaseSpinLock(&g_VBoxNetFltGlobalsWin.lockFilters);
+ if (hNotifier)
+ {
+ NTSTATUS Status = TdiDeregisterPnPHandlers(hNotifier);
+ Log2(("vboxNetFltWinUnregisterIpAddrNotifier: TdiDeregisterPnPHandlers(%p) returned %d\n",
+ hNotifier, Status)); NOREF(Status);
+ }
+ else
+ Log2(("vboxNetFltWinUnregisterIpAddrNotifier: filters remain, do not deregister handlers yet\n"));
+ }
+ else
+ Log2(("vboxNetFltWinUnregisterIpAddrNotifier: this instance did not require notifications, ignoring...\n"));
+ LogFlow(("<==vboxNetFltWinUnregisterIpAddrNotifier\n"));
+}
+#else /* VBOXNETADP */
+#define vboxNetFltWinRegisterIpAddrNotifier(x)
+#define vboxNetFltWinUnregisterIpAddrNotifier(x)
+#endif /* VBOXNETADP */
+
+int vboxNetFltOsDisconnectIt(PVBOXNETFLTINS pThis)
+{
+ NDIS_STATUS Status = vboxNetFltWinDisconnectIt(pThis);
+ Log2(("vboxNetFltOsDisconnectIt: pThis=%p pThis->pSwitchPort=%p pThis->pSwitchPort->pfnNotifyHostAddress=%p\n",
+ pThis, pThis->pSwitchPort, pThis->pSwitchPort ? pThis->pSwitchPort->pfnNotifyHostAddress : NULL));
+ vboxNetFltWinUnregisterIpAddrNotifier(pThis);
+ return Status == NDIS_STATUS_SUCCESS ? VINF_SUCCESS : VERR_GENERAL_FAILURE;
+}
+
+static void vboxNetFltWinConnectItWorker(PVOID pvContext)
+{
+ PWORKER_INFO pInfo = (PWORKER_INFO)pvContext;
+#if !defined(VBOXNETADP) || !defined(VBOXNETFLT_NO_PACKET_QUEUE)
+ NDIS_STATUS Status;
+#endif
+ PVBOXNETFLTINS pInstance = pInfo->pNetFltIf;
+
+ Assert(KeGetCurrentIrql() == PASSIVE_LEVEL);
+
+ /* this is not a rediscovery, initialize Mac cache */
+ if (vboxNetFltWinReferenceWinIf(pInstance))
+ {
+#ifndef VBOXNETADP
+ Status = vboxNetFltWinGetMacAddress(pInstance, &pInstance->u.s.MacAddr);
+ if (Status == NDIS_STATUS_SUCCESS)
+#endif
+ {
+#ifdef VBOXNETFLT_NO_PACKET_QUEUE
+ pInfo->Status = VINF_SUCCESS;
+#else
+ Status = vboxNetFltWinQuInitPacketQueue(pInstance);
+ if (Status == NDIS_STATUS_SUCCESS)
+ {
+ pInfo->Status = VINF_SUCCESS;
+ }
+ else
+ {
+ pInfo->Status = VERR_GENERAL_FAILURE;
+ }
+#endif
+ }
+#ifndef VBOXNETADP
+ else
+ {
+ pInfo->Status = VERR_INTNET_FLT_IF_FAILED;
+ }
+#endif
+
+ vboxNetFltWinDereferenceWinIf(pInstance);
+ }
+ else
+ {
+ pInfo->Status = VERR_INTNET_FLT_IF_NOT_FOUND;
+ }
+}
+
+static int vboxNetFltWinConnectIt(PVBOXNETFLTINS pThis)
+{
+ WORKER_INFO Info;
+ Info.pNetFltIf = pThis;
+
+ vboxNetFltWinJobSynchExecAtPassive(vboxNetFltWinConnectItWorker, &Info);
+
+ if (RT_SUCCESS(Info.Status))
+ vboxNetFltWinReportStuff(pThis);
+
+ return Info.Status;
+}
+
+int vboxNetFltOsConnectIt(PVBOXNETFLTINS pThis)
+{
+ Log2(("vboxNetFltOsConnectIt: pThis=%p pThis->pSwitchPort=%p pThis->pSwitchPort->pfnNotifyHostAddress=%p\n",
+ pThis, pThis->pSwitchPort, pThis->pSwitchPort ? pThis->pSwitchPort->pfnNotifyHostAddress : NULL));
+ vboxNetFltWinRegisterIpAddrNotifier(pThis);
+ return vboxNetFltWinConnectIt(pThis);
+}
+
+void vboxNetFltOsDeleteInstance(PVBOXNETFLTINS pThis)
+{
+ vboxNetFltWinDeleteInstance(pThis);
+}
+
+int vboxNetFltOsInitInstance(PVBOXNETFLTINS pThis, void *pvContext)
+{
+ int rc = RTSemMutexCreate(&pThis->u.s.hWinIfMutex);
+ if (RT_SUCCESS(rc))
+ {
+ rc = vboxNetFltWinAttachToInterface(pThis, pvContext, false /*fRediscovery*/ );
+ if (RT_SUCCESS(rc))
+ {
+ return rc;
+ }
+ RTSemMutexDestroy(pThis->u.s.hWinIfMutex);
+ }
+ return rc;
+}
+
+int vboxNetFltOsPreInitInstance(PVBOXNETFLTINS pThis)
+{
+ pThis->u.s.cModeNetFltRefs = 0;
+ pThis->u.s.cModePassThruRefs = 0;
+ vboxNetFltWinSetWinIfState(pThis, kVBoxWinIfState_Disconnected);
+ vboxNetFltWinSetOpState(&pThis->u.s.WinIf.MpState, kVBoxNetDevOpState_Deinitialized);
+#ifndef VBOXNETADP
+ vboxNetFltWinSetOpState(&pThis->u.s.WinIf.PtState, kVBoxNetDevOpState_Deinitialized);
+#endif
+ return VINF_SUCCESS;
+}
+
+void vboxNetFltPortOsNotifyMacAddress(PVBOXNETFLTINS pThis, void *pvIfData, PCRTMAC pMac)
+{
+ RT_NOREF3(pThis, pvIfData, pMac);
+}
+
+int vboxNetFltPortOsConnectInterface(PVBOXNETFLTINS pThis, void *pvIf, void **ppvIfData)
+{
+ /* Nothing to do */
+ RT_NOREF3(pThis, pvIf, ppvIfData);
+ return VINF_SUCCESS;
+}
+
+int vboxNetFltPortOsDisconnectInterface(PVBOXNETFLTINS pThis, void *pvIfData)
+{
+ /* Nothing to do */
+ RT_NOREF2(pThis, pvIfData);
+ return VINF_SUCCESS;
+}
diff --git a/src/VBox/HostDrivers/VBoxNetFlt/win/drv/VBoxNetFltRt-win.h b/src/VBox/HostDrivers/VBoxNetFlt/win/drv/VBoxNetFltRt-win.h
new file mode 100644
index 00000000..36235409
--- /dev/null
+++ b/src/VBox/HostDrivers/VBoxNetFlt/win/drv/VBoxNetFltRt-win.h
@@ -0,0 +1,972 @@
+/* $Id: VBoxNetFltRt-win.h $ */
+/** @file
+ * VBoxNetFltRt-win.h - Bridged Networking Driver, Windows Specific Code.
+ * NetFlt Runtime API
+ */
+/*
+ * Copyright (C) 2011-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+#ifndef VBOX_INCLUDED_SRC_VBoxNetFlt_win_drv_VBoxNetFltRt_win_h
+#define VBOX_INCLUDED_SRC_VBoxNetFlt_win_drv_VBoxNetFltRt_win_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+DECLHIDDEN(VOID) vboxNetFltWinUnload(IN PDRIVER_OBJECT DriverObject);
+
+#ifndef VBOXNETADP
+# if !defined(VBOX_LOOPBACK_USEFLAGS) || defined(DEBUG_NETFLT_PACKETS)
+DECLHIDDEN(bool) vboxNetFltWinMatchPackets(PNDIS_PACKET pPacket1, PNDIS_PACKET pPacket2, const INT cbMatch);
+DECLHIDDEN(bool) vboxNetFltWinMatchPacketAndSG(PNDIS_PACKET pPacket, PINTNETSG pSG, const INT cbMatch);
+# endif
+#endif
+
+/*************************
+ * packet queue API *
+ *************************/
+
+
+#define LIST_ENTRY_2_PACKET_INFO(pListEntry) \
+ ( (PVBOXNETFLT_PACKET_INFO)((uint8_t *)(pListEntry) - RT_UOFFSETOF(VBOXNETFLT_PACKET_INFO, ListEntry)) )
+
+#if !defined(VBOX_LOOPBACK_USEFLAGS) || defined(DEBUG_NETFLT_PACKETS)
+
+#define VBOX_SLE_2_PKTRSVD_PT(_pEntry) \
+ ( (PVBOXNETFLT_PKTRSVD_PT)((uint8_t *)(_pEntry) - RT_UOFFSETOF(VBOXNETFLT_PKTRSVD_PT, ListEntry)) )
+
+#define VBOX_SLE_2_SENDPACKET(_pEntry) \
+ ( (PNDIS_PACKET)((uint8_t *)(VBOX_SLE_2_PKTRSVD_PT(_pEntry)) - RT_UOFFSETOF(NDIS_PACKET, ProtocolReserved)) )
+
+#endif
+/**
+ * enqueus the packet info to the tail of the queue
+ */
+DECLINLINE(void) vboxNetFltWinQuEnqueueTail(PVBOXNETFLT_PACKET_QUEUE pQueue, PVBOXNETFLT_PACKET_INFO pPacketInfo)
+{
+ InsertTailList(pQueue, &pPacketInfo->ListEntry);
+}
+
+DECLINLINE(void) vboxNetFltWinQuEnqueueHead(PVBOXNETFLT_PACKET_QUEUE pQueue, PVBOXNETFLT_PACKET_INFO pPacketInfo)
+{
+ Assert(pPacketInfo->pPool);
+ InsertHeadList(pQueue, &pPacketInfo->ListEntry);
+}
+
+/**
+ * enqueus the packet info to the tail of the queue
+ */
+DECLINLINE(void) vboxNetFltWinQuInterlockedEnqueueTail(PVBOXNETFLT_INTERLOCKED_PACKET_QUEUE pQueue, PVBOXNETFLT_PACKET_INFO pPacketInfo)
+{
+ Assert(pPacketInfo->pPool);
+ NdisAcquireSpinLock(&pQueue->Lock);
+ vboxNetFltWinQuEnqueueTail(&pQueue->Queue, pPacketInfo);
+ NdisReleaseSpinLock(&pQueue->Lock);
+}
+
+DECLINLINE(void) vboxNetFltWinQuInterlockedEnqueueHead(PVBOXNETFLT_INTERLOCKED_PACKET_QUEUE pQueue, PVBOXNETFLT_PACKET_INFO pPacketInfo)
+{
+ NdisAcquireSpinLock(&pQueue->Lock);
+ vboxNetFltWinQuEnqueueHead(&pQueue->Queue, pPacketInfo);
+ NdisReleaseSpinLock(&pQueue->Lock);
+}
+
+/**
+ * dequeus the packet info from the head of the queue
+ */
+DECLINLINE(PVBOXNETFLT_PACKET_INFO) vboxNetFltWinQuDequeueHead(PVBOXNETFLT_PACKET_QUEUE pQueue)
+{
+ PLIST_ENTRY pListEntry = RemoveHeadList(pQueue);
+ if (pListEntry != pQueue)
+ {
+ PVBOXNETFLT_PACKET_INFO pInfo = LIST_ENTRY_2_PACKET_INFO(pListEntry);
+ Assert(pInfo->pPool);
+ return pInfo;
+ }
+ return NULL;
+}
+
+DECLINLINE(PVBOXNETFLT_PACKET_INFO) vboxNetFltWinQuDequeueTail(PVBOXNETFLT_PACKET_QUEUE pQueue)
+{
+ PLIST_ENTRY pListEntry = RemoveTailList(pQueue);
+ if (pListEntry != pQueue)
+ {
+ PVBOXNETFLT_PACKET_INFO pInfo = LIST_ENTRY_2_PACKET_INFO(pListEntry);
+ Assert(pInfo->pPool);
+ return pInfo;
+ }
+ return NULL;
+}
+
+DECLINLINE(PVBOXNETFLT_PACKET_INFO) vboxNetFltWinQuInterlockedDequeueHead(PVBOXNETFLT_INTERLOCKED_PACKET_QUEUE pInterlockedQueue)
+{
+ PVBOXNETFLT_PACKET_INFO pInfo;
+ NdisAcquireSpinLock(&pInterlockedQueue->Lock);
+ pInfo = vboxNetFltWinQuDequeueHead(&pInterlockedQueue->Queue);
+ NdisReleaseSpinLock(&pInterlockedQueue->Lock);
+ return pInfo;
+}
+
+DECLINLINE(PVBOXNETFLT_PACKET_INFO) vboxNetFltWinQuInterlockedDequeueTail(PVBOXNETFLT_INTERLOCKED_PACKET_QUEUE pInterlockedQueue)
+{
+ PVBOXNETFLT_PACKET_INFO pInfo;
+ NdisAcquireSpinLock(&pInterlockedQueue->Lock);
+ pInfo = vboxNetFltWinQuDequeueTail(&pInterlockedQueue->Queue);
+ NdisReleaseSpinLock(&pInterlockedQueue->Lock);
+ return pInfo;
+}
+
+DECLINLINE(void) vboxNetFltWinQuDequeue(PVBOXNETFLT_PACKET_INFO pInfo)
+{
+ RemoveEntryList(&pInfo->ListEntry);
+}
+
+DECLINLINE(void) vboxNetFltWinQuInterlockedDequeue(PVBOXNETFLT_INTERLOCKED_PACKET_QUEUE pInterlockedQueue, PVBOXNETFLT_PACKET_INFO pInfo)
+{
+ NdisAcquireSpinLock(&pInterlockedQueue->Lock);
+ vboxNetFltWinQuDequeue(pInfo);
+ NdisReleaseSpinLock(&pInterlockedQueue->Lock);
+}
+
+/**
+ * allocates the packet info from the pool
+ */
+DECLINLINE(PVBOXNETFLT_PACKET_INFO) vboxNetFltWinPpAllocPacketInfo(PVBOXNETFLT_PACKET_INFO_POOL pPool)
+{
+ return vboxNetFltWinQuInterlockedDequeueHead(&pPool->Queue);
+}
+
+/**
+ * returns the packet info to the pool
+ */
+DECLINLINE(void) vboxNetFltWinPpFreePacketInfo(PVBOXNETFLT_PACKET_INFO pInfo)
+{
+ PVBOXNETFLT_PACKET_INFO_POOL pPool = pInfo->pPool;
+ vboxNetFltWinQuInterlockedEnqueueHead(&pPool->Queue, pInfo);
+}
+
+/** initializes the packet queue */
+#define INIT_PACKET_QUEUE(_pQueue) InitializeListHead((_pQueue))
+
+/** initializes the packet queue */
+#define INIT_INTERLOCKED_PACKET_QUEUE(_pQueue) \
+ { \
+ INIT_PACKET_QUEUE(&(_pQueue)->Queue); \
+ NdisAllocateSpinLock(&(_pQueue)->Lock); \
+ }
+
+/** delete the packet queue */
+#define FINI_INTERLOCKED_PACKET_QUEUE(_pQueue) NdisFreeSpinLock(&(_pQueue)->Lock)
+
+/** returns the packet the packet info contains */
+#define GET_PACKET_FROM_INFO(_pPacketInfo) (ASMAtomicUoReadPtr((void * volatile *)&(_pPacketInfo)->pPacket))
+
+/** assignes the packet to the packet info */
+#define SET_PACKET_TO_INFO(_pPacketInfo, _pPacket) (ASMAtomicUoWritePtr(&(_pPacketInfo)->pPacket, (_pPacket)))
+
+/** returns the flags the packet info contains */
+#define GET_FLAGS_FROM_INFO(_pPacketInfo) (ASMAtomicUoReadU32((volatile uint32_t *)&(_pPacketInfo)->fFlags))
+
+/** sets flags to the packet info */
+#define SET_FLAGS_TO_INFO(_pPacketInfo, _fFlags) (ASMAtomicUoWriteU32((volatile uint32_t *)&(_pPacketInfo)->fFlags, (_fFlags)))
+
+#ifdef VBOXNETFLT_NO_PACKET_QUEUE
+DECLHIDDEN(bool) vboxNetFltWinPostIntnet(PVBOXNETFLTINS pInstance, PVOID pvPacket, const UINT fFlags);
+#else
+DECLHIDDEN(NDIS_STATUS) vboxNetFltWinQuEnqueuePacket(PVBOXNETFLTINS pInstance, PVOID pPacket, const UINT fPacketFlags);
+DECLHIDDEN(void) vboxNetFltWinQuFiniPacketQueue(PVBOXNETFLTINS pInstance);
+DECLHIDDEN(NTSTATUS) vboxNetFltWinQuInitPacketQueue(PVBOXNETFLTINS pInstance);
+#endif /* #ifndef VBOXNETFLT_NO_PACKET_QUEUE */
+
+
+#ifndef VBOXNETADP
+/**
+ * searches the list entry in a single-linked list
+ */
+DECLINLINE(bool) vboxNetFltWinSearchListEntry(PVBOXNETFLT_SINGLE_LIST pList, PSINGLE_LIST_ENTRY pEntry2Search, bool bRemove)
+{
+ PSINGLE_LIST_ENTRY pHead = &pList->Head;
+ PSINGLE_LIST_ENTRY pCur;
+ PSINGLE_LIST_ENTRY pPrev;
+ for (pCur = pHead->Next, pPrev = pHead; pCur; pPrev = pCur, pCur = pCur->Next)
+ {
+ if (pEntry2Search == pCur)
+ {
+ if (bRemove)
+ {
+ pPrev->Next = pCur->Next;
+ if (pCur == pList->pTail)
+ {
+ pList->pTail = pPrev;
+ }
+ }
+ return true;
+ }
+ }
+ return false;
+}
+
+#if !defined(VBOX_LOOPBACK_USEFLAGS) || defined(DEBUG_NETFLT_PACKETS)
+
+DECLINLINE(PNDIS_PACKET) vboxNetFltWinSearchPacket(PVBOXNETFLT_SINGLE_LIST pList, PNDIS_PACKET pPacket2Search, int cbMatch, bool bRemove)
+{
+ PSINGLE_LIST_ENTRY pHead = &pList->Head;
+ PSINGLE_LIST_ENTRY pCur;
+ PSINGLE_LIST_ENTRY pPrev;
+ PNDIS_PACKET pCurPacket;
+ for (pCur = pHead->Next, pPrev = pHead; pCur; pPrev = pCur, pCur = pCur->Next)
+ {
+ pCurPacket = VBOX_SLE_2_SENDPACKET(pCur);
+ if (pCurPacket == pPacket2Search || vboxNetFltWinMatchPackets(pPacket2Search, pCurPacket, cbMatch))
+ {
+ if (bRemove)
+ {
+ pPrev->Next = pCur->Next;
+ if (pCur == pList->pTail)
+ {
+ pList->pTail = pPrev;
+ }
+ }
+ return pCurPacket;
+ }
+ }
+ return NULL;
+}
+
+DECLINLINE(PNDIS_PACKET) vboxNetFltWinSearchPacketBySG(PVBOXNETFLT_SINGLE_LIST pList, PINTNETSG pSG, int cbMatch, bool bRemove)
+{
+ PSINGLE_LIST_ENTRY pHead = &pList->Head;
+ PSINGLE_LIST_ENTRY pCur;
+ PSINGLE_LIST_ENTRY pPrev;
+ PNDIS_PACKET pCurPacket;
+ for (pCur = pHead->Next, pPrev = pHead; pCur; pPrev = pCur, pCur = pCur->Next)
+ {
+ pCurPacket = VBOX_SLE_2_SENDPACKET(pCur);
+ if (vboxNetFltWinMatchPacketAndSG(pCurPacket, pSG, cbMatch))
+ {
+ if (bRemove)
+ {
+ pPrev->Next = pCur->Next;
+ if (pCur == pList->pTail)
+ {
+ pList->pTail = pPrev;
+ }
+ }
+ return pCurPacket;
+ }
+ }
+ return NULL;
+}
+
+#endif /* #if !defined(VBOX_LOOPBACK_USEFLAGS) || defined(DEBUG_NETFLT_PACKETS) */
+
+DECLINLINE(bool) vboxNetFltWinSListIsEmpty(PVBOXNETFLT_SINGLE_LIST pList)
+{
+ return !pList->Head.Next;
+}
+
+DECLINLINE(void) vboxNetFltWinPutTail(PVBOXNETFLT_SINGLE_LIST pList, PSINGLE_LIST_ENTRY pEntry)
+{
+ pList->pTail->Next = pEntry;
+ pList->pTail = pEntry;
+ pEntry->Next = NULL;
+}
+
+DECLINLINE(void) vboxNetFltWinPutHead(PVBOXNETFLT_SINGLE_LIST pList, PSINGLE_LIST_ENTRY pEntry)
+{
+ pEntry->Next = pList->Head.Next;
+ pList->Head.Next = pEntry;
+ if (!pEntry->Next)
+ pList->pTail = pEntry;
+}
+
+DECLINLINE(PSINGLE_LIST_ENTRY) vboxNetFltWinGetHead(PVBOXNETFLT_SINGLE_LIST pList)
+{
+ PSINGLE_LIST_ENTRY pEntry = pList->Head.Next;
+ if (pEntry && pEntry == pList->pTail)
+ {
+ pList->Head.Next = NULL;
+ pList->pTail = &pList->Head;
+ }
+ return pEntry;
+}
+
+DECLINLINE(bool) vboxNetFltWinInterlockedSearchListEntry(PVBOXNETFLT_INTERLOCKED_SINGLE_LIST pList, PSINGLE_LIST_ENTRY pEntry2Search, bool bRemove)
+{
+ bool bFound;
+ NdisAcquireSpinLock(&pList->Lock);
+ bFound = vboxNetFltWinSearchListEntry(&pList->List, pEntry2Search, bRemove);
+ NdisReleaseSpinLock(&pList->Lock);
+ return bFound;
+}
+
+#if !defined(VBOX_LOOPBACK_USEFLAGS) || defined(DEBUG_NETFLT_PACKETS)
+
+DECLINLINE(PNDIS_PACKET) vboxNetFltWinInterlockedSearchPacket(PVBOXNETFLT_INTERLOCKED_SINGLE_LIST pList, PNDIS_PACKET pPacket2Search, int cbMatch, bool bRemove)
+{
+ PNDIS_PACKET pFound;
+ NdisAcquireSpinLock(&pList->Lock);
+ pFound = vboxNetFltWinSearchPacket(&pList->List, pPacket2Search, cbMatch, bRemove);
+ NdisReleaseSpinLock(&pList->Lock);
+ return pFound;
+}
+
+DECLINLINE(PNDIS_PACKET) vboxNetFltWinInterlockedSearchPacketBySG(PVBOXNETFLT_INTERLOCKED_SINGLE_LIST pList, PINTNETSG pSG, int cbMatch, bool bRemove)
+{
+ PNDIS_PACKET pFound;
+ NdisAcquireSpinLock(&pList->Lock);
+ pFound = vboxNetFltWinSearchPacketBySG(&pList->List, pSG, cbMatch, bRemove);
+ NdisReleaseSpinLock(&pList->Lock);
+ return pFound;
+}
+#endif /* #if !defined(VBOX_LOOPBACK_USEFLAGS) || defined(DEBUG_NETFLT_PACKETS) */
+
+DECLINLINE(void) vboxNetFltWinInterlockedPutTail(PVBOXNETFLT_INTERLOCKED_SINGLE_LIST pList, PSINGLE_LIST_ENTRY pEntry)
+{
+ NdisAcquireSpinLock(&pList->Lock);
+ vboxNetFltWinPutTail(&pList->List, pEntry);
+ NdisReleaseSpinLock(&pList->Lock);
+}
+
+DECLINLINE(void) vboxNetFltWinInterlockedPutHead(PVBOXNETFLT_INTERLOCKED_SINGLE_LIST pList, PSINGLE_LIST_ENTRY pEntry)
+{
+ NdisAcquireSpinLock(&pList->Lock);
+ vboxNetFltWinPutHead(&pList->List, pEntry);
+ NdisReleaseSpinLock(&pList->Lock);
+}
+
+DECLINLINE(PSINGLE_LIST_ENTRY) vboxNetFltWinInterlockedGetHead(PVBOXNETFLT_INTERLOCKED_SINGLE_LIST pList)
+{
+ PSINGLE_LIST_ENTRY pEntry;
+ NdisAcquireSpinLock(&pList->Lock);
+ pEntry = vboxNetFltWinGetHead(&pList->List);
+ NdisReleaseSpinLock(&pList->Lock);
+ return pEntry;
+}
+
+# if defined(DEBUG_NETFLT_PACKETS) || !defined(VBOX_LOOPBACK_USEFLAGS)
+DECLINLINE(void) vboxNetFltWinLbPutSendPacket(PVBOXNETFLTINS pNetFlt, PNDIS_PACKET pPacket, bool bFromIntNet)
+{
+ PVBOXNETFLT_PKTRSVD_PT pSrv = (PVBOXNETFLT_PKTRSVD_PT)pPacket->ProtocolReserved;
+ pSrv->bFromIntNet = bFromIntNet;
+ vboxNetFltWinInterlockedPutHead(&pNetFlt->u.s.WinIf.SendPacketQueue, &pSrv->ListEntry);
+}
+
+DECLINLINE(bool) vboxNetFltWinLbIsFromIntNet(PNDIS_PACKET pPacket)
+{
+ PVBOXNETFLT_PKTRSVD_PT pSrv = (PVBOXNETFLT_PKTRSVD_PT)pPacket->ProtocolReserved;
+ return pSrv->bFromIntNet;
+}
+
+DECLINLINE(PNDIS_PACKET) vboxNetFltWinLbSearchLoopBack(PVBOXNETFLTINS pNetFlt, PNDIS_PACKET pPacket, bool bRemove)
+{
+ return vboxNetFltWinInterlockedSearchPacket(&pNetFlt->u.s.WinIf.SendPacketQueue, pPacket, VBOXNETFLT_PACKETMATCH_LENGTH, bRemove);
+}
+
+DECLINLINE(PNDIS_PACKET) vboxNetFltWinLbSearchLoopBackBySG(PVBOXNETFLTINS pNetFlt, PINTNETSG pSG, bool bRemove)
+{
+ return vboxNetFltWinInterlockedSearchPacketBySG(&pNetFlt->u.s.WinIf.SendPacketQueue, pSG, VBOXNETFLT_PACKETMATCH_LENGTH, bRemove);
+}
+
+DECLINLINE(bool) vboxNetFltWinLbRemoveSendPacket(PVBOXNETFLTINS pNetFlt, PNDIS_PACKET pPacket)
+{
+ PVBOXNETFLT_PKTRSVD_PT pSrv = (PVBOXNETFLT_PKTRSVD_PT)pPacket->ProtocolReserved;
+ bool bRet = vboxNetFltWinInterlockedSearchListEntry(&pNetFlt->u.s.WinIf.SendPacketQueue, &pSrv->ListEntry, true);
+#ifdef DEBUG_misha
+ Assert(bRet == (pNetFlt->enmTrunkState == INTNETTRUNKIFSTATE_ACTIVE));
+#endif
+ return bRet;
+}
+
+# endif
+
+#endif
+
+#ifdef DEBUG_misha
+DECLHIDDEN(bool) vboxNetFltWinCheckMACs(PNDIS_PACKET pPacket, PRTMAC pDst, PRTMAC pSrc);
+DECLHIDDEN(bool) vboxNetFltWinCheckMACsSG(PINTNETSG pSG, PRTMAC pDst, PRTMAC pSrc);
+extern RTMAC g_vboxNetFltWinVerifyMACBroadcast;
+extern RTMAC g_vboxNetFltWinVerifyMACGuest;
+
+# define VBOXNETFLT_LBVERIFY(_pnf, _p) \
+ do { \
+ Assert(!vboxNetFltWinCheckMACs(_p, NULL, &g_vboxNetFltWinVerifyMACGuest)); \
+ Assert(!vboxNetFltWinCheckMACs(_p, NULL, &(_pnf)->u.s.MacAddr)); \
+ } while (0)
+
+# define VBOXNETFLT_LBVERIFYSG(_pnf, _p) \
+ do { \
+ Assert(!vboxNetFltWinCheckMACsSG(_p, NULL, &g_vboxNetFltWinVerifyMACGuest)); \
+ Assert(!vboxNetFltWinCheckMACsSG(_p, NULL, &(_pnf)->u.s.MacAddr)); \
+ } while (0)
+
+#else
+# define VBOXNETFLT_LBVERIFY(_pnf, _p) do { } while (0)
+# define VBOXNETFLT_LBVERIFYSG(_pnf, _p) do { } while (0)
+#endif
+
+/** initializes the list */
+#define INIT_SINGLE_LIST(_pList) \
+ { \
+ (_pList)->Head.Next = NULL; \
+ (_pList)->pTail = &(_pList)->Head; \
+ }
+
+/** initializes the list */
+#define INIT_INTERLOCKED_SINGLE_LIST(_pList) \
+ do { \
+ INIT_SINGLE_LIST(&(_pList)->List); \
+ NdisAllocateSpinLock(&(_pList)->Lock); \
+ } while (0)
+
+/** delete the packet queue */
+#define FINI_INTERLOCKED_SINGLE_LIST(_pList) \
+ do { \
+ Assert(vboxNetFltWinSListIsEmpty(&(_pList)->List)); \
+ NdisFreeSpinLock(&(_pList)->Lock); \
+ } while (0)
+
+
+/**************************************************************************
+ * PVBOXNETFLTINS , WinIf reference/dereference (i.e. retain/release) API *
+ **************************************************************************/
+
+
+DECLHIDDEN(void) vboxNetFltWinWaitDereference(PVBOXNETFLT_WINIF_DEVICE pState);
+
+DECLINLINE(void) vboxNetFltWinReferenceModeNetFlt(PVBOXNETFLTINS pIns)
+{
+ ASMAtomicIncU32((volatile uint32_t *)&pIns->u.s.cModeNetFltRefs);
+}
+
+DECLINLINE(void) vboxNetFltWinReferenceModePassThru(PVBOXNETFLTINS pIns)
+{
+ ASMAtomicIncU32((volatile uint32_t *)&pIns->u.s.cModePassThruRefs);
+}
+
+DECLINLINE(void) vboxNetFltWinIncReferenceModeNetFlt(PVBOXNETFLTINS pIns, uint32_t v)
+{
+ ASMAtomicAddU32((volatile uint32_t *)&pIns->u.s.cModeNetFltRefs, v);
+}
+
+DECLINLINE(void) vboxNetFltWinIncReferenceModePassThru(PVBOXNETFLTINS pIns, uint32_t v)
+{
+ ASMAtomicAddU32((volatile uint32_t *)&pIns->u.s.cModePassThruRefs, v);
+}
+
+DECLINLINE(void) vboxNetFltWinDereferenceModeNetFlt(PVBOXNETFLTINS pIns)
+{
+ ASMAtomicDecU32((volatile uint32_t *)&pIns->u.s.cModeNetFltRefs);
+}
+
+DECLINLINE(void) vboxNetFltWinDereferenceModePassThru(PVBOXNETFLTINS pIns)
+{
+ ASMAtomicDecU32((volatile uint32_t *)&pIns->u.s.cModePassThruRefs);
+}
+
+DECLINLINE(void) vboxNetFltWinDecReferenceModeNetFlt(PVBOXNETFLTINS pIns, uint32_t v)
+{
+ Assert(v);
+ ASMAtomicAddU32((volatile uint32_t *)&pIns->u.s.cModeNetFltRefs, (uint32_t)(-((int32_t)v)));
+}
+
+DECLINLINE(void) vboxNetFltWinDecReferenceModePassThru(PVBOXNETFLTINS pIns, uint32_t v)
+{
+ Assert(v);
+ ASMAtomicAddU32((volatile uint32_t *)&pIns->u.s.cModePassThruRefs, (uint32_t)(-((int32_t)v)));
+}
+
+DECLINLINE(void) vboxNetFltWinSetPowerState(PVBOXNETFLT_WINIF_DEVICE pState, NDIS_DEVICE_POWER_STATE State)
+{
+ ASMAtomicUoWriteU32((volatile uint32_t *)&pState->PowerState, State);
+}
+
+DECLINLINE(NDIS_DEVICE_POWER_STATE) vboxNetFltWinGetPowerState(PVBOXNETFLT_WINIF_DEVICE pState)
+{
+ return (NDIS_DEVICE_POWER_STATE)ASMAtomicUoReadU32((volatile uint32_t *)&pState->PowerState);
+}
+
+DECLINLINE(void) vboxNetFltWinSetOpState(PVBOXNETFLT_WINIF_DEVICE pState, VBOXNETDEVOPSTATE State)
+{
+ ASMAtomicUoWriteU32((volatile uint32_t *)&pState->OpState, State);
+}
+
+DECLINLINE(VBOXNETDEVOPSTATE) vboxNetFltWinGetOpState(PVBOXNETFLT_WINIF_DEVICE pState)
+{
+ return (VBOXNETDEVOPSTATE)ASMAtomicUoReadU32((volatile uint32_t *)&pState->OpState);
+}
+
+DECLINLINE(bool) vboxNetFltWinDoReferenceDevice(PVBOXNETFLT_WINIF_DEVICE pState)
+{
+ if (vboxNetFltWinGetPowerState(pState) == NdisDeviceStateD0 && vboxNetFltWinGetOpState(pState) == kVBoxNetDevOpState_Initialized)
+ {
+ /** @todo r=bird: Since this is a volatile member, why don't you declare it as
+ * such and save yourself all the casting? */
+ ASMAtomicIncU32((uint32_t volatile *)&pState->cReferences);
+ return true;
+ }
+ return false;
+}
+
+#ifndef VBOXNETADP
+DECLINLINE(bool) vboxNetFltWinDoReferenceDevices(PVBOXNETFLT_WINIF_DEVICE pState1, PVBOXNETFLT_WINIF_DEVICE pState2)
+{
+ if (vboxNetFltWinGetPowerState(pState1) == NdisDeviceStateD0
+ && vboxNetFltWinGetOpState(pState1) == kVBoxNetDevOpState_Initialized
+ && vboxNetFltWinGetPowerState(pState2) == NdisDeviceStateD0
+ && vboxNetFltWinGetOpState(pState2) == kVBoxNetDevOpState_Initialized)
+ {
+ ASMAtomicIncU32((uint32_t volatile *)&pState1->cReferences);
+ ASMAtomicIncU32((uint32_t volatile *)&pState2->cReferences);
+ return true;
+ }
+ return false;
+}
+#endif
+
+DECLINLINE(void) vboxNetFltWinDereferenceDevice(PVBOXNETFLT_WINIF_DEVICE pState)
+{
+ ASMAtomicDecU32((uint32_t volatile *)&pState->cReferences);
+ /** @todo r=bird: Add comment explaining why these cannot hit 0 or why
+ * reference are counted */
+}
+
+#ifndef VBOXNETADP
+DECLINLINE(void) vboxNetFltWinDereferenceDevices(PVBOXNETFLT_WINIF_DEVICE pState1, PVBOXNETFLT_WINIF_DEVICE pState2)
+{
+ ASMAtomicDecU32((uint32_t volatile *)&pState1->cReferences);
+ ASMAtomicDecU32((uint32_t volatile *)&pState2->cReferences);
+}
+#endif
+
+DECLINLINE(void) vboxNetFltWinDecReferenceDevice(PVBOXNETFLT_WINIF_DEVICE pState, uint32_t v)
+{
+ Assert(v);
+ ASMAtomicAddU32((uint32_t volatile *)&pState->cReferences, (uint32_t)(-((int32_t)v)));
+}
+
+#ifndef VBOXNETADP
+DECLINLINE(void) vboxNetFltWinDecReferenceDevices(PVBOXNETFLT_WINIF_DEVICE pState1, PVBOXNETFLT_WINIF_DEVICE pState2, uint32_t v)
+{
+ ASMAtomicAddU32((uint32_t volatile *)&pState1->cReferences, (uint32_t)(-((int32_t)v)));
+ ASMAtomicAddU32((uint32_t volatile *)&pState2->cReferences, (uint32_t)(-((int32_t)v)));
+}
+#endif
+
+DECLINLINE(bool) vboxNetFltWinDoIncReferenceDevice(PVBOXNETFLT_WINIF_DEVICE pState, uint32_t v)
+{
+ Assert(v);
+ if (vboxNetFltWinGetPowerState(pState) == NdisDeviceStateD0 && vboxNetFltWinGetOpState(pState) == kVBoxNetDevOpState_Initialized)
+ {
+ ASMAtomicAddU32((uint32_t volatile *)&pState->cReferences, v);
+ return true;
+ }
+ return false;
+}
+
+#ifndef VBOXNETADP
+DECLINLINE(bool) vboxNetFltWinDoIncReferenceDevices(PVBOXNETFLT_WINIF_DEVICE pState1, PVBOXNETFLT_WINIF_DEVICE pState2, uint32_t v)
+{
+ if (vboxNetFltWinGetPowerState(pState1) == NdisDeviceStateD0
+ && vboxNetFltWinGetOpState(pState1) == kVBoxNetDevOpState_Initialized
+ && vboxNetFltWinGetPowerState(pState2) == NdisDeviceStateD0
+ && vboxNetFltWinGetOpState(pState2) == kVBoxNetDevOpState_Initialized)
+ {
+ ASMAtomicAddU32((uint32_t volatile *)&pState1->cReferences, v);
+ ASMAtomicAddU32((uint32_t volatile *)&pState2->cReferences, v);
+ return true;
+ }
+ return false;
+}
+#endif
+
+
+DECLINLINE(bool) vboxNetFltWinReferenceWinIfNetFlt(PVBOXNETFLTINS pNetFlt, bool * pbNetFltActive)
+{
+ RTSpinlockAcquire((pNetFlt)->hSpinlock);
+#ifndef VBOXNETADP
+ if (!vboxNetFltWinDoReferenceDevices(&pNetFlt->u.s.WinIf.MpState, &pNetFlt->u.s.WinIf.PtState))
+#else
+ if (!vboxNetFltWinDoReferenceDevice(&pNetFlt->u.s.WinIf.MpState))
+#endif
+ {
+ RTSpinlockRelease((pNetFlt)->hSpinlock);
+ *pbNetFltActive = false;
+ return false;
+ }
+
+ if (pNetFlt->enmTrunkState != INTNETTRUNKIFSTATE_ACTIVE)
+ {
+ vboxNetFltWinReferenceModePassThru(pNetFlt);
+ RTSpinlockRelease((pNetFlt)->hSpinlock);
+ *pbNetFltActive = false;
+ return true;
+ }
+
+ vboxNetFltRetain((pNetFlt), true /* fBusy */);
+ vboxNetFltWinReferenceModeNetFlt(pNetFlt);
+ RTSpinlockRelease((pNetFlt)->hSpinlock);
+
+ *pbNetFltActive = true;
+ return true;
+}
+
+DECLINLINE(bool) vboxNetFltWinIncReferenceWinIfNetFlt(PVBOXNETFLTINS pNetFlt, uint32_t v, bool *pbNetFltActive)
+{
+ uint32_t i;
+
+ Assert(v);
+ if (!v)
+ {
+ *pbNetFltActive = false;
+ return false;
+ }
+
+ RTSpinlockAcquire((pNetFlt)->hSpinlock);
+#ifndef VBOXNETADP
+ if (!vboxNetFltWinDoIncReferenceDevices(&pNetFlt->u.s.WinIf.MpState, &pNetFlt->u.s.WinIf.PtState, v))
+#else
+ if (!vboxNetFltWinDoIncReferenceDevice(&pNetFlt->u.s.WinIf.MpState, v))
+#endif
+ {
+ RTSpinlockRelease(pNetFlt->hSpinlock);
+ *pbNetFltActive = false;
+ return false;
+ }
+
+ if (pNetFlt->enmTrunkState != INTNETTRUNKIFSTATE_ACTIVE)
+ {
+ vboxNetFltWinIncReferenceModePassThru(pNetFlt, v);
+
+ RTSpinlockRelease((pNetFlt)->hSpinlock);
+ *pbNetFltActive = false;
+ return true;
+ }
+
+ vboxNetFltRetain(pNetFlt, true /* fBusy */);
+
+ vboxNetFltWinIncReferenceModeNetFlt(pNetFlt, v);
+
+ RTSpinlockRelease(pNetFlt->hSpinlock);
+
+ /* we have marked it as busy, so can do the res references outside the lock */
+ for (i = 0; i < v-1; i++)
+ {
+ vboxNetFltRetain(pNetFlt, true /* fBusy */);
+ }
+
+ *pbNetFltActive = true;
+
+ return true;
+}
+
+DECLINLINE(void) vboxNetFltWinDecReferenceNetFlt(PVBOXNETFLTINS pNetFlt, uint32_t n)
+{
+ uint32_t i;
+ for (i = 0; i < n; i++)
+ {
+ vboxNetFltRelease(pNetFlt, true);
+ }
+
+ vboxNetFltWinDecReferenceModeNetFlt(pNetFlt, n);
+}
+
+DECLINLINE(void) vboxNetFltWinDereferenceNetFlt(PVBOXNETFLTINS pNetFlt)
+{
+ vboxNetFltRelease(pNetFlt, true);
+
+ vboxNetFltWinDereferenceModeNetFlt(pNetFlt);
+}
+
+DECLINLINE(void) vboxNetFltWinDecReferenceWinIf(PVBOXNETFLTINS pNetFlt, uint32_t v)
+{
+#ifdef VBOXNETADP
+ vboxNetFltWinDecReferenceDevice(&pNetFlt->u.s.WinIf.MpState, v);
+#else
+ vboxNetFltWinDecReferenceDevices(&pNetFlt->u.s.WinIf.MpState, &pNetFlt->u.s.WinIf.PtState, v);
+#endif
+}
+
+DECLINLINE(void) vboxNetFltWinDereferenceWinIf(PVBOXNETFLTINS pNetFlt)
+{
+#ifdef VBOXNETADP
+ vboxNetFltWinDereferenceDevice(&pNetFlt->u.s.WinIf.MpState);
+#else
+ vboxNetFltWinDereferenceDevices(&pNetFlt->u.s.WinIf.MpState, &pNetFlt->u.s.WinIf.PtState);
+#endif
+}
+
+DECLINLINE(bool) vboxNetFltWinIncReferenceWinIf(PVBOXNETFLTINS pNetFlt, uint32_t v)
+{
+ Assert(v);
+ if (!v)
+ {
+ return false;
+ }
+
+ RTSpinlockAcquire(pNetFlt->hSpinlock);
+#ifdef VBOXNETADP
+ if (vboxNetFltWinDoIncReferenceDevice(&pNetFlt->u.s.WinIf.MpState, v))
+#else
+ if (vboxNetFltWinDoIncReferenceDevices(&pNetFlt->u.s.WinIf.MpState, &pNetFlt->u.s.WinIf.PtState, v))
+#endif
+ {
+ RTSpinlockRelease(pNetFlt->hSpinlock);
+ return true;
+ }
+
+ RTSpinlockRelease(pNetFlt->hSpinlock);
+ return false;
+}
+
+DECLINLINE(bool) vboxNetFltWinReferenceWinIf(PVBOXNETFLTINS pNetFlt)
+{
+ RTSpinlockAcquire(pNetFlt->hSpinlock);
+#ifdef VBOXNETADP
+ if (vboxNetFltWinDoReferenceDevice(&pNetFlt->u.s.WinIf.MpState))
+#else
+ if (vboxNetFltWinDoReferenceDevices(&pNetFlt->u.s.WinIf.MpState, &pNetFlt->u.s.WinIf.PtState))
+#endif
+ {
+ RTSpinlockRelease(pNetFlt->hSpinlock);
+ return true;
+ }
+
+ RTSpinlockRelease(pNetFlt->hSpinlock);
+ return false;
+}
+
+/***********************************************
+ * methods for accessing the network card info *
+ ***********************************************/
+
+DECLHIDDEN(NDIS_STATUS) vboxNetFltWinGetMacAddress(PVBOXNETFLTINS pNetFlt, PRTMAC pMac);
+DECLHIDDEN(bool) vboxNetFltWinIsPromiscuous(PVBOXNETFLTINS pNetFlt);
+DECLHIDDEN(NDIS_STATUS) vboxNetFltWinSetPromiscuous(PVBOXNETFLTINS pNetFlt, bool bYes);
+DECLHIDDEN(NDIS_STATUS) vboxNetFltWinQueryPhysicalMedium(PVBOXNETFLTINS pNetFlt, NDIS_PHYSICAL_MEDIUM * pMedium);
+
+/*********************
+ * mem alloc API *
+ *********************/
+
+DECLHIDDEN(NDIS_STATUS) vboxNetFltWinMemAlloc(PVOID* ppMemBuf, UINT cbLength);
+
+DECLHIDDEN(void) vboxNetFltWinMemFree(PVOID pMemBuf);
+
+/* convenience method used which allocates and initializes the PINTNETSG containing one
+ * segment referring the buffer of size cbBufSize
+ * the allocated PINTNETSG should be freed with the vboxNetFltWinMemFree.
+ *
+ * This is used when our ProtocolReceive callback is called and we have to return the indicated NDIS_PACKET
+ * on a callback exit. This is why we allocate the PINTNETSG and put the packet info there and enqueue it
+ * for the packet queue */
+DECLHIDDEN(NDIS_STATUS) vboxNetFltWinAllocSG(UINT cbBufSize, PINTNETSG *ppSG);
+
+/************************
+ * WinIf init/fini API *
+ ************************/
+#if defined(VBOXNETADP)
+DECLHIDDEN(NDIS_STATUS) vboxNetFltWinPtInitBind(PVBOXNETFLTINS *ppNetFlt, NDIS_HANDLE hMiniportAdapter, PNDIS_STRING pBindToMiniportName /* actually this is our miniport name*/, NDIS_HANDLE hWrapperConfigurationContext);
+DECLHIDDEN(NDIS_STATUS) vboxNetFltWinPtInitWinIf(PVBOXNETFLTWIN pWinIf);
+#else
+DECLHIDDEN(NDIS_STATUS) vboxNetFltWinPtInitBind(PVBOXNETFLTINS *ppNetFlt, PNDIS_STRING pOurMiniportName, PNDIS_STRING pBindToMiniportName);
+DECLHIDDEN(NDIS_STATUS) vboxNetFltWinPtInitWinIf(PVBOXNETFLTWIN pWinIf, PNDIS_STRING pOurDeviceName);
+#endif
+
+DECLHIDDEN(VOID) vboxNetFltWinPtFiniWinIf(PVBOXNETFLTWIN pWinIf);
+
+/************************************
+ * Execute Job at passive level API *
+ ************************************/
+
+typedef VOID (*PFNVBOXNETFLT_JOB_ROUTINE) (PVOID pContext);
+
+DECLHIDDEN(VOID) vboxNetFltWinJobSynchExecAtPassive(PFNVBOXNETFLT_JOB_ROUTINE pfnRoutine, PVOID pContext);
+
+/*******************************
+ * Ndis Packets processing API *
+ *******************************/
+DECLHIDDEN(PNDIS_PACKET) vboxNetFltWinNdisPacketFromSG(PVBOXNETFLTINS pNetFlt, PINTNETSG pSG, PVOID pBufToFree, bool bToWire, bool bCopyMemory);
+
+DECLHIDDEN(void) vboxNetFltWinFreeSGNdisPacket(PNDIS_PACKET pPacket, bool bFreeMem);
+
+#ifdef DEBUG_NETFLT_PACKETS
+#define DBG_CHECK_PACKETS(_p1, _p2) \
+ { \
+ bool _b = vboxNetFltWinMatchPackets(_p1, _p2, -1); \
+ Assert(_b); \
+ }
+
+#define DBG_CHECK_PACKET_AND_SG(_p, _sg) \
+ { \
+ bool _b = vboxNetFltWinMatchPacketAndSG(_p, _sg, -1); \
+ Assert(_b); \
+ }
+
+#define DBG_CHECK_SGS(_sg1, _sg2) \
+ { \
+ bool _b = vboxNetFltWinMatchSGs(_sg1, _sg2, -1); \
+ Assert(_b); \
+ }
+
+#else
+#define DBG_CHECK_PACKETS(_p1, _p2)
+#define DBG_CHECK_PACKET_AND_SG(_p, _sg)
+#define DBG_CHECK_SGS(_sg1, _sg2)
+#endif
+
+/**
+ * Ndis loops back broadcast packets posted to the wire by IntNet
+ * This routine is used in the mechanism of preventing this looping
+ *
+ * @param pAdapt
+ * @param pPacket
+ * @param bOnRecv true is we are receiving the packet from the wire
+ * false otherwise (i.e. the packet is from the host)
+ *
+ * @return true if the packet is a looped back one, false otherwise
+ */
+#ifdef VBOX_LOOPBACK_USEFLAGS
+DECLINLINE(bool) vboxNetFltWinIsLoopedBackPacket(PNDIS_PACKET pPacket)
+{
+ return (NdisGetPacketFlags(pPacket) & g_fPacketIsLoopedBack) == g_fPacketIsLoopedBack;
+}
+#endif
+
+/**************************************************************
+ * utility methods for ndis packet creation/initialization *
+ **************************************************************/
+
+#define VBOXNETFLT_OOB_INIT(_p) \
+ { \
+ NdisZeroMemory(NDIS_OOB_DATA_FROM_PACKET(_p), sizeof(NDIS_PACKET_OOB_DATA)); \
+ NDIS_SET_PACKET_HEADER_SIZE(_p, VBOXNETFLT_PACKET_ETHEADER_SIZE); \
+ }
+
+#ifndef VBOXNETADP
+
+DECLINLINE(NDIS_STATUS) vboxNetFltWinCopyPacketInfoOnRecv(PNDIS_PACKET pDstPacket, PNDIS_PACKET pSrcPacket, bool bForceStatusResources)
+{
+ NDIS_STATUS Status = bForceStatusResources ? NDIS_STATUS_RESOURCES : NDIS_GET_PACKET_STATUS(pSrcPacket);
+ NDIS_SET_PACKET_STATUS(pDstPacket, Status);
+
+ NDIS_PACKET_FIRST_NDIS_BUFFER(pDstPacket) = NDIS_PACKET_FIRST_NDIS_BUFFER(pSrcPacket);
+ NDIS_PACKET_LAST_NDIS_BUFFER(pDstPacket) = NDIS_PACKET_LAST_NDIS_BUFFER(pSrcPacket);
+
+ NdisGetPacketFlags(pDstPacket) = NdisGetPacketFlags(pSrcPacket);
+
+ NDIS_SET_ORIGINAL_PACKET(pDstPacket, NDIS_GET_ORIGINAL_PACKET(pSrcPacket));
+ NDIS_SET_PACKET_HEADER_SIZE(pDstPacket, NDIS_GET_PACKET_HEADER_SIZE(pSrcPacket));
+
+ return Status;
+}
+
+DECLINLINE(void) vboxNetFltWinCopyPacketInfoOnSend(PNDIS_PACKET pDstPacket, PNDIS_PACKET pSrcPacket)
+{
+ NDIS_PACKET_FIRST_NDIS_BUFFER(pDstPacket) = NDIS_PACKET_FIRST_NDIS_BUFFER(pSrcPacket);
+ NDIS_PACKET_LAST_NDIS_BUFFER(pDstPacket) = NDIS_PACKET_LAST_NDIS_BUFFER(pSrcPacket);
+
+ NdisGetPacketFlags(pDstPacket) = NdisGetPacketFlags(pSrcPacket);
+
+ NdisMoveMemory(NDIS_OOB_DATA_FROM_PACKET(pDstPacket),
+ NDIS_OOB_DATA_FROM_PACKET(pSrcPacket),
+ sizeof (NDIS_PACKET_OOB_DATA));
+
+ NdisIMCopySendPerPacketInfo(pDstPacket, pSrcPacket);
+
+ PVOID pMediaSpecificInfo = NULL;
+ UINT fMediaSpecificInfoSize = 0;
+
+ NDIS_GET_PACKET_MEDIA_SPECIFIC_INFO(pSrcPacket, &pMediaSpecificInfo, &fMediaSpecificInfoSize);
+
+ if (pMediaSpecificInfo || fMediaSpecificInfoSize)
+ {
+ NDIS_SET_PACKET_MEDIA_SPECIFIC_INFO(pDstPacket, pMediaSpecificInfo, fMediaSpecificInfoSize);
+ }
+}
+
+DECLHIDDEN(NDIS_STATUS) vboxNetFltWinPrepareSendPacket(PVBOXNETFLTINS pNetFlt, PNDIS_PACKET pPacket, PNDIS_PACKET *ppMyPacket);
+DECLHIDDEN(NDIS_STATUS) vboxNetFltWinPrepareRecvPacket(PVBOXNETFLTINS pNetFlt, PNDIS_PACKET pPacket, PNDIS_PACKET *ppMyPacket, bool bDpr);
+#endif
+
+DECLHIDDEN(void) vboxNetFltWinSleep(ULONG milis);
+
+#define MACS_EQUAL(_m1, _m2) \
+ ((_m1).au16[0] == (_m2).au16[0] \
+ && (_m1).au16[1] == (_m2).au16[1] \
+ && (_m1).au16[2] == (_m2).au16[2])
+
+
+DECLHIDDEN(NDIS_STATUS) vboxNetFltWinDetachFromInterface(PVBOXNETFLTINS pNetFlt, bool bOnUnbind);
+DECLHIDDEN(NDIS_STATUS) vboxNetFltWinCopyString(PNDIS_STRING pDst, PNDIS_STRING pSrc);
+
+
+/**
+ * Sets the enmState member atomically.
+ *
+ * Used for all updates.
+ *
+ * @param pThis The instance.
+ * @param enmNewState The new value.
+ */
+DECLINLINE(void) vboxNetFltWinSetWinIfState(PVBOXNETFLTINS pNetFlt, VBOXNETFLT_WINIFSTATE enmNewState)
+{
+ ASMAtomicWriteU32((uint32_t volatile *)&pNetFlt->u.s.WinIf.enmState, enmNewState);
+}
+
+/**
+ * Gets the enmState member atomically.
+ *
+ * Used for all reads.
+ *
+ * @returns The enmState value.
+ * @param pThis The instance.
+ */
+DECLINLINE(VBOXNETFLT_WINIFSTATE) vboxNetFltWinGetWinIfState(PVBOXNETFLTINS pNetFlt)
+{
+ return (VBOXNETFLT_WINIFSTATE)ASMAtomicUoReadU32((uint32_t volatile *)&pNetFlt->u.s.WinIf.enmState);
+}
+
+/* reference the driver module to prevent driver unload */
+DECLHIDDEN(void) vboxNetFltWinDrvReference();
+/* dereference the driver module to prevent driver unload */
+DECLHIDDEN(void) vboxNetFltWinDrvDereference();
+
+
+#ifndef VBOXNETADP
+# define VBOXNETFLT_PROMISCUOUS_SUPPORTED(_pNetFlt) (!(_pNetFlt)->fDisablePromiscuous)
+#else
+# define STATISTIC_INCREASE(_s) ASMAtomicIncU32((uint32_t volatile *)&(_s));
+
+DECLHIDDEN(void) vboxNetFltWinGenerateMACAddress(RTMAC *pMac);
+DECLHIDDEN(int) vboxNetFltWinMAC2NdisString(RTMAC *pMac, PNDIS_STRING pNdisString);
+DECLHIDDEN(int) vboxNetFltWinMACFromNdisString(RTMAC *pMac, PNDIS_STRING pNdisString);
+
+#endif
+#endif /* !VBOX_INCLUDED_SRC_VBoxNetFlt_win_drv_VBoxNetFltRt_win_h */
diff --git a/src/VBox/HostDrivers/VBoxNetFlt/win/drv/VBoxNetLwf-win.cpp b/src/VBox/HostDrivers/VBoxNetFlt/win/drv/VBoxNetLwf-win.cpp
new file mode 100644
index 00000000..6520e909
--- /dev/null
+++ b/src/VBox/HostDrivers/VBoxNetFlt/win/drv/VBoxNetLwf-win.cpp
@@ -0,0 +1,2736 @@
+/* $Id: VBoxNetLwf-win.cpp $ */
+/** @file
+ * VBoxNetLwf-win.cpp - NDIS6 Bridged Networking Driver, Windows-specific code.
+ */
+/*
+ * Copyright (C) 2014-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+#define LOG_GROUP LOG_GROUP_NET_FLT_DRV
+
+/*
+ * If VBOXNETLWF_SYNC_SEND is defined we won't allocate data buffers, but use
+ * the original buffers coming from IntNet to build MDLs around them. This
+ * also means that we need to wait for send operation to complete before
+ * returning the buffers, which hinders performance way too much.
+ */
+//#define VBOXNETLWF_SYNC_SEND
+
+/*
+ * If VBOXNETLWF_FIXED_SIZE_POOLS is defined we pre-allocate data buffers of
+ * fixed size in five pools. Each pool uses different size to accomodate packets
+ * of various sizes. We allocate these buffers once and re-use them when send
+ * operation is complete.
+ * If VBOXNETLWF_FIXED_SIZE_POOLS is not defined we allocate data buffers before
+ * each send operation and free then upon completion.
+ */
+#define VBOXNETLWF_FIXED_SIZE_POOLS
+
+/*
+ * Don't ask me why it is 42. Empirically this is what goes down the stack.
+ * OTOH, as we know from trustworthy sources, 42 is the answer, so be it.
+ */
+#define VBOXNETLWF_MAX_FRAME_SIZE(mtu) (mtu + 42)
+
+#include <VBox/version.h>
+#include <VBox/err.h>
+#include <iprt/initterm.h>
+#include <iprt/net.h>
+#include <iprt/list.h>
+#include <VBox/intnetinline.h>
+
+#include <iprt/nt/ntddk.h>
+#include <iprt/nt/ndis.h>
+#include <iprt/win/netioapi.h>
+#include <mstcpip.h>
+
+#define LogError(x) DbgPrint x
+
+#if 0
+#undef Log
+#define Log(x) DbgPrint x
+#undef LogFlow
+#define LogFlow(x) DbgPrint x
+#endif
+
+/** We have an entirely different structure than the one defined in VBoxNetFltCmn-win.h */
+typedef struct VBOXNETFLTWIN
+{
+ /** filter module context handle */
+ NDIS_HANDLE hModuleCtx;
+ /** IP address change notifier handle */
+ HANDLE hNotifier; /* Must be here as hModuleCtx may already be NULL when vboxNetFltOsDeleteInstance is called */
+} VBOXNETFLTWIN, *PVBOXNETFLTWIN;
+#define VBOXNETFLT_NO_PACKET_QUEUE
+#define VBOXNETFLT_OS_SPECFIC 1
+#include "VBoxNetFltInternal.h"
+
+#include "VBoxNetLwf-win.h"
+#include "VBox/VBoxNetCmn-win.h"
+
+typedef enum {
+ LwfState_Detached = 0,
+ LwfState_Attaching,
+ LwfState_Paused,
+ LwfState_Restarting,
+ LwfState_Running,
+ LwfState_Pausing,
+ LwfState_32BitHack = 0x7fffffff
+} VBOXNETLWFSTATE;
+
+/*
+ * Valid state transitions are:
+ * 1) Disconnected -> Connecting : start the worker thread, attempting to init IDC;
+ * 2) Connecting -> Disconnected : failed to start IDC init worker thread;
+ * 3) Connecting -> Connected : IDC init successful, terminate the worker;
+ * 4) Connecting -> Stopping : IDC init incomplete, but the driver is being unloaded, terminate the worker;
+ * 5) Connected -> Stopping : IDC init was successful, no worker, the driver is being unloaded;
+ *
+ * Driver terminates in Stopping state.
+ */
+typedef enum {
+ LwfIdcState_Disconnected = 0, /* Initial state */
+ LwfIdcState_Connecting, /* Attemping to init IDC, worker thread running */
+ LwfIdcState_Connected, /* Successfully connected to IDC, worker thread terminated */
+ LwfIdcState_Stopping /* Terminating the worker thread and disconnecting IDC */
+} VBOXNETLWFIDCSTATE;
+
+struct _VBOXNETLWF_MODULE;
+
+typedef struct VBOXNETLWFGLOBALS
+{
+ /** synch event used for device creation synchronization */
+ //KEVENT SynchEvent;
+ /** Device reference count */
+ //int cDeviceRefs;
+ /** ndis device */
+ NDIS_HANDLE hDevice;
+ /** device object */
+ PDEVICE_OBJECT pDevObj;
+ /** our filter driver handle */
+ NDIS_HANDLE hFilterDriver;
+ /** lock protecting the module list */
+ NDIS_SPIN_LOCK Lock;
+ /** the head of module list */
+ RTLISTANCHOR listModules;
+ /** IDC initialization state */
+ volatile uint32_t enmIdcState;
+ /** IDC init thread handle */
+ HANDLE hInitIdcThread;
+} VBOXNETLWFGLOBALS, *PVBOXNETLWFGLOBALS;
+
+/**
+ * The (common) global data.
+ */
+static VBOXNETFLTGLOBALS g_VBoxNetFltGlobals;
+/* win-specific global data */
+VBOXNETLWFGLOBALS g_VBoxNetLwfGlobals;
+
+#ifdef VBOXNETLWF_FIXED_SIZE_POOLS
+static ULONG g_cbPool[] = { 576+56, 1556, 4096+56, 6192+56, 9056 };
+#endif /* VBOXNETLWF_FIXED_SIZE_POOLS */
+
+typedef struct _VBOXNETLWF_MODULE {
+ RTLISTNODE node;
+
+ NDIS_HANDLE hFilter;
+#ifndef VBOXNETLWF_FIXED_SIZE_POOLS
+ NDIS_HANDLE hPool;
+#else /* VBOXNETLWF_FIXED_SIZE_POOLS */
+ NDIS_HANDLE hPool[RT_ELEMENTS(g_cbPool)];
+#endif /* VBOXNETLWF_FIXED_SIZE_POOLS */
+ PVBOXNETLWFGLOBALS pGlobals;
+ /** Associated instance of NetFlt, one-to-one relationship */
+ PVBOXNETFLTINS pNetFlt; /// @todo Consider automic access!
+ /** Module state as described in http://msdn.microsoft.com/en-us/library/windows/hardware/ff550017(v=vs.85).aspx */
+ volatile uint32_t enmState; /* No lock needed yet, atomic should suffice. */
+ /** Mutex to prevent pausing while transmitting on behalf of NetFlt */
+ NDIS_MUTEX InTransmit;
+#ifdef VBOXNETLWF_SYNC_SEND
+ /** Event signalled when sending to the wire is complete */
+ KEVENT EventWire;
+ /** Event signalled when NDIS returns our receive notification */
+ KEVENT EventHost;
+#else /* !VBOXNETLWF_SYNC_SEND */
+ /** Event signalled when all pending sends (both to wire and host) have completed */
+ NDIS_EVENT EventSendComplete;
+ /** Counter for pending sends (both to wire and host) */
+ int32_t cPendingBuffers;
+ /** Work Item to deliver offloading indications at passive IRQL */
+ NDIS_HANDLE hWorkItem;
+#endif /* !VBOXNETLWF_SYNC_SEND */
+ /** MAC address of underlying adapter */
+ RTMAC MacAddr;
+ /** Size of offload config structure */
+ USHORT cbOffloadConfig;
+ /** Saved offload configuration */
+ PNDIS_OFFLOAD pSavedOffloadConfig;
+ /** Temporary buffer for disabling offload configuration */
+ PNDIS_OFFLOAD pDisabledOffloadConfig;
+ /** the cloned request we have passed down */
+ PNDIS_OID_REQUEST pPendingRequest;
+ /** true if the underlying miniport supplied offloading config */
+ bool fOffloadConfigValid;
+ /** true if the trunk expects data from us */
+ bool fActive;
+ /** true if the host wants the adapter to be in promisc mode */
+ bool fHostPromisc;
+ /** true if the user wants packets being sent or received by VMs to be visible to the host in promisc mode */
+ bool fPassVmTrafficToHost;
+ /** Name of underlying adapter */
+ char szMiniportName[1];
+} VBOXNETLWF_MODULE;
+typedef VBOXNETLWF_MODULE *PVBOXNETLWF_MODULE;
+
+/*
+ * A structure to wrap OID requests in.
+ */
+typedef struct _VBOXNETLWF_OIDREQ {
+ NDIS_OID_REQUEST Request;
+ NDIS_STATUS Status;
+ NDIS_EVENT Event;
+} VBOXNETLWF_OIDREQ;
+typedef VBOXNETLWF_OIDREQ *PVBOXNETLWF_OIDREQ;
+
+
+/*********************************************************************************************************************************
+* Internal Functions *
+*********************************************************************************************************************************/
+static FILTER_ATTACH vboxNetLwfWinAttach;
+static FILTER_DETACH vboxNetLwfWinDetach;
+static FILTER_RESTART vboxNetLwfWinRestart;
+static FILTER_PAUSE vboxNetLwfWinPause;
+static FILTER_OID_REQUEST vboxNetLwfWinOidRequest;
+static FILTER_OID_REQUEST_COMPLETE vboxNetLwfWinOidRequestComplete;
+//static FILTER_CANCEL_OID_REQUEST vboxNetLwfWinCancelOidRequest;
+static FILTER_STATUS vboxNetLwfWinStatus;
+//static FILTER_NET_PNP_EVENT vboxNetLwfWinPnPEvent;
+static FILTER_SEND_NET_BUFFER_LISTS vboxNetLwfWinSendNetBufferLists;
+static FILTER_SEND_NET_BUFFER_LISTS_COMPLETE vboxNetLwfWinSendNetBufferListsComplete;
+static FILTER_RECEIVE_NET_BUFFER_LISTS vboxNetLwfWinReceiveNetBufferLists;
+static FILTER_RETURN_NET_BUFFER_LISTS vboxNetLwfWinReturnNetBufferLists;
+static KSTART_ROUTINE vboxNetLwfWinInitIdcWorker;
+
+static VOID vboxNetLwfWinUnloadDriver(IN PDRIVER_OBJECT pDriver);
+static int vboxNetLwfWinInitBase(void);
+static int vboxNetLwfWinFini(void);
+
+
+
+/**
+ * Logs an error to the system event log.
+ *
+ * @param ErrCode Error to report to event log.
+ * @param ReturnedStatus Error that was reported by the driver to the caller.
+ * @param uErrId Unique error id representing the location in the driver.
+ * @param cbDumpData Number of bytes at pDumpData.
+ * @param pDumpData Pointer to data that will be added to the message (see 'details' tab).
+ */
+static void vboxNetLwfLogErrorEvent(NTSTATUS uErrCode, NTSTATUS uReturnedStatus, ULONG uErrId)
+{
+ /* Figure out how many modules are attached and if they are going to fit into the dump data. */
+ unsigned cMaxModules = (ERROR_LOG_MAXIMUM_SIZE - FIELD_OFFSET(IO_ERROR_LOG_PACKET, DumpData)) / sizeof(RTMAC);
+ unsigned cModules = 0;
+ PVBOXNETLWF_MODULE pModuleCtx;
+ NdisAcquireSpinLock(&g_VBoxNetLwfGlobals.Lock);
+ RTListForEach(&g_VBoxNetLwfGlobals.listModules, pModuleCtx, VBOXNETLWF_MODULE, node)
+ ++cModules;
+ NdisReleaseSpinLock(&g_VBoxNetLwfGlobals.Lock);
+ /* Prevent overflow */
+ if (cModules > cMaxModules)
+ cModules = cMaxModules;
+
+ /* DumpDataSize must be a multiple of sizeof(ULONG). */
+ unsigned cbDumpData = (cModules * sizeof(RTMAC) + 3) & ~3;
+ /* Prevent underflow */
+ unsigned cbTotal = RT_MAX(FIELD_OFFSET(IO_ERROR_LOG_PACKET, DumpData) + cbDumpData,
+ sizeof(IO_ERROR_LOG_PACKET));
+
+ PIO_ERROR_LOG_PACKET pErrEntry;
+ pErrEntry = (PIO_ERROR_LOG_PACKET)IoAllocateErrorLogEntry(g_VBoxNetLwfGlobals.pDevObj,
+ (UCHAR)cbTotal);
+ if (pErrEntry)
+ {
+ PRTMAC pDump = (PRTMAC)pErrEntry->DumpData;
+ /*
+ * Initialize the whole structure with zeros in case we are suddenly short
+ * of data because the list is empty or has become smaller.
+ */
+ memset(pErrEntry, 0, cbTotal);
+
+ NdisAcquireSpinLock(&g_VBoxNetLwfGlobals.Lock);
+ RTListForEach(&g_VBoxNetLwfGlobals.listModules, pModuleCtx, VBOXNETLWF_MODULE, node)
+ {
+ /* The list could have been modified while we were allocating the entry, rely on cModules instead! */
+ if (cModules-- == 0)
+ break;
+ *pDump++ = pModuleCtx->MacAddr;
+ }
+ NdisReleaseSpinLock(&g_VBoxNetLwfGlobals.Lock);
+
+ pErrEntry->DumpDataSize = cbDumpData;
+ pErrEntry->ErrorCode = uErrCode;
+ pErrEntry->UniqueErrorValue = uErrId;
+ pErrEntry->FinalStatus = uReturnedStatus;
+ IoWriteErrorLogEntry(pErrEntry);
+ }
+ else
+ {
+ DbgPrint("Failed to allocate error log entry (cb=%u)\n", cbTotal);
+ }
+}
+
+#ifdef DEBUG
+
+static const char *vboxNetLwfWinStatusToText(NDIS_STATUS code)
+{
+ switch (code)
+ {
+ case NDIS_STATUS_MEDIA_CONNECT: return "NDIS_STATUS_MEDIA_CONNECT";
+ case NDIS_STATUS_MEDIA_DISCONNECT: return "NDIS_STATUS_MEDIA_DISCONNECT";
+ case NDIS_STATUS_RESET_START: return "NDIS_STATUS_RESET_START";
+ case NDIS_STATUS_RESET_END: return "NDIS_STATUS_RESET_END";
+ case NDIS_STATUS_MEDIA_BUSY: return "NDIS_STATUS_MEDIA_BUSY";
+ case NDIS_STATUS_MEDIA_SPECIFIC_INDICATION: return "NDIS_STATUS_MEDIA_SPECIFIC_INDICATION";
+ case NDIS_STATUS_LINK_SPEED_CHANGE: return "NDIS_STATUS_LINK_SPEED_CHANGE";
+ case NDIS_STATUS_LINK_STATE: return "NDIS_STATUS_LINK_STATE";
+ case NDIS_STATUS_PORT_STATE: return "NDIS_STATUS_PORT_STATE";
+ case NDIS_STATUS_OPER_STATUS: return "NDIS_STATUS_OPER_STATUS";
+ case NDIS_STATUS_NETWORK_CHANGE: return "NDIS_STATUS_NETWORK_CHANGE";
+ case NDIS_STATUS_PACKET_FILTER: return "NDIS_STATUS_PACKET_FILTER";
+ case NDIS_STATUS_TASK_OFFLOAD_CURRENT_CONFIG: return "NDIS_STATUS_TASK_OFFLOAD_CURRENT_CONFIG";
+ case NDIS_STATUS_TASK_OFFLOAD_HARDWARE_CAPABILITIES: return "NDIS_STATUS_TASK_OFFLOAD_HARDWARE_CAPABILITIES";
+ case NDIS_STATUS_OFFLOAD_ENCASPULATION_CHANGE: return "NDIS_STATUS_OFFLOAD_ENCASPULATION_CHANGE";
+ case NDIS_STATUS_TCP_CONNECTION_OFFLOAD_HARDWARE_CAPABILITIES: return "NDIS_STATUS_TCP_CONNECTION_OFFLOAD_HARDWARE_CAPABILITIES";
+ }
+ return "unknown";
+}
+
+static void vboxNetLwfWinDumpFilterTypes(ULONG uFlags)
+{
+ if (uFlags & NDIS_PACKET_TYPE_DIRECTED) Log5((" NDIS_PACKET_TYPE_DIRECTED\n"));
+ if (uFlags & NDIS_PACKET_TYPE_MULTICAST) Log5((" NDIS_PACKET_TYPE_MULTICAST\n"));
+ if (uFlags & NDIS_PACKET_TYPE_ALL_MULTICAST) Log5((" NDIS_PACKET_TYPE_ALL_MULTICAST\n"));
+ if (uFlags & NDIS_PACKET_TYPE_BROADCAST) Log5((" NDIS_PACKET_TYPE_BROADCAST\n"));
+ if (uFlags & NDIS_PACKET_TYPE_PROMISCUOUS) Log5((" NDIS_PACKET_TYPE_PROMISCUOUS\n"));
+ if (uFlags & NDIS_PACKET_TYPE_ALL_FUNCTIONAL) Log5((" NDIS_PACKET_TYPE_ALL_FUNCTIONAL\n"));
+ if (uFlags & NDIS_PACKET_TYPE_ALL_LOCAL) Log5((" NDIS_PACKET_TYPE_ALL_LOCAL\n"));
+ if (uFlags & NDIS_PACKET_TYPE_FUNCTIONAL) Log5((" NDIS_PACKET_TYPE_FUNCTIONAL\n"));
+ if (uFlags & NDIS_PACKET_TYPE_GROUP) Log5((" NDIS_PACKET_TYPE_GROUP\n"));
+ if (uFlags & NDIS_PACKET_TYPE_MAC_FRAME) Log5((" NDIS_PACKET_TYPE_MAC_FRAME\n"));
+ if (uFlags & NDIS_PACKET_TYPE_SMT) Log5((" NDIS_PACKET_TYPE_SMT\n"));
+ if (uFlags & NDIS_PACKET_TYPE_SOURCE_ROUTING) Log5((" NDIS_PACKET_TYPE_SOURCE_ROUTING\n"));
+ if (uFlags == 0) Log5((" NONE\n"));
+}
+
+DECLINLINE(void) vboxNetLwfWinDumpEncapsulation(const char *pcszText, ULONG uEncapsulation)
+{
+ if (uEncapsulation == NDIS_ENCAPSULATION_NOT_SUPPORTED)
+ Log5(("%s not supported\n", pcszText));
+ else
+ {
+ Log5(("%s", pcszText));
+ if (uEncapsulation & NDIS_ENCAPSULATION_NULL)
+ Log5((" null"));
+ if (uEncapsulation & NDIS_ENCAPSULATION_IEEE_802_3)
+ Log5((" 802.3"));
+ if (uEncapsulation & NDIS_ENCAPSULATION_IEEE_802_3_P_AND_Q)
+ Log5((" 802.3pq"));
+ if (uEncapsulation & NDIS_ENCAPSULATION_IEEE_802_3_P_AND_Q_IN_OOB)
+ Log5((" 802.3pq(oob)"));
+ if (uEncapsulation & NDIS_ENCAPSULATION_IEEE_LLC_SNAP_ROUTED)
+ Log5((" LLC"));
+ Log5(("\n"));
+ }
+}
+
+DECLINLINE(const char *) vboxNetLwfWinSetOnOffText(ULONG uOnOff)
+{
+ switch (uOnOff)
+ {
+ case NDIS_OFFLOAD_SET_NO_CHANGE: return "no change";
+ case NDIS_OFFLOAD_SET_ON: return "on";
+ case NDIS_OFFLOAD_SET_OFF: return "off";
+ }
+ return "unknown";
+}
+
+DECLINLINE(const char *) vboxNetLwfWinOnOffText(ULONG uOnOff)
+{
+ switch (uOnOff)
+ {
+ case NDIS_OFFLOAD_NOT_SUPPORTED: return "off";
+ case NDIS_OFFLOAD_SUPPORTED: return "on";
+ }
+ return "unknown";
+}
+
+DECLINLINE(const char *) vboxNetLwfWinSupportedText(ULONG uSupported)
+{
+ switch (uSupported)
+ {
+ case NDIS_OFFLOAD_NOT_SUPPORTED: return "not supported";
+ case NDIS_OFFLOAD_SUPPORTED: return "supported";
+ }
+ return "unknown";
+}
+
+static void vboxNetLwfWinDumpSetOffloadSettings(PNDIS_OFFLOAD pOffloadConfig)
+{
+ vboxNetLwfWinDumpEncapsulation(" Checksum.IPv4Transmit.Encapsulation =", pOffloadConfig->Checksum.IPv4Transmit.Encapsulation);
+ Log5((" Checksum.IPv4Transmit.IpOptionsSupported = %s\n", vboxNetLwfWinSetOnOffText(pOffloadConfig->Checksum.IPv4Transmit.IpOptionsSupported)));
+ Log5((" Checksum.IPv4Transmit.TcpOptionsSupported = %s\n", vboxNetLwfWinSetOnOffText(pOffloadConfig->Checksum.IPv4Transmit.TcpOptionsSupported)));
+ Log5((" Checksum.IPv4Transmit.TcpChecksum = %s\n", vboxNetLwfWinSetOnOffText(pOffloadConfig->Checksum.IPv4Transmit.TcpChecksum)));
+ Log5((" Checksum.IPv4Transmit.UdpChecksum = %s\n", vboxNetLwfWinSetOnOffText(pOffloadConfig->Checksum.IPv4Transmit.UdpChecksum)));
+ Log5((" Checksum.IPv4Transmit.IpChecksum = %s\n", vboxNetLwfWinSetOnOffText(pOffloadConfig->Checksum.IPv4Transmit.IpChecksum)));
+ vboxNetLwfWinDumpEncapsulation(" Checksum.IPv4Receive.Encapsulation =", pOffloadConfig->Checksum.IPv4Receive.Encapsulation);
+ Log5((" Checksum.IPv4Receive.IpOptionsSupported = %s\n", vboxNetLwfWinSetOnOffText(pOffloadConfig->Checksum.IPv4Receive.IpOptionsSupported)));
+ Log5((" Checksum.IPv4Receive.TcpOptionsSupported = %s\n", vboxNetLwfWinSetOnOffText(pOffloadConfig->Checksum.IPv4Receive.TcpOptionsSupported)));
+ Log5((" Checksum.IPv4Receive.TcpChecksum = %s\n", vboxNetLwfWinSetOnOffText(pOffloadConfig->Checksum.IPv4Receive.TcpChecksum)));
+ Log5((" Checksum.IPv4Receive.UdpChecksum = %s\n", vboxNetLwfWinSetOnOffText(pOffloadConfig->Checksum.IPv4Receive.UdpChecksum)));
+ Log5((" Checksum.IPv4Receive.IpChecksum = %s\n", vboxNetLwfWinSetOnOffText(pOffloadConfig->Checksum.IPv4Receive.IpChecksum)));
+ vboxNetLwfWinDumpEncapsulation(" Checksum.IPv6Transmit.Encapsulation =", pOffloadConfig->Checksum.IPv6Transmit.Encapsulation);
+ Log5((" Checksum.IPv6Transmit.IpExtensionHeadersSupported = %s\n", vboxNetLwfWinSetOnOffText(pOffloadConfig->Checksum.IPv6Transmit.IpExtensionHeadersSupported)));
+ Log5((" Checksum.IPv6Transmit.TcpOptionsSupported = %s\n", vboxNetLwfWinSetOnOffText(pOffloadConfig->Checksum.IPv6Transmit.TcpOptionsSupported)));
+ Log5((" Checksum.IPv6Transmit.TcpChecksum = %s\n", vboxNetLwfWinSetOnOffText(pOffloadConfig->Checksum.IPv6Transmit.TcpChecksum)));
+ Log5((" Checksum.IPv6Transmit.UdpChecksum = %s\n", vboxNetLwfWinSetOnOffText(pOffloadConfig->Checksum.IPv6Transmit.UdpChecksum)));
+ vboxNetLwfWinDumpEncapsulation(" Checksum.IPv6Receive.Encapsulation =", pOffloadConfig->Checksum.IPv6Receive.Encapsulation);
+ Log5((" Checksum.IPv6Receive.IpExtensionHeadersSupported = %s\n", vboxNetLwfWinSetOnOffText(pOffloadConfig->Checksum.IPv6Receive.IpExtensionHeadersSupported)));
+ Log5((" Checksum.IPv6Receive.TcpOptionsSupported = %s\n", vboxNetLwfWinSetOnOffText(pOffloadConfig->Checksum.IPv6Receive.TcpOptionsSupported)));
+ Log5((" Checksum.IPv6Receive.TcpChecksum = %s\n", vboxNetLwfWinSetOnOffText(pOffloadConfig->Checksum.IPv6Receive.TcpChecksum)));
+ Log5((" Checksum.IPv6Receive.UdpChecksum = %s\n", vboxNetLwfWinSetOnOffText(pOffloadConfig->Checksum.IPv6Receive.UdpChecksum)));
+ vboxNetLwfWinDumpEncapsulation(" LsoV1.IPv4.Encapsulation =", pOffloadConfig->LsoV1.IPv4.Encapsulation);
+ Log5((" LsoV1.IPv4.TcpOptions = %s\n", vboxNetLwfWinSupportedText(pOffloadConfig->LsoV1.IPv4.TcpOptions)));
+ Log5((" LsoV1.IPv4.IpOptions = %s\n", vboxNetLwfWinSupportedText(pOffloadConfig->LsoV1.IPv4.IpOptions)));
+ vboxNetLwfWinDumpEncapsulation(" LsoV2.IPv4.Encapsulation =", pOffloadConfig->LsoV2.IPv4.Encapsulation);
+ vboxNetLwfWinDumpEncapsulation(" LsoV2.IPv6.Encapsulation =", pOffloadConfig->LsoV2.IPv6.Encapsulation);
+ Log5((" LsoV2.IPv6.IpExtensionHeadersSupported = %s\n", vboxNetLwfWinSupportedText(pOffloadConfig->LsoV2.IPv6.IpExtensionHeadersSupported)));
+ Log5((" LsoV2.IPv6.TcpOptionsSupported = %s\n", vboxNetLwfWinSupportedText(pOffloadConfig->LsoV2.IPv6.TcpOptionsSupported)));
+}
+
+static void vboxNetLwfWinDumpOffloadSettings(PNDIS_OFFLOAD pOffloadConfig)
+{
+ vboxNetLwfWinDumpEncapsulation(" Checksum.IPv4Transmit.Encapsulation =", pOffloadConfig->Checksum.IPv4Transmit.Encapsulation);
+ Log5((" Checksum.IPv4Transmit.IpOptionsSupported = %s\n", vboxNetLwfWinOnOffText(pOffloadConfig->Checksum.IPv4Transmit.IpOptionsSupported)));
+ Log5((" Checksum.IPv4Transmit.TcpOptionsSupported = %s\n", vboxNetLwfWinOnOffText(pOffloadConfig->Checksum.IPv4Transmit.TcpOptionsSupported)));
+ Log5((" Checksum.IPv4Transmit.TcpChecksum = %s\n", vboxNetLwfWinOnOffText(pOffloadConfig->Checksum.IPv4Transmit.TcpChecksum)));
+ Log5((" Checksum.IPv4Transmit.UdpChecksum = %s\n", vboxNetLwfWinOnOffText(pOffloadConfig->Checksum.IPv4Transmit.UdpChecksum)));
+ Log5((" Checksum.IPv4Transmit.IpChecksum = %s\n", vboxNetLwfWinOnOffText(pOffloadConfig->Checksum.IPv4Transmit.IpChecksum)));
+ vboxNetLwfWinDumpEncapsulation(" Checksum.IPv4Receive.Encapsulation =", pOffloadConfig->Checksum.IPv4Receive.Encapsulation);
+ Log5((" Checksum.IPv4Receive.IpOptionsSupported = %s\n", vboxNetLwfWinOnOffText(pOffloadConfig->Checksum.IPv4Receive.IpOptionsSupported)));
+ Log5((" Checksum.IPv4Receive.TcpOptionsSupported = %s\n", vboxNetLwfWinOnOffText(pOffloadConfig->Checksum.IPv4Receive.TcpOptionsSupported)));
+ Log5((" Checksum.IPv4Receive.TcpChecksum = %s\n", vboxNetLwfWinOnOffText(pOffloadConfig->Checksum.IPv4Receive.TcpChecksum)));
+ Log5((" Checksum.IPv4Receive.UdpChecksum = %s\n", vboxNetLwfWinOnOffText(pOffloadConfig->Checksum.IPv4Receive.UdpChecksum)));
+ Log5((" Checksum.IPv4Receive.IpChecksum = %s\n", vboxNetLwfWinOnOffText(pOffloadConfig->Checksum.IPv4Receive.IpChecksum)));
+ vboxNetLwfWinDumpEncapsulation(" Checksum.IPv6Transmit.Encapsulation =", pOffloadConfig->Checksum.IPv6Transmit.Encapsulation);
+ Log5((" Checksum.IPv6Transmit.IpExtensionHeadersSupported = %s\n", vboxNetLwfWinOnOffText(pOffloadConfig->Checksum.IPv6Transmit.IpExtensionHeadersSupported)));
+ Log5((" Checksum.IPv6Transmit.TcpOptionsSupported = %s\n", vboxNetLwfWinOnOffText(pOffloadConfig->Checksum.IPv6Transmit.TcpOptionsSupported)));
+ Log5((" Checksum.IPv6Transmit.TcpChecksum = %s\n", vboxNetLwfWinOnOffText(pOffloadConfig->Checksum.IPv6Transmit.TcpChecksum)));
+ Log5((" Checksum.IPv6Transmit.UdpChecksum = %s\n", vboxNetLwfWinOnOffText(pOffloadConfig->Checksum.IPv6Transmit.UdpChecksum)));
+ vboxNetLwfWinDumpEncapsulation(" Checksum.IPv6Receive.Encapsulation =", pOffloadConfig->Checksum.IPv6Receive.Encapsulation);
+ Log5((" Checksum.IPv6Receive.IpExtensionHeadersSupported = %s\n", vboxNetLwfWinOnOffText(pOffloadConfig->Checksum.IPv6Receive.IpExtensionHeadersSupported)));
+ Log5((" Checksum.IPv6Receive.TcpOptionsSupported = %s\n", vboxNetLwfWinOnOffText(pOffloadConfig->Checksum.IPv6Receive.TcpOptionsSupported)));
+ Log5((" Checksum.IPv6Receive.TcpChecksum = %s\n", vboxNetLwfWinOnOffText(pOffloadConfig->Checksum.IPv6Receive.TcpChecksum)));
+ Log5((" Checksum.IPv6Receive.UdpChecksum = %s\n", vboxNetLwfWinOnOffText(pOffloadConfig->Checksum.IPv6Receive.UdpChecksum)));
+ vboxNetLwfWinDumpEncapsulation(" LsoV1.IPv4.Encapsulation =", pOffloadConfig->LsoV1.IPv4.Encapsulation);
+ Log5((" LsoV1.IPv4.TcpOptions = %s\n", vboxNetLwfWinSupportedText(pOffloadConfig->LsoV1.IPv4.TcpOptions)));
+ Log5((" LsoV1.IPv4.IpOptions = %s\n", vboxNetLwfWinSupportedText(pOffloadConfig->LsoV1.IPv4.IpOptions)));
+ vboxNetLwfWinDumpEncapsulation(" LsoV2.IPv4.Encapsulation =", pOffloadConfig->LsoV2.IPv4.Encapsulation);
+ vboxNetLwfWinDumpEncapsulation(" LsoV2.IPv6.Encapsulation =", pOffloadConfig->LsoV2.IPv6.Encapsulation);
+ Log5((" LsoV2.IPv6.IpExtensionHeadersSupported = %s\n", vboxNetLwfWinSupportedText(pOffloadConfig->LsoV2.IPv6.IpExtensionHeadersSupported)));
+ Log5((" LsoV2.IPv6.TcpOptionsSupported = %s\n", vboxNetLwfWinSupportedText(pOffloadConfig->LsoV2.IPv6.TcpOptionsSupported)));
+}
+
+static const char *vboxNetLwfWinStateToText(uint32_t enmState)
+{
+ switch (enmState)
+ {
+ case LwfState_Detached: return "Detached";
+ case LwfState_Attaching: return "Attaching";
+ case LwfState_Paused: return "Paused";
+ case LwfState_Restarting: return "Restarting";
+ case LwfState_Running: return "Running";
+ case LwfState_Pausing: return "Pausing";
+ }
+ return "invalid";
+}
+
+static void vboxNetLwfWinDumpPackets(const char *pszMsg, PNET_BUFFER_LIST pBufLists)
+{
+ for (PNET_BUFFER_LIST pList = pBufLists; pList; pList = NET_BUFFER_LIST_NEXT_NBL(pList))
+ {
+ for (PNET_BUFFER pBuf = NET_BUFFER_LIST_FIRST_NB(pList); pBuf; pBuf = NET_BUFFER_NEXT_NB(pBuf))
+ {
+ Log6(("%s packet: src=%p cb=%d offset=%d", pszMsg, pList->SourceHandle, NET_BUFFER_DATA_LENGTH(pBuf), NET_BUFFER_DATA_OFFSET(pBuf)));
+ for (PMDL pMdl = NET_BUFFER_FIRST_MDL(pBuf);
+ pMdl != NULL;
+ pMdl = NDIS_MDL_LINKAGE(pMdl))
+ {
+ Log6((" MDL: cb=%d", MmGetMdlByteCount(pMdl)));
+ }
+ Log6(("\n"));
+ }
+ }
+}
+
+DECLINLINE(const char *) vboxNetLwfWinEthTypeStr(uint16_t uType)
+{
+ switch (uType)
+ {
+ case RTNET_ETHERTYPE_IPV4: return "IP";
+ case RTNET_ETHERTYPE_IPV6: return "IPv6";
+ case RTNET_ETHERTYPE_ARP: return "ARP";
+ }
+ return "unknown";
+}
+
+#define VBOXNETLWF_PKTDMPSIZE 0x50
+
+/**
+ * Dump a packet to debug log.
+ *
+ * @param cpPacket The packet.
+ * @param cb The size of the packet.
+ * @param cszText A string denoting direction of packet transfer.
+ */
+DECLINLINE(void) vboxNetLwfWinDumpPacket(PCINTNETSG pSG, const char *cszText)
+{
+ uint8_t bPacket[VBOXNETLWF_PKTDMPSIZE];
+
+ uint32_t cb = pSG->cbTotal < VBOXNETLWF_PKTDMPSIZE ? pSG->cbTotal : VBOXNETLWF_PKTDMPSIZE;
+ IntNetSgReadEx(pSG, 0, cb, bPacket);
+
+ AssertReturnVoid(cb >= 14);
+
+ uint8_t *pHdr = bPacket;
+ uint8_t *pEnd = bPacket + cb;
+ AssertReturnVoid(pEnd - pHdr >= 14);
+ uint16_t uEthType = RT_N2H_U16(*(uint16_t*)(pHdr+12));
+ Log2(("NetLWF: %s (%d bytes), %RTmac => %RTmac, EthType=%s(0x%x)\n",
+ cszText, pSG->cbTotal, pHdr+6, pHdr, vboxNetLwfWinEthTypeStr(uEthType), uEthType));
+ pHdr += sizeof(RTNETETHERHDR);
+ if (uEthType == RTNET_ETHERTYPE_VLAN)
+ {
+ AssertReturnVoid(pEnd - pHdr >= 4);
+ uEthType = RT_N2H_U16(*(uint16_t*)(pHdr+2));
+ Log2((" + VLAN: id=%d EthType=%s(0x%x)\n", RT_N2H_U16(*(uint16_t*)(pHdr)) & 0xFFF,
+ vboxNetLwfWinEthTypeStr(uEthType), uEthType));
+ pHdr += 2 * sizeof(uint16_t);
+ }
+ uint8_t uProto = 0xFF;
+ switch (uEthType)
+ {
+ case RTNET_ETHERTYPE_IPV6:
+ AssertReturnVoid(pEnd - pHdr >= 40);
+ uProto = pHdr[6];
+ Log2((" + IPv6: %RTnaipv6 => %RTnaipv6\n", pHdr+8, pHdr+24));
+ pHdr += 40;
+ break;
+ case RTNET_ETHERTYPE_IPV4:
+ AssertReturnVoid(pEnd - pHdr >= 20);
+ uProto = pHdr[9];
+ Log2((" + IP: %RTnaipv4 => %RTnaipv4\n", *(uint32_t*)(pHdr+12), *(uint32_t*)(pHdr+16)));
+ pHdr += (pHdr[0] & 0xF) * 4;
+ break;
+ case RTNET_ETHERTYPE_ARP:
+ AssertReturnVoid(pEnd - pHdr >= 28);
+ AssertReturnVoid(RT_N2H_U16(*(uint16_t*)(pHdr+2)) == RTNET_ETHERTYPE_IPV4);
+ switch (RT_N2H_U16(*(uint16_t*)(pHdr+6)))
+ {
+ case 1: /* ARP request */
+ Log2((" + ARP-REQ: who-has %RTnaipv4 tell %RTnaipv4\n",
+ *(uint32_t*)(pHdr+24), *(uint32_t*)(pHdr+14)));
+ break;
+ case 2: /* ARP reply */
+ Log2((" + ARP-RPL: %RTnaipv4 is-at %RTmac\n",
+ *(uint32_t*)(pHdr+14), pHdr+8));
+ break;
+ default:
+ Log2((" + ARP: unknown op %d\n", RT_N2H_U16(*(uint16_t*)(pHdr+6))));
+ break;
+ }
+ break;
+ /* There is no default case as uProto is initialized with 0xFF */
+ }
+ while (uProto != 0xFF)
+ {
+ switch (uProto)
+ {
+ case 0: /* IPv6 Hop-by-Hop option*/
+ case 60: /* IPv6 Destination option*/
+ case 43: /* IPv6 Routing option */
+ case 44: /* IPv6 Fragment option */
+ Log2((" + IPv6 option (%d): <not implemented>\n", uProto));
+ uProto = pHdr[0];
+ pHdr += pHdr[1] * 8 + 8; /* Skip to the next extension/protocol */
+ break;
+ case 51: /* IPv6 IPsec AH */
+ Log2((" + IPv6 IPsec AH: <not implemented>\n"));
+ uProto = pHdr[0];
+ pHdr += (pHdr[1] + 2) * 4; /* Skip to the next extension/protocol */
+ break;
+ case 50: /* IPv6 IPsec ESP */
+ /* Cannot decode IPsec, fall through */
+ Log2((" + IPv6 IPsec ESP: <not implemented>\n"));
+ uProto = 0xFF;
+ break;
+ case 59: /* No Next Header */
+ Log2((" + IPv6 No Next Header\n"));
+ uProto = 0xFF;
+ break;
+ case 58: /* IPv6-ICMP */
+ switch (pHdr[0])
+ {
+ case 1: Log2((" + IPv6-ICMP: destination unreachable, code %d\n", pHdr[1])); break;
+ case 128: Log2((" + IPv6-ICMP: echo request\n")); break;
+ case 129: Log2((" + IPv6-ICMP: echo reply\n")); break;
+ default: Log2((" + IPv6-ICMP: unknown type %d, code %d\n", pHdr[0], pHdr[1])); break;
+ }
+ uProto = 0xFF;
+ break;
+ case 1: /* ICMP */
+ switch (pHdr[0])
+ {
+ case 0: Log2((" + ICMP: echo reply\n")); break;
+ case 8: Log2((" + ICMP: echo request\n")); break;
+ case 3: Log2((" + ICMP: destination unreachable, code %d\n", pHdr[1])); break;
+ default: Log2((" + ICMP: unknown type %d, code %d\n", pHdr[0], pHdr[1])); break;
+ }
+ uProto = 0xFF;
+ break;
+ case 6: /* TCP */
+ Log2((" + TCP: src=%d dst=%d seq=%x ack=%x\n",
+ RT_N2H_U16(*(uint16_t*)(pHdr)), RT_N2H_U16(*(uint16_t*)(pHdr+2)),
+ RT_N2H_U32(*(uint32_t*)(pHdr+4)), RT_N2H_U32(*(uint32_t*)(pHdr+8))));
+ uProto = 0xFF;
+ break;
+ case 17: /* UDP */
+ Log2((" + UDP: src=%d dst=%d\n",
+ RT_N2H_U16(*(uint16_t*)(pHdr)), RT_N2H_U16(*(uint16_t*)(pHdr+2))));
+ uProto = 0xFF;
+ break;
+ default:
+ Log2((" + Unknown: proto=0x%x\n", uProto));
+ uProto = 0xFF;
+ break;
+ }
+ }
+ Log3(("%.*Rhxd\n", cb, bPacket));
+}
+
+#else /* !DEBUG */
+# define vboxNetLwfWinDumpFilterTypes(uFlags) do { } while (0)
+# define vboxNetLwfWinDumpOffloadSettings(p) do { } while (0)
+# define vboxNetLwfWinDumpSetOffloadSettings(p) do { } while (0)
+# define vboxNetLwfWinDumpPackets(m,l) do { } while (0)
+# define vboxNetLwfWinDumpPacket(p,t) do { } while (0)
+#endif /* !DEBUG */
+
+DECLINLINE(bool) vboxNetLwfWinChangeState(PVBOXNETLWF_MODULE pModuleCtx, uint32_t enmNew, uint32_t enmOld = LwfState_32BitHack)
+{
+ AssertReturn(pModuleCtx, false);
+
+ bool fSuccess = true;
+ if (enmOld != LwfState_32BitHack)
+ {
+ fSuccess = ASMAtomicCmpXchgU32(&pModuleCtx->enmState, enmNew, enmOld);
+ if (fSuccess)
+ Log(("vboxNetLwfWinChangeState: state change %s -> %s\n",
+ vboxNetLwfWinStateToText(enmOld),
+ vboxNetLwfWinStateToText(enmNew)));
+ else
+ Log(("ERROR! vboxNetLwfWinChangeState: failed state change %s (actual=%s) -> %s\n",
+ vboxNetLwfWinStateToText(enmOld),
+ vboxNetLwfWinStateToText(ASMAtomicReadU32(&pModuleCtx->enmState)),
+ vboxNetLwfWinStateToText(enmNew)));
+ Assert(fSuccess);
+ }
+ else
+ {
+ uint32_t enmPrevState = ASMAtomicXchgU32(&pModuleCtx->enmState, enmNew);
+ Log(("vboxNetLwfWinChangeState: state change %s -> %s\n",
+ vboxNetLwfWinStateToText(enmPrevState),
+ vboxNetLwfWinStateToText(enmNew)));
+ NOREF(enmPrevState);
+ }
+ return fSuccess;
+}
+
+DECLINLINE(void) vboxNetLwfWinInitOidRequest(PVBOXNETLWF_OIDREQ pRequest)
+{
+ NdisZeroMemory(pRequest, sizeof(VBOXNETLWF_OIDREQ));
+
+ NdisInitializeEvent(&pRequest->Event);
+
+ pRequest->Request.Header.Type = NDIS_OBJECT_TYPE_OID_REQUEST;
+ pRequest->Request.Header.Revision = NDIS_OID_REQUEST_REVISION_1;
+ pRequest->Request.Header.Size = NDIS_SIZEOF_OID_REQUEST_REVISION_1;
+
+ pRequest->Request.RequestId = (PVOID)VBOXNETLWF_REQ_ID;
+}
+
+static NDIS_STATUS vboxNetLwfWinSyncOidRequest(PVBOXNETLWF_MODULE pModuleCtx, PVBOXNETLWF_OIDREQ pRequest)
+{
+ NDIS_STATUS Status = NdisFOidRequest(pModuleCtx->hFilter, &pRequest->Request);
+ if (Status == NDIS_STATUS_PENDING)
+ {
+ NdisWaitEvent(&pRequest->Event, 0);
+ Status = pRequest->Status;
+ }
+ return Status;
+}
+
+DECLINLINE(void) vboxNetLwfWinCopyOidRequestResults(PNDIS_OID_REQUEST pFrom, PNDIS_OID_REQUEST pTo)
+{
+ switch (pFrom->RequestType)
+ {
+ case NdisRequestSetInformation:
+ pTo->DATA.SET_INFORMATION.BytesRead = pFrom->DATA.SET_INFORMATION.BytesRead;
+ pTo->DATA.SET_INFORMATION.BytesNeeded = pFrom->DATA.SET_INFORMATION.BytesNeeded;
+ break;
+ case NdisRequestMethod:
+ pTo->DATA.METHOD_INFORMATION.OutputBufferLength = pFrom->DATA.METHOD_INFORMATION.OutputBufferLength;
+ pTo->DATA.METHOD_INFORMATION.BytesWritten = pFrom->DATA.METHOD_INFORMATION.BytesWritten;
+ pTo->DATA.METHOD_INFORMATION.BytesRead = pFrom->DATA.METHOD_INFORMATION.BytesRead;
+ pTo->DATA.METHOD_INFORMATION.BytesNeeded = pFrom->DATA.METHOD_INFORMATION.BytesNeeded;
+ break;
+ case NdisRequestQueryInformation:
+ case NdisRequestQueryStatistics:
+ default:
+ pTo->DATA.QUERY_INFORMATION.BytesWritten = pFrom->DATA.QUERY_INFORMATION.BytesWritten;
+ pTo->DATA.QUERY_INFORMATION.BytesNeeded = pFrom->DATA.QUERY_INFORMATION.BytesNeeded;
+ }
+}
+
+void inline vboxNetLwfWinOverridePacketFiltersUp(PVBOXNETLWF_MODULE pModuleCtx, ULONG *pFilters)
+{
+ if (ASMAtomicReadBool(&pModuleCtx->fActive) && !ASMAtomicReadBool(&pModuleCtx->fHostPromisc))
+ *pFilters &= ~NDIS_PACKET_TYPE_PROMISCUOUS;
+}
+
+NDIS_STATUS vboxNetLwfWinOidRequest(IN NDIS_HANDLE hModuleCtx,
+ IN PNDIS_OID_REQUEST pOidRequest)
+{
+ LogFlow(("==>vboxNetLwfWinOidRequest: module=%p\n", hModuleCtx));
+ vboxNetCmnWinDumpOidRequest(__FUNCTION__, pOidRequest);
+ PVBOXNETLWF_MODULE pModuleCtx = (PVBOXNETLWF_MODULE)hModuleCtx;
+ PNDIS_OID_REQUEST pClone = NULL;
+ NDIS_STATUS Status = NdisAllocateCloneOidRequest(pModuleCtx->hFilter,
+ pOidRequest,
+ VBOXNETLWF_MEM_TAG,
+ &pClone);
+ if (Status == NDIS_STATUS_SUCCESS)
+ {
+ /* Save the pointer to the original */
+ *((PNDIS_OID_REQUEST*)(pClone->SourceReserved)) = pOidRequest;
+
+ pClone->RequestId = pOidRequest->RequestId;
+ /* We are not supposed to get another request until we are through with the one we "postponed" */
+ PNDIS_OID_REQUEST pPrev = ASMAtomicXchgPtrT(&pModuleCtx->pPendingRequest, pClone, PNDIS_OID_REQUEST);
+ Assert(pPrev == NULL);
+ pModuleCtx->pPendingRequest = pClone;
+ if (pOidRequest->RequestType == NdisRequestSetInformation
+ && pOidRequest->DATA.SET_INFORMATION.Oid == OID_GEN_CURRENT_PACKET_FILTER)
+ {
+ ASMAtomicWriteBool(&pModuleCtx->fHostPromisc, !!(*(ULONG*)pOidRequest->DATA.SET_INFORMATION.InformationBuffer & NDIS_PACKET_TYPE_PROMISCUOUS));
+ Log(("vboxNetLwfWinOidRequest: host wanted to set packet filter value to:\n"));
+ vboxNetLwfWinDumpFilterTypes(*(ULONG*)pOidRequest->DATA.SET_INFORMATION.InformationBuffer);
+ /* Keep adapter in promisc mode as long as we are active. */
+ if (ASMAtomicReadBool(&pModuleCtx->fActive))
+ *(ULONG*)pClone->DATA.SET_INFORMATION.InformationBuffer |= NDIS_PACKET_TYPE_PROMISCUOUS;
+ Log5(("vboxNetLwfWinOidRequest: pass the following packet filters to miniport:\n"));
+ vboxNetLwfWinDumpFilterTypes(*(ULONG*)pOidRequest->DATA.SET_INFORMATION.InformationBuffer);
+ }
+ if (pOidRequest->RequestType == NdisRequestSetInformation
+ && pOidRequest->DATA.SET_INFORMATION.Oid == OID_TCP_OFFLOAD_CURRENT_CONFIG)
+ {
+ Log5(("vboxNetLwfWinOidRequest: offloading set to:\n"));
+ vboxNetLwfWinDumpSetOffloadSettings((PNDIS_OFFLOAD)pOidRequest->DATA.SET_INFORMATION.InformationBuffer);
+ }
+
+ /* Forward the clone to underlying filters/miniport */
+ Status = NdisFOidRequest(pModuleCtx->hFilter, pClone);
+ if (Status != NDIS_STATUS_PENDING)
+ {
+ /* Synchronous completion */
+ pPrev = ASMAtomicXchgPtrT(&pModuleCtx->pPendingRequest, NULL, PNDIS_OID_REQUEST);
+ Assert(pPrev == pClone);
+ Log5(("vboxNetLwfWinOidRequest: got the following packet filters from miniport:\n"));
+ vboxNetLwfWinDumpFilterTypes(*(ULONG*)pOidRequest->DATA.QUERY_INFORMATION.InformationBuffer);
+ /*
+ * The host does not expect the adapter to be in promisc mode,
+ * unless it enabled the mode. Let's not disillusion it.
+ */
+ if ( pOidRequest->RequestType == NdisRequestQueryInformation
+ && pOidRequest->DATA.QUERY_INFORMATION.Oid == OID_GEN_CURRENT_PACKET_FILTER)
+ vboxNetLwfWinOverridePacketFiltersUp(pModuleCtx, (ULONG*)pOidRequest->DATA.QUERY_INFORMATION.InformationBuffer);
+ Log5(("vboxNetLwfWinOidRequest: reporting to the host the following packet filters:\n"));
+ vboxNetLwfWinDumpFilterTypes(*(ULONG*)pOidRequest->DATA.QUERY_INFORMATION.InformationBuffer);
+ vboxNetLwfWinCopyOidRequestResults(pClone, pOidRequest);
+ NdisFreeCloneOidRequest(pModuleCtx->hFilter, pClone);
+ }
+ /* In case of async completion we do the rest in vboxNetLwfWinOidRequestComplete() */
+ }
+ else
+ {
+ LogError(("vboxNetLwfWinOidRequest: NdisAllocateCloneOidRequest failed with 0x%x\n", Status));
+ }
+ LogFlow(("<==vboxNetLwfWinOidRequest: Status=0x%x\n", Status));
+ return Status;
+}
+
+VOID vboxNetLwfWinOidRequestComplete(IN NDIS_HANDLE hModuleCtx,
+ IN PNDIS_OID_REQUEST pRequest,
+ IN NDIS_STATUS Status)
+{
+ LogFlow(("==>vboxNetLwfWinOidRequestComplete: module=%p req=%p status=0x%x\n", hModuleCtx, pRequest, Status));
+ PVBOXNETLWF_MODULE pModuleCtx = (PVBOXNETLWF_MODULE)hModuleCtx;
+ PNDIS_OID_REQUEST pOriginal = *((PNDIS_OID_REQUEST*)(pRequest->SourceReserved));
+ if (pOriginal)
+ {
+ /* NDIS is supposed to serialize requests */
+ PNDIS_OID_REQUEST pPrev = ASMAtomicXchgPtrT(&pModuleCtx->pPendingRequest, NULL, PNDIS_OID_REQUEST);
+ Assert(pPrev == pRequest); NOREF(pPrev);
+
+ Log5(("vboxNetLwfWinOidRequestComplete: completed rq type=%d oid=%x\n", pRequest->RequestType, pRequest->DATA.QUERY_INFORMATION.Oid));
+ vboxNetLwfWinCopyOidRequestResults(pRequest, pOriginal);
+ if ( pRequest->RequestType == NdisRequestQueryInformation
+ && pRequest->DATA.QUERY_INFORMATION.Oid == OID_GEN_CURRENT_PACKET_FILTER)
+ {
+ Log5(("vboxNetLwfWinOidRequestComplete: underlying miniport reports its packet filters:\n"));
+ vboxNetLwfWinDumpFilterTypes(*(ULONG*)pRequest->DATA.QUERY_INFORMATION.InformationBuffer);
+ vboxNetLwfWinOverridePacketFiltersUp(pModuleCtx, (ULONG*)pRequest->DATA.QUERY_INFORMATION.InformationBuffer);
+ Log5(("vboxNetLwfWinOidRequestComplete: reporting the following packet filters to upper protocol:\n"));
+ vboxNetLwfWinDumpFilterTypes(*(ULONG*)pRequest->DATA.QUERY_INFORMATION.InformationBuffer);
+ }
+ NdisFreeCloneOidRequest(pModuleCtx->hFilter, pRequest);
+ NdisFOidRequestComplete(pModuleCtx->hFilter, pOriginal, Status);
+ }
+ else
+ {
+ /* This is not a clone, we originated it */
+ Log(("vboxNetLwfWinOidRequestComplete: locally originated request (%p) completed, status=0x%x\n", pRequest, Status));
+ PVBOXNETLWF_OIDREQ pRqWrapper = RT_FROM_MEMBER(pRequest, VBOXNETLWF_OIDREQ, Request);
+ pRqWrapper->Status = Status;
+ NdisSetEvent(&pRqWrapper->Event);
+ }
+ LogFlow(("<==vboxNetLwfWinOidRequestComplete\n"));
+}
+
+
+static bool vboxNetLwfWinIsPromiscuous(PVBOXNETLWF_MODULE pModuleCtx)
+{
+ return ASMAtomicReadBool(&pModuleCtx->fHostPromisc);
+}
+
+#if 0
+static NDIS_STATUS vboxNetLwfWinGetPacketFilter(PVBOXNETLWF_MODULE pModuleCtx)
+{
+ LogFlow(("==>vboxNetLwfWinGetPacketFilter: module=%p\n", pModuleCtx));
+ VBOXNETLWF_OIDREQ Rq;
+ vboxNetLwfWinInitOidRequest(&Rq);
+ Rq.Request.RequestType = NdisRequestQueryInformation;
+ Rq.Request.DATA.QUERY_INFORMATION.Oid = OID_GEN_CURRENT_PACKET_FILTER;
+ Rq.Request.DATA.QUERY_INFORMATION.InformationBuffer = &pModuleCtx->uPacketFilter;
+ Rq.Request.DATA.QUERY_INFORMATION.InformationBufferLength = sizeof(pModuleCtx->uPacketFilter);
+ NDIS_STATUS Status = vboxNetLwfWinSyncOidRequest(pModuleCtx, &Rq);
+ if (Status != NDIS_STATUS_SUCCESS)
+ {
+ LogError(("vboxNetLwfWinGetPacketFilter: vboxNetLwfWinSyncOidRequest(query, OID_GEN_CURRENT_PACKET_FILTER) failed with 0x%x\n", Status));
+ return FALSE;
+ }
+ if (Rq.Request.DATA.QUERY_INFORMATION.BytesWritten != sizeof(pModuleCtx->uPacketFilter))
+ {
+ LogError(("vboxNetLwfWinGetPacketFilter: vboxNetLwfWinSyncOidRequest(query, OID_GEN_CURRENT_PACKET_FILTER) failed to write neccessary amount (%d bytes), actually written %d bytes\n", sizeof(pModuleCtx->uPacketFilter), Rq.Request.DATA.QUERY_INFORMATION.BytesWritten));
+ }
+
+ Log5(("vboxNetLwfWinGetPacketFilter: OID_GEN_CURRENT_PACKET_FILTER query returned the following filters:\n"));
+ vboxNetLwfWinDumpFilterTypes(pModuleCtx->uPacketFilter);
+
+ LogFlow(("<==vboxNetLwfWinGetPacketFilter: status=0x%x\n", Status));
+ return Status;
+}
+#endif
+
+static NDIS_STATUS vboxNetLwfWinSetPacketFilter(PVBOXNETLWF_MODULE pModuleCtx, bool fPromisc)
+{
+ LogFlow(("==>vboxNetLwfWinSetPacketFilter: module=%p %s\n", pModuleCtx, fPromisc ? "promiscuous" : "normal"));
+ ULONG uFilter = 0;
+ VBOXNETLWF_OIDREQ Rq;
+ vboxNetLwfWinInitOidRequest(&Rq);
+ Rq.Request.RequestType = NdisRequestQueryInformation;
+ Rq.Request.DATA.QUERY_INFORMATION.Oid = OID_GEN_CURRENT_PACKET_FILTER;
+ Rq.Request.DATA.QUERY_INFORMATION.InformationBuffer = &uFilter;
+ Rq.Request.DATA.QUERY_INFORMATION.InformationBufferLength = sizeof(uFilter);
+ NDIS_STATUS Status = vboxNetLwfWinSyncOidRequest(pModuleCtx, &Rq);
+ if (Status != NDIS_STATUS_SUCCESS)
+ {
+ LogError(("vboxNetLwfWinSetPacketFilter: vboxNetLwfWinSyncOidRequest(query, OID_GEN_CURRENT_PACKET_FILTER) failed with 0x%x\n", Status));
+ return Status;
+ }
+ if (Rq.Request.DATA.QUERY_INFORMATION.BytesWritten != sizeof(uFilter))
+ {
+ LogError(("vboxNetLwfWinSetPacketFilter: vboxNetLwfWinSyncOidRequest(query, OID_GEN_CURRENT_PACKET_FILTER) failed to write neccessary amount (%d bytes), actually written %d bytes\n", sizeof(uFilter), Rq.Request.DATA.QUERY_INFORMATION.BytesWritten));
+ return NDIS_STATUS_FAILURE;
+ }
+
+ Log5(("vboxNetLwfWinSetPacketFilter: OID_GEN_CURRENT_PACKET_FILTER query returned the following filters:\n"));
+ vboxNetLwfWinDumpFilterTypes(uFilter);
+
+ if (fPromisc)
+ {
+ /* If we about to go promiscuous, save the state before we change it. */
+ ASMAtomicWriteBool(&pModuleCtx->fHostPromisc, !!(uFilter & NDIS_PACKET_TYPE_PROMISCUOUS));
+ uFilter |= NDIS_PACKET_TYPE_PROMISCUOUS;
+ }
+ else
+ {
+ /* Reset promisc only if it was not enabled before we had changed it. */
+ if (!ASMAtomicReadBool(&pModuleCtx->fHostPromisc))
+ uFilter &= ~NDIS_PACKET_TYPE_PROMISCUOUS;
+ }
+
+ Log5(("vboxNetLwfWinSetPacketFilter: OID_GEN_CURRENT_PACKET_FILTER about to set the following filters:\n"));
+ vboxNetLwfWinDumpFilterTypes(uFilter);
+
+ NdisResetEvent(&Rq.Event); /* need to reset as it has been set by query op */
+ Rq.Request.RequestType = NdisRequestSetInformation;
+ Rq.Request.DATA.SET_INFORMATION.Oid = OID_GEN_CURRENT_PACKET_FILTER;
+ Rq.Request.DATA.SET_INFORMATION.InformationBuffer = &uFilter;
+ Rq.Request.DATA.SET_INFORMATION.InformationBufferLength = sizeof(uFilter);
+ Status = vboxNetLwfWinSyncOidRequest(pModuleCtx, &Rq);
+ if (Status != NDIS_STATUS_SUCCESS)
+ {
+ LogError(("vboxNetLwfWinSetPacketFilter: vboxNetLwfWinSyncOidRequest(set, OID_GEN_CURRENT_PACKET_FILTER, vvv below vvv) failed with 0x%x\n", Status));
+ vboxNetLwfWinDumpFilterTypes(uFilter);
+ }
+ LogFlow(("<==vboxNetLwfWinSetPacketFilter: status=0x%x\n", Status));
+ return Status;
+}
+
+
+static NTSTATUS vboxNetLwfWinDevDispatch(IN PDEVICE_OBJECT pDevObj, IN PIRP pIrp)
+{
+ RT_NOREF1(pDevObj);
+ PIO_STACK_LOCATION pIrpSl = IoGetCurrentIrpStackLocation(pIrp);;
+ NTSTATUS Status = STATUS_SUCCESS;
+
+ switch (pIrpSl->MajorFunction)
+ {
+ case IRP_MJ_DEVICE_CONTROL:
+ Status = STATUS_NOT_SUPPORTED;
+ break;
+ case IRP_MJ_CREATE:
+ case IRP_MJ_CLEANUP:
+ case IRP_MJ_CLOSE:
+ break;
+ default:
+ AssertFailed();
+ break;
+ }
+
+ pIrp->IoStatus.Status = Status;
+ IoCompleteRequest(pIrp, IO_NO_INCREMENT);
+
+ return Status;
+}
+
+/** @todo So far we had no use for device, should we even bother to create it? */
+static NDIS_STATUS vboxNetLwfWinDevCreate(PVBOXNETLWFGLOBALS pGlobals)
+{
+ NDIS_STRING DevName, LinkName;
+ PDRIVER_DISPATCH aMajorFunctions[IRP_MJ_MAXIMUM_FUNCTION+1];
+ NdisInitUnicodeString(&DevName, VBOXNETLWF_NAME_DEVICE);
+ NdisInitUnicodeString(&LinkName, VBOXNETLWF_NAME_LINK);
+
+ Assert(!pGlobals->hDevice);
+ Assert(!pGlobals->pDevObj);
+ NdisZeroMemory(aMajorFunctions, sizeof (aMajorFunctions));
+ aMajorFunctions[IRP_MJ_CREATE] = vboxNetLwfWinDevDispatch;
+ aMajorFunctions[IRP_MJ_CLEANUP] = vboxNetLwfWinDevDispatch;
+ aMajorFunctions[IRP_MJ_CLOSE] = vboxNetLwfWinDevDispatch;
+ aMajorFunctions[IRP_MJ_DEVICE_CONTROL] = vboxNetLwfWinDevDispatch;
+
+ NDIS_DEVICE_OBJECT_ATTRIBUTES DeviceAttributes;
+ NdisZeroMemory(&DeviceAttributes, sizeof(DeviceAttributes));
+ DeviceAttributes.Header.Type = NDIS_OBJECT_TYPE_DEVICE_OBJECT_ATTRIBUTES;
+ DeviceAttributes.Header.Revision = NDIS_DEVICE_OBJECT_ATTRIBUTES_REVISION_1;
+ DeviceAttributes.Header.Size = sizeof(DeviceAttributes);
+ DeviceAttributes.DeviceName = &DevName;
+ DeviceAttributes.SymbolicName = &LinkName;
+ DeviceAttributes.MajorFunctions = aMajorFunctions;
+ //DeviceAttributes.ExtensionSize = sizeof(FILTER_DEVICE_EXTENSION);
+
+ NDIS_STATUS Status = NdisRegisterDeviceEx(pGlobals->hFilterDriver,
+ &DeviceAttributes,
+ &pGlobals->pDevObj,
+ &pGlobals->hDevice);
+ Log(("vboxNetLwfWinDevCreate: NdisRegisterDeviceEx returned 0x%x\n", Status));
+ Assert(Status == NDIS_STATUS_SUCCESS);
+#if 0
+ if (Status == NDIS_STATUS_SUCCESS)
+ {
+ PFILTER_DEVICE_EXTENSION pExtension;
+ pExtension = NdisGetDeviceReservedExtension(pGlobals->pDevObj);
+ pExtension->Signature = VBOXNETLWF_MEM_TAG;
+ pExtension->Handle = pGlobals->hFilterDriver;
+ }
+#endif
+ return Status;
+}
+
+static void vboxNetLwfWinDevDestroy(PVBOXNETLWFGLOBALS pGlobals)
+{
+ Assert(pGlobals->hDevice);
+ Assert(pGlobals->pDevObj);
+ NdisDeregisterDeviceEx(pGlobals->hDevice);
+ pGlobals->hDevice = NULL;
+ pGlobals->pDevObj = NULL;
+}
+
+static void vboxNetLwfWinDisableOffloading(PNDIS_OFFLOAD pOffloadConfig)
+{
+ pOffloadConfig->Checksum.IPv4Transmit.Encapsulation = NDIS_ENCAPSULATION_NOT_SUPPORTED;
+ pOffloadConfig->Checksum.IPv4Transmit.IpOptionsSupported = NDIS_OFFLOAD_NOT_SUPPORTED;
+ pOffloadConfig->Checksum.IPv4Transmit.TcpOptionsSupported = NDIS_OFFLOAD_NOT_SUPPORTED;
+ pOffloadConfig->Checksum.IPv4Transmit.TcpChecksum = NDIS_OFFLOAD_NOT_SUPPORTED;
+ pOffloadConfig->Checksum.IPv4Transmit.UdpChecksum = NDIS_OFFLOAD_NOT_SUPPORTED;
+ pOffloadConfig->Checksum.IPv4Transmit.IpChecksum = NDIS_OFFLOAD_NOT_SUPPORTED;
+ pOffloadConfig->Checksum.IPv6Transmit.Encapsulation = NDIS_ENCAPSULATION_NOT_SUPPORTED;
+ pOffloadConfig->Checksum.IPv6Transmit.IpExtensionHeadersSupported = NDIS_OFFLOAD_NOT_SUPPORTED;
+ pOffloadConfig->Checksum.IPv6Transmit.TcpOptionsSupported = NDIS_OFFLOAD_NOT_SUPPORTED;
+ pOffloadConfig->Checksum.IPv6Transmit.TcpChecksum = NDIS_OFFLOAD_NOT_SUPPORTED;
+ pOffloadConfig->Checksum.IPv6Transmit.UdpChecksum = NDIS_OFFLOAD_NOT_SUPPORTED;
+ pOffloadConfig->LsoV1.IPv4.Encapsulation = NDIS_ENCAPSULATION_NOT_SUPPORTED;
+ pOffloadConfig->LsoV1.IPv4.TcpOptions = NDIS_OFFLOAD_NOT_SUPPORTED;
+ pOffloadConfig->LsoV1.IPv4.IpOptions = NDIS_OFFLOAD_NOT_SUPPORTED;
+ pOffloadConfig->LsoV2.IPv4.Encapsulation = NDIS_ENCAPSULATION_NOT_SUPPORTED;
+ pOffloadConfig->LsoV2.IPv6.Encapsulation = NDIS_ENCAPSULATION_NOT_SUPPORTED;
+ pOffloadConfig->LsoV2.IPv6.IpExtensionHeadersSupported = NDIS_OFFLOAD_NOT_SUPPORTED;
+ pOffloadConfig->LsoV2.IPv6.TcpOptionsSupported = NDIS_OFFLOAD_NOT_SUPPORTED;
+}
+
+static void vboxNetLwfWinUpdateSavedOffloadConfig(PVBOXNETLWF_MODULE pModuleCtx, PNDIS_OFFLOAD pOffload)
+{
+ if (pModuleCtx->cbOffloadConfig < pOffload->Header.Size)
+ {
+ vboxNetLwfLogErrorEvent(IO_ERR_INTERNAL_ERROR, STATUS_SUCCESS, 10);
+ return;
+ }
+
+ NdisMoveMemory(pModuleCtx->pSavedOffloadConfig, pOffload, pOffload->Header.Size);
+ NdisMoveMemory(pModuleCtx->pDisabledOffloadConfig, pOffload, pOffload->Header.Size);
+ vboxNetLwfWinDisableOffloading(pModuleCtx->pDisabledOffloadConfig);
+ pModuleCtx->fOffloadConfigValid = true;
+}
+
+#ifdef VBOXNETLWF_FIXED_SIZE_POOLS
+static void vboxNetLwfWinFreePools(PVBOXNETLWF_MODULE pModuleCtx, int cPools)
+{
+ for (int i = 0; i < cPools; ++i)
+ {
+ if (pModuleCtx->hPool[i])
+ {
+ NdisFreeNetBufferListPool(pModuleCtx->hPool[i]);
+ Log4(("vboxNetLwfWinFreePools: freed NBL+NB pool 0x%p\n", pModuleCtx->hPool[i]));
+ }
+ }
+}
+#endif /* VBOXNETLWF_FIXED_SIZE_POOLS */
+
+
+static void vboxNetLwfWinFreeModuleResources(PVBOXNETLWF_MODULE pModuleCtx)
+{
+#ifdef VBOXNETLWF_FIXED_SIZE_POOLS
+ vboxNetLwfWinFreePools(pModuleCtx, RT_ELEMENTS(g_cbPool));
+#else /* !VBOXNETLWF_FIXED_SIZE_POOLS */
+ if (pModuleCtx->hPool)
+ {
+ NdisFreeNetBufferListPool(pModuleCtx->hPool);
+ Log4(("vboxNetLwfWinFreeModuleResources: freed NBL+NB pool 0x%p\n", pModuleCtx->hPool));
+ }
+#endif /* !VBOXNETLWF_FIXED_SIZE_POOLS */
+ if (pModuleCtx->pDisabledOffloadConfig)
+ NdisFreeMemory(pModuleCtx->pDisabledOffloadConfig, 0, 0);
+ if (pModuleCtx->pSavedOffloadConfig)
+ NdisFreeMemory(pModuleCtx->pSavedOffloadConfig, 0, 0);
+ if (pModuleCtx->hWorkItem)
+ NdisFreeIoWorkItem(pModuleCtx->hWorkItem);
+ NdisFreeMemory(pModuleCtx, 0, 0);
+}
+
+
+DECLARE_GLOBAL_CONST_UNICODE_STRING(g_strHostOnlyMiniportName, L"VirtualBox Host-Only");
+
+static NDIS_STATUS vboxNetLwfWinAttach(IN NDIS_HANDLE hFilter, IN NDIS_HANDLE hDriverCtx,
+ IN PNDIS_FILTER_ATTACH_PARAMETERS pParameters)
+{
+ LogFlow(("==>vboxNetLwfWinAttach: filter=%p\n", hFilter));
+
+ PVBOXNETLWFGLOBALS pGlobals = (PVBOXNETLWFGLOBALS)hDriverCtx;
+ if (!pGlobals)
+ {
+ vboxNetLwfLogErrorEvent(IO_ERR_INTERNAL_ERROR, NDIS_STATUS_FAILURE, 1);
+ return NDIS_STATUS_FAILURE;
+ }
+
+ /*
+ * We need a copy of NDIS_STRING structure as we are going to modify length
+ * of the base miniport instance name since RTL does not support comparing
+ * first n characters of two strings. We check if miniport names start with
+ * "Virtual Host-Only" to detect host-only adapters. It is a waste of resources
+ * to bind our filter to host-only adapters since they now operate independently.
+ */
+ NDIS_STRING strTruncatedInstanceName = *pParameters->BaseMiniportInstanceName; /* Do not copy data, only the structure itself */
+ strTruncatedInstanceName.Length = g_strHostOnlyMiniportName.Length; /* Truncate instance name */
+ if (RtlEqualUnicodeString(&strTruncatedInstanceName, &g_strHostOnlyMiniportName, TRUE /* Case insensitive */))
+ {
+ DbgPrint("vboxNetLwfWinAttach: won't attach to %wZ\n", pParameters->BaseMiniportInstanceName);
+ return NDIS_STATUS_FAILURE;
+ }
+
+ ANSI_STRING strMiniportName;
+ /* We use the miniport name to associate this filter module with the netflt instance */
+ NTSTATUS rc = RtlUnicodeStringToAnsiString(&strMiniportName,
+ pParameters->BaseMiniportName,
+ TRUE);
+ if (rc != STATUS_SUCCESS)
+ {
+ LogError(("vboxNetLwfWinAttach: RtlUnicodeStringToAnsiString(%ls) failed with 0x%x\n",
+ pParameters->BaseMiniportName, rc));
+ vboxNetLwfLogErrorEvent(IO_ERR_INTERNAL_ERROR, NDIS_STATUS_FAILURE, 2);
+ return NDIS_STATUS_FAILURE;
+ }
+ DbgPrint("vboxNetLwfWinAttach: friendly name=%wZ\n", pParameters->BaseMiniportInstanceName);
+ DbgPrint("vboxNetLwfWinAttach: name=%Z\n", &strMiniportName);
+
+ UINT cbModuleWithNameExtra = sizeof(VBOXNETLWF_MODULE) + strMiniportName.Length;
+ PVBOXNETLWF_MODULE pModuleCtx = (PVBOXNETLWF_MODULE)NdisAllocateMemoryWithTagPriority(hFilter,
+ cbModuleWithNameExtra,
+ VBOXNETLWF_MEM_TAG,
+ LowPoolPriority);
+ if (!pModuleCtx)
+ {
+ LogError(("vboxNetLwfWinAttach: Failed to allocate module context for %ls\n", pParameters->BaseMiniportName));
+ RtlFreeAnsiString(&strMiniportName);
+ vboxNetLwfLogErrorEvent(IO_ERR_INSUFFICIENT_RESOURCES, NDIS_STATUS_RESOURCES, 3);
+ return NDIS_STATUS_RESOURCES;
+ }
+ Log4(("vboxNetLwfWinAttach: allocated module context 0x%p\n", pModuleCtx));
+
+ NdisZeroMemory(pModuleCtx, cbModuleWithNameExtra);
+ NdisMoveMemory(pModuleCtx->szMiniportName, strMiniportName.Buffer, strMiniportName.Length);
+ RtlFreeAnsiString(&strMiniportName);
+
+ pModuleCtx->hWorkItem = NdisAllocateIoWorkItem(g_VBoxNetLwfGlobals.hFilterDriver);
+ if (!pModuleCtx->hWorkItem)
+ {
+ LogError(("vboxNetLwfWinAttach: Failed to allocate work item for %ls\n",
+ pParameters->BaseMiniportName));
+ NdisFreeMemory(pModuleCtx, 0, 0);
+ vboxNetLwfLogErrorEvent(IO_ERR_INSUFFICIENT_RESOURCES, NDIS_STATUS_RESOURCES, 4);
+ return NDIS_STATUS_RESOURCES;
+ }
+
+ Assert(pParameters->MacAddressLength == sizeof(RTMAC));
+ NdisMoveMemory(&pModuleCtx->MacAddr, pParameters->CurrentMacAddress, RT_MIN(sizeof(RTMAC), pParameters->MacAddressLength));
+
+ pModuleCtx->cbOffloadConfig = sizeof(NDIS_OFFLOAD) * 2; /* Best guess to accomodate future expansion. */
+ /* Get the exact size, if possible. */
+ if (pParameters->DefaultOffloadConfiguration)
+ pModuleCtx->cbOffloadConfig = pParameters->DefaultOffloadConfiguration->Header.Size;
+ else
+ vboxNetLwfLogErrorEvent(IO_ERR_INTERNAL_ERROR, STATUS_SUCCESS, 8);
+
+ pModuleCtx->pSavedOffloadConfig =
+ (PNDIS_OFFLOAD)NdisAllocateMemoryWithTagPriority(hFilter, pModuleCtx->cbOffloadConfig,
+ VBOXNETLWF_MEM_TAG, LowPoolPriority);
+ pModuleCtx->pDisabledOffloadConfig =
+ (PNDIS_OFFLOAD)NdisAllocateMemoryWithTagPriority(hFilter, pModuleCtx->cbOffloadConfig,
+ VBOXNETLWF_MEM_TAG, LowPoolPriority);
+ if (!pModuleCtx->pSavedOffloadConfig || !pModuleCtx->pDisabledOffloadConfig)
+ {
+ LogError(("vboxNetLwfWinAttach: Failed to allocate offload config buffers for %ls\n",
+ pParameters->BaseMiniportName));
+ vboxNetLwfWinFreeModuleResources(pModuleCtx);
+ vboxNetLwfLogErrorEvent(IO_ERR_INSUFFICIENT_RESOURCES, NDIS_STATUS_RESOURCES, 9);
+ return NDIS_STATUS_RESOURCES;
+ }
+
+ if (pParameters->DefaultOffloadConfiguration)
+ vboxNetLwfWinUpdateSavedOffloadConfig(pModuleCtx, pParameters->DefaultOffloadConfiguration);
+ else
+ {
+ NdisZeroMemory(pModuleCtx->pDisabledOffloadConfig, pModuleCtx->cbOffloadConfig);
+ pModuleCtx->pDisabledOffloadConfig->Header.Type = NDIS_OBJECT_TYPE_OFFLOAD;
+ pModuleCtx->pDisabledOffloadConfig->Header.Revision = NDIS_OFFLOAD_REVISION_1;
+ pModuleCtx->pDisabledOffloadConfig->Header.Size = NDIS_SIZEOF_NDIS_OFFLOAD_REVISION_1;
+ }
+
+ pModuleCtx->pGlobals = pGlobals;
+ pModuleCtx->hFilter = hFilter;
+ vboxNetLwfWinChangeState(pModuleCtx, LwfState_Attaching);
+ /* Initialize transmission mutex and events */
+ NDIS_INIT_MUTEX(&pModuleCtx->InTransmit);
+#ifdef VBOXNETLWF_SYNC_SEND
+ KeInitializeEvent(&pModuleCtx->EventWire, SynchronizationEvent, FALSE);
+ KeInitializeEvent(&pModuleCtx->EventHost, SynchronizationEvent, FALSE);
+#else /* !VBOXNETLWF_SYNC_SEND */
+ NdisInitializeEvent(&pModuleCtx->EventSendComplete);
+ pModuleCtx->cPendingBuffers = 0;
+#endif /* !VBOXNETLWF_SYNC_SEND */
+
+#ifdef VBOXNETLWF_FIXED_SIZE_POOLS
+ for (int i = 0; i < RT_ELEMENTS(g_cbPool); ++i)
+ {
+ /* Allocate buffer pools */
+ NET_BUFFER_LIST_POOL_PARAMETERS PoolParams;
+ NdisZeroMemory(&PoolParams, sizeof(PoolParams));
+ PoolParams.Header.Type = NDIS_OBJECT_TYPE_DEFAULT;
+ PoolParams.Header.Revision = NET_BUFFER_LIST_POOL_PARAMETERS_REVISION_1;
+ PoolParams.Header.Size = sizeof(PoolParams);
+ PoolParams.ProtocolId = NDIS_PROTOCOL_ID_DEFAULT;
+ PoolParams.fAllocateNetBuffer = TRUE;
+ PoolParams.ContextSize = 0; /** @todo Do we need to consider underlying drivers? I think not. */
+ PoolParams.PoolTag = VBOXNETLWF_MEM_TAG;
+ PoolParams.DataSize = g_cbPool[i];
+ pModuleCtx->hPool[i] = NdisAllocateNetBufferListPool(hFilter, &PoolParams);
+ if (!pModuleCtx->hPool[i])
+ {
+ LogError(("vboxNetLwfWinAttach: NdisAllocateNetBufferListPool failed\n"));
+ vboxNetLwfWinFreeModuleResources(pModuleCtx);
+ vboxNetLwfLogErrorEvent(IO_ERR_INSUFFICIENT_RESOURCES, NDIS_STATUS_RESOURCES, 7);
+ return NDIS_STATUS_RESOURCES;
+ }
+ Log4(("vboxNetLwfWinAttach: allocated NBL+NB pool (data size=%u) 0x%p\n",
+ PoolParams.DataSize, pModuleCtx->hPool[i]));
+ }
+#else /* !VBOXNETLWF_FIXED_SIZE_POOLS */
+ /* Allocate buffer pools */
+ NET_BUFFER_LIST_POOL_PARAMETERS PoolParams;
+ NdisZeroMemory(&PoolParams, sizeof(PoolParams));
+ PoolParams.Header.Type = NDIS_OBJECT_TYPE_DEFAULT;
+ PoolParams.Header.Revision = NET_BUFFER_LIST_POOL_PARAMETERS_REVISION_1;
+ PoolParams.Header.Size = sizeof(PoolParams);
+ PoolParams.ProtocolId = NDIS_PROTOCOL_ID_DEFAULT;
+ PoolParams.fAllocateNetBuffer = TRUE;
+ PoolParams.ContextSize = 0; /** @todo Do we need to consider underlying drivers? I think not. */
+ PoolParams.PoolTag = VBOXNETLWF_MEM_TAG;
+ pModuleCtx->hPool = NdisAllocateNetBufferListPool(hFilter, &PoolParams);
+ if (!pModuleCtx->hPool)
+ {
+ LogError(("vboxNetLwfWinAttach: NdisAllocateNetBufferListPool failed\n"));
+ vboxNetLwfWinFreeModuleResources(pModuleCtx);
+ vboxNetLwfLogErrorEvent(IO_ERR_INSUFFICIENT_RESOURCES, NDIS_STATUS_RESOURCES, 7);
+ return NDIS_STATUS_RESOURCES;
+ }
+ Log4(("vboxNetLwfWinAttach: allocated NBL+NB pool 0x%p\n", pModuleCtx->hPool));
+#endif /* !VBOXNETLWF_FIXED_SIZE_POOLS */
+
+ NDIS_FILTER_ATTRIBUTES Attributes;
+ NdisZeroMemory(&Attributes, sizeof(Attributes));
+ Attributes.Header.Revision = NDIS_FILTER_ATTRIBUTES_REVISION_1;
+ Attributes.Header.Size = sizeof(Attributes);
+ Attributes.Header.Type = NDIS_OBJECT_TYPE_FILTER_ATTRIBUTES;
+ Attributes.Flags = 0;
+ NDIS_STATUS Status = NdisFSetAttributes(hFilter, pModuleCtx, &Attributes);
+ if (Status != NDIS_STATUS_SUCCESS)
+ {
+ LogError(("vboxNetLwfWinAttach: NdisFSetAttributes failed with 0x%x\n", Status));
+ vboxNetLwfWinFreeModuleResources(pModuleCtx);
+ vboxNetLwfLogErrorEvent(IO_ERR_INTERNAL_ERROR, NDIS_STATUS_RESOURCES, 5);
+ return NDIS_STATUS_RESOURCES;
+ }
+ /* Insert into module chain */
+ NdisAcquireSpinLock(&pGlobals->Lock);
+ RTListPrepend(&pGlobals->listModules, &pModuleCtx->node);
+ NdisReleaseSpinLock(&pGlobals->Lock);
+
+ vboxNetLwfWinChangeState(pModuleCtx, LwfState_Paused);
+
+ /// @todo Somehow the packet filter is 0 at this point: Status = vboxNetLwfWinGetPacketFilter(pModuleCtx);
+ /// @todo We actually update it later in status handler, perhaps we should not do anything here.
+
+ LogFlow(("<==vboxNetLwfWinAttach: Status = 0x%x\n", Status));
+ return Status;
+}
+
+static VOID vboxNetLwfWinDetach(IN NDIS_HANDLE hModuleCtx)
+{
+ LogFlow(("==>vboxNetLwfWinDetach: module=%p\n", hModuleCtx));
+ PVBOXNETLWF_MODULE pModuleCtx = (PVBOXNETLWF_MODULE)hModuleCtx;
+ vboxNetLwfWinChangeState(pModuleCtx, LwfState_Detached, LwfState_Paused);
+
+ /* Remove from module chain */
+ NdisAcquireSpinLock(&pModuleCtx->pGlobals->Lock);
+ RTListNodeRemove(&pModuleCtx->node);
+ NdisReleaseSpinLock(&pModuleCtx->pGlobals->Lock);
+
+ PVBOXNETFLTINS pNetFltIns = pModuleCtx->pNetFlt; /// @todo Atomic?
+ if (pNetFltIns && vboxNetFltTryRetainBusyNotDisconnected(pNetFltIns))
+ {
+ /*
+ * Set hModuleCtx to null now in order to prevent filter restart,
+ * OID requests and other stuff associated with NetFlt deactivation.
+ */
+ pNetFltIns->u.s.WinIf.hModuleCtx = NULL;
+ /* Notify NetFlt that we are going down */
+ pNetFltIns->pSwitchPort->pfnDisconnect(pNetFltIns->pSwitchPort, &pNetFltIns->MyPort, vboxNetFltPortReleaseBusy);
+ /* We do not 'release' netflt instance since it has been done by pfnDisconnect */
+ }
+ pModuleCtx->pNetFlt = NULL;
+
+ /*
+ * We have to make sure that all NET_BUFFER_LIST structures have been freed by now, but
+ * it does not require us to do anything here since it has already been taken care of
+ * by vboxNetLwfWinPause().
+ */
+ vboxNetLwfWinFreeModuleResources(pModuleCtx);
+ Log4(("vboxNetLwfWinDetach: freed module context 0x%p\n", pModuleCtx));
+ LogFlow(("<==vboxNetLwfWinDetach\n"));
+}
+
+
+static NDIS_STATUS vboxNetLwfWinPause(IN NDIS_HANDLE hModuleCtx, IN PNDIS_FILTER_PAUSE_PARAMETERS pParameters)
+{
+ RT_NOREF1(pParameters);
+ LogFlow(("==>vboxNetLwfWinPause: module=%p\n", hModuleCtx));
+ PVBOXNETLWF_MODULE pModuleCtx = (PVBOXNETLWF_MODULE)hModuleCtx;
+ vboxNetLwfWinChangeState(pModuleCtx, LwfState_Pausing, LwfState_Running);
+ /* Wait for pending send/indication operations to complete. */
+ NDIS_WAIT_FOR_MUTEX(&pModuleCtx->InTransmit);
+#ifndef VBOXNETLWF_SYNC_SEND
+ NdisWaitEvent(&pModuleCtx->EventSendComplete, 1000 /* ms */);
+#endif /* !VBOXNETLWF_SYNC_SEND */
+ vboxNetLwfWinChangeState(pModuleCtx, LwfState_Paused, LwfState_Pausing);
+ NDIS_RELEASE_MUTEX(&pModuleCtx->InTransmit);
+ LogFlow(("<==vboxNetLwfWinPause\n"));
+ return NDIS_STATUS_SUCCESS; /* Failure is not an option */
+}
+
+
+static void vboxNetLwfWinIndicateOffload(PVBOXNETLWF_MODULE pModuleCtx, PNDIS_OFFLOAD pOffload)
+{
+ Log5(("vboxNetLwfWinIndicateOffload: offload config changed to:\n"));
+ vboxNetLwfWinDumpOffloadSettings(pOffload);
+ NDIS_STATUS_INDICATION OffloadingIndication;
+ NdisZeroMemory(&OffloadingIndication, sizeof(OffloadingIndication));
+ OffloadingIndication.Header.Type = NDIS_OBJECT_TYPE_STATUS_INDICATION;
+ OffloadingIndication.Header.Revision = NDIS_STATUS_INDICATION_REVISION_1;
+ OffloadingIndication.Header.Size = NDIS_SIZEOF_STATUS_INDICATION_REVISION_1;
+ OffloadingIndication.SourceHandle = pModuleCtx->hFilter;
+ OffloadingIndication.StatusCode = NDIS_STATUS_TASK_OFFLOAD_CURRENT_CONFIG;
+ OffloadingIndication.StatusBuffer = pOffload;
+ OffloadingIndication.StatusBufferSize = pOffload->Header.Size;
+ NdisFIndicateStatus(pModuleCtx->hFilter, &OffloadingIndication);
+}
+
+
+static NDIS_STATUS vboxNetLwfWinRestart(IN NDIS_HANDLE hModuleCtx, IN PNDIS_FILTER_RESTART_PARAMETERS pParameters)
+{
+ RT_NOREF1(pParameters);
+ LogFlow(("==>vboxNetLwfWinRestart: module=%p\n", hModuleCtx));
+ PVBOXNETLWF_MODULE pModuleCtx = (PVBOXNETLWF_MODULE)hModuleCtx;
+ vboxNetLwfWinChangeState(pModuleCtx, LwfState_Restarting, LwfState_Paused);
+
+ /* By default the packets that go between VMs and wire are invisible to the host. */
+ pModuleCtx->fPassVmTrafficToHost = false;
+
+ NDIS_HANDLE hConfig;
+ NDIS_CONFIGURATION_OBJECT cfgObj;
+ cfgObj.Header.Type = NDIS_OBJECT_TYPE_CONFIGURATION_OBJECT;
+ cfgObj.Header.Revision = NDIS_CONFIGURATION_OBJECT_REVISION_1;
+ cfgObj.Header.Size = sizeof(NDIS_CONFIGURATION_OBJECT);
+ cfgObj.NdisHandle = g_VBoxNetLwfGlobals.hFilterDriver;
+
+ NDIS_STATUS Status = NdisOpenConfigurationEx(&cfgObj, &hConfig);
+ if (Status == NDIS_STATUS_SUCCESS)
+ {
+ NDIS_STRING strCfgParam = NDIS_STRING_CONST("PassVmTrafficToHost");
+ PNDIS_CONFIGURATION_PARAMETER pParam = NULL;
+ NdisReadConfiguration(&Status, &pParam, hConfig, &strCfgParam, NdisParameterInteger);
+ if (Status != NDIS_STATUS_SUCCESS)
+ {
+ Log(("vboxNetLwfWinRestart: Failed to read 'PassVmTrafficToHost' from the registry.\n"));
+ }
+ else if (pParam->ParameterData.IntegerData != 0)
+ {
+ Log(("vboxNetLwfWinRestart: Allowing the host to see VM traffic in promisc mode by user request.\n"));
+ pModuleCtx->fPassVmTrafficToHost = true;
+ }
+ NdisCloseConfiguration(hConfig);
+ }
+ vboxNetLwfWinChangeState(pModuleCtx, LwfState_Running, LwfState_Restarting);
+ LogFlow(("<==vboxNetLwfWinRestart: Status = 0x%x, returning NDIS_STATUS_SUCCESS nontheless.\n", Status));
+ return NDIS_STATUS_SUCCESS;
+}
+
+
+static void vboxNetLwfWinDestroySG(PINTNETSG pSG)
+{
+ NdisFreeMemory(pSG, 0, 0);
+ Log4(("vboxNetLwfWinDestroySG: freed SG 0x%p\n", pSG));
+}
+
+/**
+ * Worker for vboxNetLwfWinNBtoSG() that gets the max segment count needed.
+ * @note vboxNetLwfWinNBtoSG may use fewer depending on cbPacket and offset!
+ * @note vboxNetAdpWinCalcSegments() is a copy of this code.
+ */
+DECLINLINE(ULONG) vboxNetLwfWinCalcSegments(PNET_BUFFER pNetBuf)
+{
+ ULONG cSegs = 0;
+ for (PMDL pMdl = NET_BUFFER_CURRENT_MDL(pNetBuf); pMdl; pMdl = NDIS_MDL_LINKAGE(pMdl))
+ {
+ /* Skip empty MDLs (see @bugref{9233}) */
+ if (MmGetMdlByteCount(pMdl))
+ cSegs++;
+ }
+ return cSegs;
+}
+
+DECLINLINE(void) vboxNetLwfWinFreeMdlChain(PMDL pMdl)
+{
+#ifndef VBOXNETLWF_FIXED_SIZE_POOLS
+ PMDL pMdlNext;
+ while (pMdl)
+ {
+ pMdlNext = pMdl->Next;
+# ifndef VBOXNETLWF_SYNC_SEND
+ PUCHAR pDataBuf;
+ ULONG cb = 0;
+ NdisQueryMdl(pMdl, &pDataBuf, &cb, NormalPagePriority);
+# endif /* !VBOXNETLWF_SYNC_SEND */
+ NdisFreeMdl(pMdl);
+ Log4(("vboxNetLwfWinFreeMdlChain: freed MDL 0x%p\n", pMdl));
+# ifndef VBOXNETLWF_SYNC_SEND
+ NdisFreeMemory(pDataBuf, 0, 0);
+ Log4(("vboxNetLwfWinFreeMdlChain: freed data buffer 0x%p\n", pDataBuf));
+# endif /* !VBOXNETLWF_SYNC_SEND */
+ pMdl = pMdlNext;
+ }
+#else /* VBOXNETLWF_FIXED_SIZE_POOLS */
+ RT_NOREF1(pMdl);
+#endif /* VBOXNETLWF_FIXED_SIZE_POOLS */
+}
+
+/** @todo
+ * 1) Copy data from SG to MDL (if we decide to complete asynchronously).
+ * 2) Provide context/backfill space. Nobody does it, should we?
+ * 3) We always get a single segment from intnet. Simplify?
+ */
+static PNET_BUFFER_LIST vboxNetLwfWinSGtoNB(PVBOXNETLWF_MODULE pModule, PINTNETSG pSG)
+{
+ AssertReturn(pSG->cSegsUsed >= 1, NULL);
+ LogFlow(("==>vboxNetLwfWinSGtoNB: segments=%d hPool=%p cb=%u\n", pSG->cSegsUsed,
+ pModule->hPool, pSG->cbTotal));
+ AssertReturn(pModule->hPool, NULL);
+
+#ifdef VBOXNETLWF_SYNC_SEND
+ PINTNETSEG pSeg = pSG->aSegs;
+ PMDL pMdl = NdisAllocateMdl(pModule->hFilter, pSeg->pv, pSeg->cb);
+ if (!pMdl)
+ {
+ LogError(("vboxNetLwfWinSGtoNB: failed to allocate an MDL\n"));
+ LogFlow(("<==vboxNetLwfWinSGtoNB: return NULL\n"));
+ return NULL;
+ }
+ Log4(("vboxNetLwfWinSGtoNB: allocated Mdl 0x%p\n", pMdl));
+ PMDL pMdlCurr = pMdl;
+ for (int i = 1; i < pSG->cSegsUsed; i++)
+ {
+ pSeg = &pSG->aSegs[i];
+ pMdlCurr->Next = NdisAllocateMdl(pModule->hFilter, pSeg->pv, pSeg->cb);
+ if (!pMdlCurr->Next)
+ {
+ LogError(("vboxNetLwfWinSGtoNB: failed to allocate an MDL\n"));
+ /* Tear down all MDL we chained so far */
+ vboxNetLwfWinFreeMdlChain(pMdl);
+ return NULL;
+ }
+ pMdlCurr = pMdlCurr->Next;
+ Log4(("vboxNetLwfWinSGtoNB: allocated Mdl 0x%p\n", pMdlCurr));
+ }
+ PNET_BUFFER_LIST pBufList = NdisAllocateNetBufferAndNetBufferList(pModule->hPool,
+ 0 /* ContextSize */,
+ 0 /* ContextBackFill */,
+ pMdl,
+ 0 /* DataOffset */,
+ pSG->cbTotal);
+ if (pBufList)
+ {
+ Log4(("vboxNetLwfWinSGtoNB: allocated NBL+NB 0x%p\n", pBufList));
+ pBufList->SourceHandle = pModule->hFilter;
+ /** @todo Do we need to initialize anything else? */
+ }
+ else
+ {
+ LogError(("vboxNetLwfWinSGtoNB: failed to allocate an NBL+NB\n"));
+ vboxNetLwfWinFreeMdlChain(pMdl);
+ }
+#else /* !VBOXNETLWF_SYNC_SEND */
+
+# ifdef VBOXNETLWF_FIXED_SIZE_POOLS
+ int iPool = 0;
+ ULONG cbFrame = VBOXNETLWF_MAX_FRAME_SIZE(pSG->cbTotal);
+ /* Let's find the appropriate pool first */
+ for (iPool = 0; iPool < RT_ELEMENTS(g_cbPool); ++iPool)
+ if (cbFrame <= g_cbPool[iPool])
+ break;
+ if (iPool >= RT_ELEMENTS(g_cbPool))
+ {
+ LogError(("vboxNetLwfWinSGtoNB: frame is too big (%u > %u), drop it.\n", cbFrame, g_cbPool[RT_ELEMENTS(g_cbPool)-1]));
+ LogFlow(("<==vboxNetLwfWinSGtoNB: return NULL\n"));
+ return NULL;
+ }
+ PNET_BUFFER_LIST pBufList = NdisAllocateNetBufferList(pModule->hPool[iPool],
+ 0 /** @todo ContextSize */,
+ 0 /** @todo ContextBackFill */);
+ if (!pBufList)
+ {
+ LogError(("vboxNetLwfWinSGtoNB: failed to allocate netbuffer (cb=%u) from pool %d\n", cbFrame, iPool));
+ LogFlow(("<==vboxNetLwfWinSGtoNB: return NULL\n"));
+ return NULL;
+ }
+ const ULONG cbAlignmentMask = sizeof(USHORT) - 1; /* Microsoft LB/FO provider expects packets to be aligned at word boundary. */
+ ULONG cbAlignedFrame = (pSG->cbTotal + cbAlignmentMask) & ~cbAlignmentMask;
+ Assert(cbAlignedFrame >= pSG->cbTotal);
+ Assert(cbFrame >= cbAlignedFrame);
+ NET_BUFFER *pBuffer = NET_BUFFER_LIST_FIRST_NB(pBufList);
+ NDIS_STATUS Status = NdisRetreatNetBufferDataStart(pBuffer, cbAlignedFrame, 0 /** @todo DataBackfill */, NULL);
+ if (cbAlignedFrame - pSG->cbTotal > 0)
+ {
+ /* Make sure padding zeros do not get to the wire. */
+ if (NET_BUFFER_DATA_LENGTH(pBuffer) != cbAlignedFrame)
+ vboxNetLwfLogErrorEvent(IO_ERR_INTERNAL_ERROR, STATUS_SUCCESS, 11);
+ else
+ NET_BUFFER_DATA_LENGTH(pBuffer) = pSG->cbTotal;
+ }
+ if (Status == NDIS_STATUS_SUCCESS)
+ {
+ uint8_t *pDst = (uint8_t*)NdisGetDataBuffer(pBuffer, pSG->cbTotal, NULL, 1, 0);
+ if (pDst)
+ {
+ for (int i = 0; i < pSG->cSegsUsed; i++)
+ {
+ NdisMoveMemory(pDst, pSG->aSegs[i].pv, pSG->aSegs[i].cb);
+ pDst += pSG->aSegs[i].cb;
+ }
+ Log4(("vboxNetLwfWinSGtoNB: allocated NBL+NB 0x%p\n", pBufList));
+ pBufList->SourceHandle = pModule->hFilter;
+ }
+ else
+ {
+ LogError(("vboxNetLwfWinSGtoNB: failed to obtain the buffer pointer (size=%u)\n", pSG->cbTotal));
+ NdisAdvanceNetBufferDataStart(pBuffer, cbAlignedFrame, false, NULL); /** @todo why bother? */
+ NdisFreeNetBufferList(pBufList);
+ pBufList = NULL;
+ }
+ }
+ else
+ {
+ LogError(("vboxNetLwfWinSGtoNB: NdisRetreatNetBufferDataStart failed with 0x%x (size=%u)\n", Status, pSG->cbTotal));
+ NdisFreeNetBufferList(pBufList);
+ pBufList = NULL;
+ }
+# else /* !VBOXNETLWF_FIXED_SIZE_POOLS */
+ PNET_BUFFER_LIST pBufList = NULL;
+ ULONG cbMdl = VBOXNETLWF_MAX_FRAME_SIZE(pSG->cbTotal);
+ ULONG uDataOffset = cbMdl - pSG->cbTotal;
+ PUCHAR pDataBuf = (PUCHAR)NdisAllocateMemoryWithTagPriority(pModule->hFilter, cbMdl,
+ VBOXNETLWF_MEM_TAG, NormalPoolPriority);
+ if (pDataBuf)
+ {
+ Log4(("vboxNetLwfWinSGtoNB: allocated data buffer (cb=%u) 0x%p\n", cbMdl, pDataBuf));
+ PMDL pMdl = NdisAllocateMdl(pModule->hFilter, pDataBuf, cbMdl);
+ if (!pMdl)
+ {
+ NdisFreeMemory(pDataBuf, 0, 0);
+ Log4(("vboxNetLwfWinSGtoNB: freed data buffer 0x%p\n", pDataBuf));
+ LogError(("vboxNetLwfWinSGtoNB: failed to allocate an MDL (cb=%u)\n", cbMdl));
+ LogFlow(("<==vboxNetLwfWinSGtoNB: return NULL\n"));
+ return NULL;
+ }
+ PUCHAR pDst = pDataBuf + uDataOffset;
+ for (int i = 0; i < pSG->cSegsUsed; i++)
+ {
+ NdisMoveMemory(pDst, pSG->aSegs[i].pv, pSG->aSegs[i].cb);
+ pDst += pSG->aSegs[i].cb;
+ }
+ pBufList = NdisAllocateNetBufferAndNetBufferList(pModule->hPool,
+ 0 /* ContextSize */,
+ 0 /* ContextBackFill */,
+ pMdl,
+ uDataOffset,
+ pSG->cbTotal);
+ if (pBufList)
+ {
+ Log4(("vboxNetLwfWinSGtoNB: allocated NBL+NB 0x%p\n", pBufList));
+ pBufList->SourceHandle = pModule->hFilter;
+ /** @todo Do we need to initialize anything else? */
+ }
+ else
+ {
+ LogError(("vboxNetLwfWinSGtoNB: failed to allocate an NBL+NB\n"));
+ vboxNetLwfWinFreeMdlChain(pMdl);
+ }
+ }
+ else
+ {
+ LogError(("vboxNetLwfWinSGtoNB: failed to allocate data buffer (size=%u)\n", cbMdl));
+ }
+# endif /* !VBOXNETLWF_FIXED_SIZE_POOLS */
+
+#endif /* !VBOXNETLWF_SYNC_SEND */
+ LogFlow(("<==vboxNetLwfWinSGtoNB: return %p\n", pBufList));
+ return pBufList;
+}
+
+/**
+ * @note vboxNetAdpWinNBtoSG() is a copy of this code.
+ */
+static PINTNETSG vboxNetLwfWinNBtoSG(PVBOXNETLWF_MODULE pModule, PNET_BUFFER pNetBuf)
+{
+ ULONG cbPacket = NET_BUFFER_DATA_LENGTH(pNetBuf);
+ ULONG cSegs = vboxNetLwfWinCalcSegments(pNetBuf);
+ /* Allocate and initialize SG */
+ PINTNETSG pSG = (PINTNETSG)NdisAllocateMemoryWithTagPriority(pModule->hFilter,
+ RT_UOFFSETOF_DYN(INTNETSG, aSegs[cSegs]),
+ VBOXNETLWF_MEM_TAG,
+ NormalPoolPriority);
+ AssertReturn(pSG, pSG);
+ Log4(("vboxNetLwfWinNBtoSG: allocated SG 0x%p\n", pSG));
+ IntNetSgInitTempSegs(pSG, cbPacket /*cbTotal*/, cSegs, cSegs /*cSegsUsed*/);
+
+ ULONG uOffset = NET_BUFFER_CURRENT_MDL_OFFSET(pNetBuf);
+ cSegs = 0;
+ for (PMDL pMdl = NET_BUFFER_CURRENT_MDL(pNetBuf);
+ pMdl != NULL && cbPacket > 0;
+ pMdl = NDIS_MDL_LINKAGE(pMdl))
+ {
+ ULONG cbSrc = MmGetMdlByteCount(pMdl);
+ if (cbSrc == 0)
+ continue; /* Skip empty MDLs (see @bugref{9233}) */
+
+ PUCHAR pSrc = (PUCHAR)MmGetSystemAddressForMdlSafe(pMdl, LowPagePriority);
+ if (!pSrc)
+ {
+ vboxNetLwfWinDestroySG(pSG);
+ return NULL;
+ }
+
+ /* Handle the offset in the current (which is the first for us) MDL */
+ if (uOffset)
+ {
+ if (uOffset < cbSrc)
+ {
+ pSrc += uOffset;
+ cbSrc -= uOffset;
+ uOffset = 0;
+ }
+ else
+ {
+ /* This is an invalid MDL chain */
+ vboxNetLwfWinDestroySG(pSG);
+ return NULL;
+ }
+ }
+
+ /* Do not read the last MDL beyond packet's end */
+ if (cbSrc > cbPacket)
+ cbSrc = cbPacket;
+
+ Assert(cSegs < pSG->cSegsAlloc);
+ pSG->aSegs[cSegs].pv = pSrc;
+ pSG->aSegs[cSegs].cb = cbSrc;
+ pSG->aSegs[cSegs].Phys = NIL_RTHCPHYS;
+ cSegs++;
+ cbPacket -= cbSrc;
+ }
+
+ Assert(cbPacket == 0);
+ Assert(cSegs <= pSG->cSegsUsed);
+
+ /* Update actual segment count in case we used fewer than anticipated. */
+ pSG->cSegsUsed = (uint16_t)cSegs;
+
+ return pSG;
+}
+
+VOID vboxNetLwfWinStatus(IN NDIS_HANDLE hModuleCtx, IN PNDIS_STATUS_INDICATION pIndication)
+{
+ LogFlow(("==>vboxNetLwfWinStatus: module=%p\n", hModuleCtx));
+ PVBOXNETLWF_MODULE pModuleCtx = (PVBOXNETLWF_MODULE)hModuleCtx;
+ Log(("vboxNetLwfWinStatus: Got status indication: %s\n", vboxNetLwfWinStatusToText(pIndication->StatusCode)));
+ switch (pIndication->StatusCode)
+ {
+ case NDIS_STATUS_PACKET_FILTER:
+ vboxNetLwfWinDumpFilterTypes(*(ULONG*)pIndication->StatusBuffer);
+ vboxNetLwfWinOverridePacketFiltersUp(pModuleCtx, (ULONG*)pIndication->StatusBuffer);
+ Log(("vboxNetLwfWinStatus: Reporting status: %s\n", vboxNetLwfWinStatusToText(pIndication->StatusCode)));
+ vboxNetLwfWinDumpFilterTypes(*(ULONG*)pIndication->StatusBuffer);
+ break;
+ case NDIS_STATUS_TASK_OFFLOAD_CURRENT_CONFIG:
+ Log5(("vboxNetLwfWinStatus: offloading currently set to:\n"));
+ vboxNetLwfWinDumpOffloadSettings((PNDIS_OFFLOAD)pIndication->StatusBuffer);
+ vboxNetLwfWinUpdateSavedOffloadConfig(pModuleCtx, (PNDIS_OFFLOAD)pIndication->StatusBuffer);
+ if (ASMAtomicReadBool(&pModuleCtx->fActive))
+ vboxNetLwfWinDisableOffloading((PNDIS_OFFLOAD)pIndication->StatusBuffer);
+ Log5(("vboxNetLwfWinStatus: reporting offloading up as:\n"));
+ vboxNetLwfWinDumpOffloadSettings((PNDIS_OFFLOAD)pIndication->StatusBuffer);
+ break;
+ }
+ NdisFIndicateStatus(pModuleCtx->hFilter, pIndication);
+ LogFlow(("<==vboxNetLwfWinStatus\n"));
+}
+
+static bool vboxNetLwfWinForwardToIntNet(PVBOXNETLWF_MODULE pModuleCtx, PNET_BUFFER_LIST pBufLists, uint32_t fSrc)
+{
+ /* We must not forward anything to the trunk unless it is ready to receive. */
+ if (!ASMAtomicReadBool(&pModuleCtx->fActive))
+ {
+ Log(("vboxNetLwfWinForwardToIntNet: trunk is inactive, won't forward\n"));
+ return false;
+ }
+ /* Some NPF protocols make NDIS to loop back packets at miniport level, we must ignore those. */
+ if (NdisTestNblFlag(pBufLists, NDIS_NBL_FLAGS_IS_LOOPBACK_PACKET))
+ {
+ if (pBufLists->SourceHandle == pModuleCtx->hFilter && !pModuleCtx->fPassVmTrafficToHost)
+ {
+ /* Drop the packets we've injected. */
+ vboxNetLwfWinDumpPackets("vboxNetLwfWinForwardToIntNet: dropping loopback", pBufLists);
+ return true;
+ }
+ vboxNetLwfWinDumpPackets("vboxNetLwfWinForwardToIntNet: passing through loopback", pBufLists);
+ return false;
+ }
+
+ AssertReturn(pModuleCtx->pNetFlt, false);
+ AssertReturn(pModuleCtx->pNetFlt->pSwitchPort, false);
+ AssertReturn(pModuleCtx->pNetFlt->pSwitchPort->pfnRecv, false);
+ LogFlow(("==>vboxNetLwfWinForwardToIntNet: module=%p\n", pModuleCtx));
+ Assert(pBufLists); /* The chain must contain at least one list */
+ Assert(NET_BUFFER_LIST_NEXT_NBL(pBufLists) == NULL); /* The caller is supposed to unlink the list from the chain */
+ /*
+ * Even if NBL contains more than one buffer we are prepared to deal with it.
+ * When any of buffers should not be dropped we keep the whole list. It is
+ * better to leak some "unexpected" packets to the wire/host than to loose any.
+ */
+ bool fDropIt = false;
+ bool fDontDrop = false;
+ int nLists = 0;
+ for (PNET_BUFFER_LIST pList = pBufLists; pList; pList = NET_BUFFER_LIST_NEXT_NBL(pList))
+ {
+ int nBuffers = 0;
+ nLists++;
+ for (PNET_BUFFER pBuf = NET_BUFFER_LIST_FIRST_NB(pList); pBuf; pBuf = NET_BUFFER_NEXT_NB(pBuf))
+ {
+ nBuffers++;
+ PINTNETSG pSG = vboxNetLwfWinNBtoSG(pModuleCtx, pBuf);
+ if (pSG)
+ {
+ vboxNetLwfWinDumpPacket(pSG, (fSrc & INTNETTRUNKDIR_WIRE)?"intnet <-- wire":"intnet <-- host");
+ /* A bit paranoid, but we do not use any locks, so... */
+ if (ASMAtomicReadBool(&pModuleCtx->fActive))
+ if (pModuleCtx->pNetFlt->pSwitchPort->pfnRecv(pModuleCtx->pNetFlt->pSwitchPort, NULL, pSG, fSrc))
+ fDropIt = true;
+ else
+ fDontDrop = true;
+ vboxNetLwfWinDestroySG(pSG);
+ }
+ }
+ Log(("vboxNetLwfWinForwardToIntNet: list=%d buffers=%d\n", nLists, nBuffers));
+ }
+ Log(("vboxNetLwfWinForwardToIntNet: lists=%d drop=%s don't=%s\n", nLists, fDropIt ? "true":"false", fDontDrop ? "true":"false"));
+
+ /* If the host (and the user) wants to see all packets we must not drop any. */
+ if (pModuleCtx->fPassVmTrafficToHost && vboxNetLwfWinIsPromiscuous(pModuleCtx))
+ fDropIt = false;
+
+ LogFlow(("<==vboxNetLwfWinForwardToIntNet: return '%s'\n",
+ fDropIt ? (fDontDrop ? "do not drop (some)" : "drop it") : "do not drop (any)"));
+ return fDropIt && !fDontDrop; /* Drop the list if ALL its buffers are being dropped! */
+}
+
+DECLINLINE(bool) vboxNetLwfWinIsRunning(PVBOXNETLWF_MODULE pModule)
+{
+ Log(("vboxNetLwfWinIsRunning: state=%d\n", ASMAtomicReadU32(&pModule->enmState)));
+ return ASMAtomicReadU32(&pModule->enmState) == LwfState_Running;
+}
+
+VOID vboxNetLwfWinSendNetBufferLists(IN NDIS_HANDLE hModuleCtx, IN PNET_BUFFER_LIST pBufLists, IN NDIS_PORT_NUMBER nPort, IN ULONG fFlags)
+{
+ LogFlow(("==>vboxNetLwfWinSendNetBufferLists: module=%p\n", hModuleCtx));
+ PVBOXNETLWF_MODULE pModule = (PVBOXNETLWF_MODULE)hModuleCtx;
+ vboxNetLwfWinDumpPackets("vboxNetLwfWinSendNetBufferLists: got", pBufLists);
+
+ if (!ASMAtomicReadBool(&pModule->fActive))
+ {
+ /*
+ * The trunk is inactive, jusp pass along all packets to the next
+ * underlying driver.
+ */
+ NdisFSendNetBufferLists(pModule->hFilter, pBufLists, nPort, fFlags);
+ return;
+ }
+
+ if (vboxNetLwfWinIsRunning(pModule))
+ {
+ PNET_BUFFER_LIST pNext = NULL;
+ PNET_BUFFER_LIST pDropHead = NULL;
+ PNET_BUFFER_LIST pDropTail = NULL;
+ PNET_BUFFER_LIST pPassHead = NULL;
+ PNET_BUFFER_LIST pPassTail = NULL;
+ for (PNET_BUFFER_LIST pList = pBufLists; pList; pList = pNext)
+ {
+ pNext = NET_BUFFER_LIST_NEXT_NBL(pList);
+ NET_BUFFER_LIST_NEXT_NBL(pList) = NULL; /* Unlink */
+ if (vboxNetLwfWinForwardToIntNet(pModule, pList, INTNETTRUNKDIR_HOST))
+ {
+ NET_BUFFER_LIST_STATUS(pList) = NDIS_STATUS_SUCCESS;
+ if (pDropHead)
+ {
+ NET_BUFFER_LIST_NEXT_NBL(pDropTail) = pList;
+ pDropTail = pList;
+ }
+ else
+ pDropHead = pDropTail = pList;
+ }
+ else
+ {
+ if (pPassHead)
+ {
+ NET_BUFFER_LIST_NEXT_NBL(pPassTail) = pList;
+ pPassTail = pList;
+ }
+ else
+ pPassHead = pPassTail = pList;
+ }
+ }
+ Assert((pBufLists == pPassHead) || (pBufLists == pDropHead));
+ if (pPassHead)
+ {
+ vboxNetLwfWinDumpPackets("vboxNetLwfWinSendNetBufferLists: passing down", pPassHead);
+ NdisFSendNetBufferLists(pModule->hFilter, pBufLists, nPort, fFlags);
+ }
+ if (pDropHead)
+ {
+ vboxNetLwfWinDumpPackets("vboxNetLwfWinSendNetBufferLists: consumed", pDropHead);
+ NdisFSendNetBufferListsComplete(pModule->hFilter, pDropHead,
+ fFlags & NDIS_SEND_FLAGS_DISPATCH_LEVEL ? NDIS_SEND_COMPLETE_FLAGS_DISPATCH_LEVEL : 0);
+ }
+ }
+ else
+ {
+ for (PNET_BUFFER_LIST pList = pBufLists; pList; pList = NET_BUFFER_LIST_NEXT_NBL(pList))
+ {
+ NET_BUFFER_LIST_STATUS(pList) = NDIS_STATUS_PAUSED;
+ }
+ vboxNetLwfWinDumpPackets("vboxNetLwfWinSendNetBufferLists: consumed", pBufLists);
+ NdisFSendNetBufferListsComplete(pModule->hFilter, pBufLists,
+ fFlags & NDIS_SEND_FLAGS_DISPATCH_LEVEL ? NDIS_SEND_COMPLETE_FLAGS_DISPATCH_LEVEL : 0);
+
+ }
+ LogFlow(("<==vboxNetLwfWinSendNetBufferLists\n"));
+}
+
+VOID vboxNetLwfWinSendNetBufferListsComplete(IN NDIS_HANDLE hModuleCtx, IN PNET_BUFFER_LIST pBufLists, IN ULONG fFlags)
+{
+ LogFlow(("==>vboxNetLwfWinSendNetBufferListsComplete: module=%p\n", hModuleCtx));
+ PVBOXNETLWF_MODULE pModule = (PVBOXNETLWF_MODULE)hModuleCtx;
+ PNET_BUFFER_LIST pList = pBufLists;
+ PNET_BUFFER_LIST pNextList;
+ PNET_BUFFER_LIST pPrevList = NULL;
+ while (pList)
+ {
+ pNextList = NET_BUFFER_LIST_NEXT_NBL(pList);
+ if (pList->SourceHandle == pModule->hFilter)
+ {
+ /* We allocated this NET_BUFFER_LIST, let's free it up */
+ Assert(NET_BUFFER_LIST_FIRST_NB(pList));
+ Assert(NET_BUFFER_FIRST_MDL(NET_BUFFER_LIST_FIRST_NB(pList)));
+ /*
+ * All our NBLs hold a single NB each, no need to iterate over a list.
+ * There is no need to free an associated NB explicitly either, as it was
+ * preallocated with NBL structure.
+ */
+ Assert(!NET_BUFFER_NEXT_NB(NET_BUFFER_LIST_FIRST_NB(pList)));
+ vboxNetLwfWinFreeMdlChain(NET_BUFFER_FIRST_MDL(NET_BUFFER_LIST_FIRST_NB(pList)));
+ /* Unlink this list from the chain */
+ if (pPrevList)
+ NET_BUFFER_LIST_NEXT_NBL(pPrevList) = pNextList;
+ else
+ pBufLists = pNextList;
+ Log(("vboxNetLwfWinSendNetBufferListsComplete: our list %p, next=%p, previous=%p, head=%p\n", pList, pNextList, pPrevList, pBufLists));
+ NdisFreeNetBufferList(pList);
+#ifdef VBOXNETLWF_SYNC_SEND
+ Log4(("vboxNetLwfWinSendNetBufferListsComplete: freed NBL+NB 0x%p\n", pList));
+ KeSetEvent(&pModule->EventWire, 0, FALSE);
+#else /* !VBOXNETLWF_SYNC_SEND */
+ Log4(("vboxNetLwfWinSendNetBufferListsComplete: freed NBL+NB+MDL+Data 0x%p\n", pList));
+ Assert(ASMAtomicReadS32(&pModule->cPendingBuffers) > 0);
+ if (ASMAtomicDecS32(&pModule->cPendingBuffers) == 0)
+ NdisSetEvent(&pModule->EventSendComplete);
+#endif /* !VBOXNETLWF_SYNC_SEND */
+ }
+ else
+ {
+ pPrevList = pList;
+ Log(("vboxNetLwfWinSendNetBufferListsComplete: passing list %p, next=%p, previous=%p, head=%p\n", pList, pNextList, pPrevList, pBufLists));
+ }
+ pList = pNextList;
+ }
+ if (pBufLists)
+ {
+ /* There are still lists remaining in the chain, pass'em up */
+ NdisFSendNetBufferListsComplete(pModule->hFilter, pBufLists, fFlags);
+ }
+ LogFlow(("<==vboxNetLwfWinSendNetBufferListsComplete\n"));
+}
+
+VOID vboxNetLwfWinReceiveNetBufferLists(IN NDIS_HANDLE hModuleCtx,
+ IN PNET_BUFFER_LIST pBufLists,
+ IN NDIS_PORT_NUMBER nPort,
+ IN ULONG nBufLists,
+ IN ULONG fFlags)
+{
+ /// @todo Do we need loopback handling?
+ LogFlow(("==>vboxNetLwfWinReceiveNetBufferLists: module=%p\n", hModuleCtx));
+ PVBOXNETLWF_MODULE pModule = (PVBOXNETLWF_MODULE)hModuleCtx;
+ vboxNetLwfWinDumpPackets("vboxNetLwfWinReceiveNetBufferLists: got", pBufLists);
+
+ if (!ASMAtomicReadBool(&pModule->fActive))
+ {
+ /*
+ * The trunk is inactive, just pass along all packets to the next
+ * overlying driver.
+ */
+ NdisFIndicateReceiveNetBufferLists(pModule->hFilter, pBufLists, nPort, nBufLists, fFlags);
+ LogFlow(("<==vboxNetLwfWinReceiveNetBufferLists: inactive trunk\n"));
+ return;
+ }
+
+ if (vboxNetLwfWinIsRunning(pModule))
+ {
+ if (NDIS_TEST_RECEIVE_CANNOT_PEND(fFlags))
+ {
+ for (PNET_BUFFER_LIST pList = pBufLists; pList; pList = NET_BUFFER_LIST_NEXT_NBL(pList))
+ {
+ PNET_BUFFER_LIST pNext = NET_BUFFER_LIST_NEXT_NBL(pList);
+ NET_BUFFER_LIST_NEXT_NBL(pList) = NULL; /* Unlink temporarily */
+ if (!vboxNetLwfWinForwardToIntNet(pModule, pList, INTNETTRUNKDIR_WIRE))
+ {
+ vboxNetLwfWinDumpPackets("vboxNetLwfWinReceiveNetBufferLists: passing up", pList);
+ NdisFIndicateReceiveNetBufferLists(pModule->hFilter, pList, nPort, nBufLists, fFlags);
+ }
+ NET_BUFFER_LIST_NEXT_NBL(pList) = pNext; /* Restore the link */
+ }
+ }
+ else
+ {
+ /* We collect dropped NBLs in a separate list in order to "return" them. */
+ PNET_BUFFER_LIST pNext = NULL;
+ PNET_BUFFER_LIST pDropHead = NULL;
+ PNET_BUFFER_LIST pDropTail = NULL;
+ PNET_BUFFER_LIST pPassHead = NULL;
+ PNET_BUFFER_LIST pPassTail = NULL;
+ ULONG nDrop = 0, nPass = 0;
+ for (PNET_BUFFER_LIST pList = pBufLists; pList; pList = pNext)
+ {
+ pNext = NET_BUFFER_LIST_NEXT_NBL(pList);
+ NET_BUFFER_LIST_NEXT_NBL(pList) = NULL; /* Unlink */
+ if (vboxNetLwfWinForwardToIntNet(pModule, pList, INTNETTRUNKDIR_WIRE))
+ {
+ if (nDrop++)
+ {
+ NET_BUFFER_LIST_NEXT_NBL(pDropTail) = pList;
+ pDropTail = pList;
+ }
+ else
+ pDropHead = pDropTail = pList;
+ }
+ else
+ {
+ if (nPass++)
+ {
+ NET_BUFFER_LIST_NEXT_NBL(pPassTail) = pList;
+ pPassTail = pList;
+ }
+ else
+ pPassHead = pPassTail = pList;
+ }
+ }
+ Assert((pBufLists == pPassHead) || (pBufLists == pDropHead));
+ Assert(nDrop + nPass == nBufLists);
+ if (pPassHead)
+ {
+ vboxNetLwfWinDumpPackets("vboxNetLwfWinReceiveNetBufferLists: passing up", pPassHead);
+ NdisFIndicateReceiveNetBufferLists(pModule->hFilter, pPassHead, nPort, nPass, fFlags);
+ }
+ if (pDropHead)
+ {
+ vboxNetLwfWinDumpPackets("vboxNetLwfWinReceiveNetBufferLists: consumed", pDropHead);
+ NdisFReturnNetBufferLists(pModule->hFilter, pDropHead,
+ fFlags & NDIS_RECEIVE_FLAGS_DISPATCH_LEVEL ? NDIS_RETURN_FLAGS_DISPATCH_LEVEL : 0);
+ }
+ }
+
+ }
+ else
+ {
+ vboxNetLwfWinDumpPackets("vboxNetLwfWinReceiveNetBufferLists: consumed", pBufLists);
+ if ((fFlags & NDIS_RECEIVE_FLAGS_RESOURCES) == 0)
+ NdisFReturnNetBufferLists(pModule->hFilter, pBufLists,
+ fFlags & NDIS_RECEIVE_FLAGS_DISPATCH_LEVEL ? NDIS_RETURN_FLAGS_DISPATCH_LEVEL : 0);
+ }
+ LogFlow(("<==vboxNetLwfWinReceiveNetBufferLists\n"));
+}
+
+VOID vboxNetLwfWinReturnNetBufferLists(IN NDIS_HANDLE hModuleCtx, IN PNET_BUFFER_LIST pBufLists, IN ULONG fFlags)
+{
+ LogFlow(("==>vboxNetLwfWinReturnNetBufferLists: module=%p\n", hModuleCtx));
+ PVBOXNETLWF_MODULE pModule = (PVBOXNETLWF_MODULE)hModuleCtx;
+ PNET_BUFFER_LIST pList = pBufLists;
+ PNET_BUFFER_LIST pNextList;
+ PNET_BUFFER_LIST pPrevList = NULL;
+ /** @todo Move common part into a separate function to be used by vboxNetLwfWinSendNetBufferListsComplete() as well */
+ while (pList)
+ {
+ pNextList = NET_BUFFER_LIST_NEXT_NBL(pList);
+ if (pList->SourceHandle == pModule->hFilter)
+ {
+ /* We allocated this NET_BUFFER_LIST, let's free it up */
+ Assert(NET_BUFFER_LIST_FIRST_NB(pList));
+ Assert(NET_BUFFER_FIRST_MDL(NET_BUFFER_LIST_FIRST_NB(pList)));
+ /*
+ * All our NBLs hold a single NB each, no need to iterate over a list.
+ * There is no need to free an associated NB explicitly either, as it was
+ * preallocated with NBL structure.
+ */
+ vboxNetLwfWinFreeMdlChain(NET_BUFFER_FIRST_MDL(NET_BUFFER_LIST_FIRST_NB(pList)));
+ /* Unlink this list from the chain */
+ if (pPrevList)
+ NET_BUFFER_LIST_NEXT_NBL(pPrevList) = pNextList;
+ else
+ pBufLists = pNextList;
+ NdisFreeNetBufferList(pList);
+#ifdef VBOXNETLWF_SYNC_SEND
+ Log4(("vboxNetLwfWinReturnNetBufferLists: freed NBL+NB 0x%p\n", pList));
+ KeSetEvent(&pModule->EventHost, 0, FALSE);
+#else /* !VBOXNETLWF_SYNC_SEND */
+ Log4(("vboxNetLwfWinReturnNetBufferLists: freed NBL+NB+MDL+Data 0x%p\n", pList));
+ Assert(ASMAtomicReadS32(&pModule->cPendingBuffers) > 0);
+ if (ASMAtomicDecS32(&pModule->cPendingBuffers) == 0)
+ NdisSetEvent(&pModule->EventSendComplete);
+#endif /* !VBOXNETLWF_SYNC_SEND */
+ }
+ else
+ pPrevList = pList;
+ pList = pNextList;
+ }
+ if (pBufLists)
+ {
+ /* There are still lists remaining in the chain, pass'em up */
+ NdisFReturnNetBufferLists(pModule->hFilter, pBufLists, fFlags);
+ }
+ LogFlow(("<==vboxNetLwfWinReturnNetBufferLists\n"));
+}
+
+/**
+ * register the filter driver
+ */
+DECLHIDDEN(NDIS_STATUS) vboxNetLwfWinRegister(PDRIVER_OBJECT pDriverObject, PUNICODE_STRING pRegistryPathStr)
+{
+ RT_NOREF1(pRegistryPathStr);
+ NDIS_FILTER_DRIVER_CHARACTERISTICS FChars;
+ NDIS_STRING FriendlyName;
+ NDIS_STRING UniqueName;
+ NDIS_STRING ServiceName;
+
+ NdisInitUnicodeString(&FriendlyName, VBOXNETLWF_NAME_FRIENDLY);
+ NdisInitUnicodeString(&UniqueName, VBOXNETLWF_NAME_UNIQUE);
+ NdisInitUnicodeString(&ServiceName, VBOXNETLWF_NAME_SERVICE);
+
+ NdisZeroMemory(&FChars, sizeof (FChars));
+
+ FChars.Header.Type = NDIS_OBJECT_TYPE_FILTER_DRIVER_CHARACTERISTICS;
+ FChars.Header.Size = sizeof(NDIS_FILTER_DRIVER_CHARACTERISTICS);
+ FChars.Header.Revision = NDIS_FILTER_CHARACTERISTICS_REVISION_1;
+
+ FChars.MajorNdisVersion = VBOXNETLWF_VERSION_NDIS_MAJOR;
+ FChars.MinorNdisVersion = VBOXNETLWF_VERSION_NDIS_MINOR;
+
+ FChars.FriendlyName = FriendlyName;
+ FChars.UniqueName = UniqueName;
+ FChars.ServiceName = ServiceName;
+
+ /* Mandatory functions */
+ FChars.AttachHandler = vboxNetLwfWinAttach;
+ FChars.DetachHandler = vboxNetLwfWinDetach;
+ FChars.RestartHandler = vboxNetLwfWinRestart;
+ FChars.PauseHandler = vboxNetLwfWinPause;
+
+ /* Optional functions, non changeble at run-time */
+ FChars.OidRequestHandler = vboxNetLwfWinOidRequest;
+ FChars.OidRequestCompleteHandler = vboxNetLwfWinOidRequestComplete;
+ //FChars.CancelOidRequestHandler = vboxNetLwfWinCancelOidRequest;
+ FChars.StatusHandler = vboxNetLwfWinStatus;
+ //FChars.NetPnPEventHandler = vboxNetLwfWinPnPEvent;
+
+ /* Datapath functions */
+ FChars.SendNetBufferListsHandler = vboxNetLwfWinSendNetBufferLists;
+ FChars.SendNetBufferListsCompleteHandler = vboxNetLwfWinSendNetBufferListsComplete;
+ FChars.ReceiveNetBufferListsHandler = vboxNetLwfWinReceiveNetBufferLists;
+ FChars.ReturnNetBufferListsHandler = vboxNetLwfWinReturnNetBufferLists;
+
+ pDriverObject->DriverUnload = vboxNetLwfWinUnloadDriver;
+
+ NDIS_STATUS Status;
+ g_VBoxNetLwfGlobals.hFilterDriver = NULL;
+ Log(("vboxNetLwfWinRegister: registering filter driver...\n"));
+ Status = NdisFRegisterFilterDriver(pDriverObject,
+ (NDIS_HANDLE)&g_VBoxNetLwfGlobals,
+ &FChars,
+ &g_VBoxNetLwfGlobals.hFilterDriver);
+ Assert(Status == STATUS_SUCCESS);
+ if (Status == STATUS_SUCCESS)
+ {
+ Log(("vboxNetLwfWinRegister: successfully registered filter driver; registering device...\n"));
+ Status = vboxNetLwfWinDevCreate(&g_VBoxNetLwfGlobals);
+ Assert(Status == STATUS_SUCCESS);
+ Log(("vboxNetLwfWinRegister: vboxNetLwfWinDevCreate() returned 0x%x\n", Status));
+ }
+ else
+ {
+ LogError(("vboxNetLwfWinRegister: failed to register filter driver, status=0x%x", Status));
+ }
+ return Status;
+}
+
+static int vboxNetLwfWinStartInitIdcThread()
+{
+ int rc = VERR_INVALID_STATE;
+
+ if (ASMAtomicCmpXchgU32(&g_VBoxNetLwfGlobals.enmIdcState, LwfIdcState_Connecting, LwfIdcState_Disconnected))
+ {
+ Log(("vboxNetLwfWinStartInitIdcThread: IDC state change Diconnected -> Connecting\n"));
+
+ NTSTATUS Status = PsCreateSystemThread(&g_VBoxNetLwfGlobals.hInitIdcThread,
+ THREAD_ALL_ACCESS,
+ NULL,
+ NULL,
+ NULL,
+ vboxNetLwfWinInitIdcWorker,
+ &g_VBoxNetLwfGlobals);
+ Log(("vboxNetLwfWinStartInitIdcThread: create IDC initialization thread, status=0x%x\n", Status));
+ if (Status != STATUS_SUCCESS)
+ {
+ LogError(("vboxNetLwfWinStartInitIdcThread: IDC initialization failed (system thread creation, status=0x%x)\n", Status));
+ /*
+ * We failed to init IDC and there will be no second chance.
+ */
+ Log(("vboxNetLwfWinStartInitIdcThread: IDC state change Connecting -> Diconnected\n"));
+ ASMAtomicWriteU32(&g_VBoxNetLwfGlobals.enmIdcState, LwfIdcState_Disconnected);
+ }
+ rc = RTErrConvertFromNtStatus(Status);
+ }
+ return rc;
+}
+
+static void vboxNetLwfWinStopInitIdcThread()
+{
+}
+
+
+RT_C_DECLS_BEGIN
+
+NTSTATUS DriverEntry(IN PDRIVER_OBJECT pDriverObject, IN PUNICODE_STRING pRegistryPath);
+
+RT_C_DECLS_END
+
+NTSTATUS DriverEntry(IN PDRIVER_OBJECT pDriverObject, IN PUNICODE_STRING pRegistryPath)
+{
+ NDIS_STATUS Status = NDIS_STATUS_SUCCESS;
+ int rc;
+
+ /* the idc registration is initiated via IOCTL since our driver
+ * can be loaded when the VBoxDrv is not in case we are a Ndis IM driver */
+ rc = vboxNetLwfWinInitBase();
+ AssertRC(rc);
+ if (RT_SUCCESS(rc))
+ {
+ NdisZeroMemory(&g_VBoxNetLwfGlobals, sizeof (g_VBoxNetLwfGlobals));
+ RTListInit(&g_VBoxNetLwfGlobals.listModules);
+ NdisAllocateSpinLock(&g_VBoxNetLwfGlobals.Lock);
+ /*
+ * We choose to ignore IDC initialization errors here because if we fail to load
+ * our filter the upper protocols won't bind to the associated adapter, causing
+ * network failure at the host. Better to have non-working filter than broken
+ * networking on the host.
+ */
+ rc = vboxNetLwfWinStartInitIdcThread();
+ AssertRC(rc);
+
+ Status = vboxNetLwfWinRegister(pDriverObject, pRegistryPath);
+ Assert(Status == STATUS_SUCCESS);
+ if (Status == NDIS_STATUS_SUCCESS)
+ {
+ Log(("NETLWF: started successfully\n"));
+ return STATUS_SUCCESS;
+ }
+ NdisFreeSpinLock(&g_VBoxNetLwfGlobals.Lock);
+ vboxNetLwfWinFini();
+ }
+ else
+ {
+ Status = NDIS_STATUS_FAILURE;
+ }
+
+ return Status;
+}
+
+
+static VOID vboxNetLwfWinUnloadDriver(IN PDRIVER_OBJECT pDriver)
+{
+ RT_NOREF1(pDriver);
+ LogFlow(("==>vboxNetLwfWinUnloadDriver: driver=%p\n", pDriver));
+ vboxNetLwfWinDevDestroy(&g_VBoxNetLwfGlobals);
+ NdisFDeregisterFilterDriver(g_VBoxNetLwfGlobals.hFilterDriver);
+ NdisFreeSpinLock(&g_VBoxNetLwfGlobals.Lock);
+ LogFlow(("<==vboxNetLwfWinUnloadDriver\n"));
+ vboxNetLwfWinFini();
+}
+
+static const char *vboxNetLwfWinIdcStateToText(uint32_t enmState)
+{
+ switch (enmState)
+ {
+ case LwfIdcState_Disconnected: return "Disconnected";
+ case LwfIdcState_Connecting: return "Connecting";
+ case LwfIdcState_Connected: return "Connected";
+ case LwfIdcState_Stopping: return "Stopping";
+ }
+ return "Unknown";
+}
+
+static VOID vboxNetLwfWinInitIdcWorker(PVOID pvContext)
+{
+ int rc;
+ PVBOXNETLWFGLOBALS pGlobals = (PVBOXNETLWFGLOBALS)pvContext;
+
+ while (ASMAtomicReadU32(&pGlobals->enmIdcState) == LwfIdcState_Connecting)
+ {
+ rc = vboxNetFltInitIdc(&g_VBoxNetFltGlobals);
+ if (RT_SUCCESS(rc))
+ {
+ if (!ASMAtomicCmpXchgU32(&pGlobals->enmIdcState, LwfIdcState_Connected, LwfIdcState_Connecting))
+ {
+ /* The state has been changed (the only valid transition is to "Stopping"), undo init */
+ rc = vboxNetFltTryDeleteIdc(&g_VBoxNetFltGlobals);
+ Log(("vboxNetLwfWinInitIdcWorker: state change (Connecting -> %s) while initializing IDC, deleted IDC, rc=0x%x\n",
+ vboxNetLwfWinIdcStateToText(ASMAtomicReadU32(&pGlobals->enmIdcState)), rc));
+ }
+ else
+ {
+ Log(("vboxNetLwfWinInitIdcWorker: IDC state change Connecting -> Connected\n"));
+ }
+ }
+ else
+ {
+ LARGE_INTEGER WaitIn100nsUnits;
+ WaitIn100nsUnits.QuadPart = -(LONGLONG)10000000; /* 1 sec */
+ KeDelayExecutionThread(KernelMode, FALSE /* non-alertable */, &WaitIn100nsUnits);
+ }
+ }
+ PsTerminateSystemThread(STATUS_SUCCESS);
+}
+
+static int vboxNetLwfWinTryFiniIdc()
+{
+ int rc = VINF_SUCCESS;
+ NTSTATUS Status;
+ PKTHREAD pThread = NULL;
+ uint32_t enmPrevState = ASMAtomicXchgU32(&g_VBoxNetLwfGlobals.enmIdcState, LwfIdcState_Stopping);
+
+ Log(("vboxNetLwfWinTryFiniIdc: IDC state change %s -> Stopping\n", vboxNetLwfWinIdcStateToText(enmPrevState)));
+
+ switch (enmPrevState)
+ {
+ case LwfIdcState_Disconnected:
+ /* Have not even attempted to connect -- nothing to do. */
+ break;
+ case LwfIdcState_Stopping:
+ /* Impossible, but another thread is alreading doing FiniIdc, bail out */
+ LogError(("vboxNetLwfWinTryFiniIdc: called in 'Stopping' state\n"));
+ rc = VERR_INVALID_STATE;
+ break;
+ case LwfIdcState_Connecting:
+ /* the worker thread is running, let's wait for it to stop */
+ Status = ObReferenceObjectByHandle(g_VBoxNetLwfGlobals.hInitIdcThread,
+ THREAD_ALL_ACCESS, NULL, KernelMode,
+ (PVOID*)&pThread, NULL);
+ if (Status == STATUS_SUCCESS)
+ {
+ KeWaitForSingleObject(pThread, Executive, KernelMode, FALSE, NULL);
+ ObDereferenceObject(pThread);
+ }
+ else
+ {
+ LogError(("vboxNetLwfWinTryFiniIdc: ObReferenceObjectByHandle(%p) failed with 0x%x\n",
+ g_VBoxNetLwfGlobals.hInitIdcThread, Status));
+ }
+ rc = RTErrConvertFromNtStatus(Status);
+ break;
+ case LwfIdcState_Connected:
+ /* the worker succeeded in IDC init and terminated */
+ rc = vboxNetFltTryDeleteIdc(&g_VBoxNetFltGlobals);
+ Log(("vboxNetLwfWinTryFiniIdc: deleted IDC, rc=0x%x\n", rc));
+ break;
+ }
+ return rc;
+}
+
+static void vboxNetLwfWinFiniBase()
+{
+ vboxNetFltDeleteGlobals(&g_VBoxNetFltGlobals);
+
+ /*
+ * Undo the work done during start (in reverse order).
+ */
+ memset(&g_VBoxNetFltGlobals, 0, sizeof(g_VBoxNetFltGlobals));
+
+ RTLogDestroy(RTLogRelSetDefaultInstance(NULL));
+ RTLogDestroy(RTLogSetDefaultInstance(NULL));
+
+ RTR0Term();
+}
+
+static int vboxNetLwfWinInitBase()
+{
+ int rc = RTR0Init(0);
+ if (!RT_SUCCESS(rc))
+ return rc;
+
+ memset(&g_VBoxNetFltGlobals, 0, sizeof(g_VBoxNetFltGlobals));
+ rc = vboxNetFltInitGlobals(&g_VBoxNetFltGlobals);
+ if (!RT_SUCCESS(rc))
+ RTR0Term();
+
+ return rc;
+}
+
+static int vboxNetLwfWinFini()
+{
+ int rc = vboxNetLwfWinTryFiniIdc();
+ if (RT_SUCCESS(rc))
+ {
+ vboxNetLwfWinFiniBase();
+ }
+ return rc;
+}
+
+
+/*
+ *
+ * The OS specific interface definition
+ *
+ */
+
+
+bool vboxNetFltOsMaybeRediscovered(PVBOXNETFLTINS pThis)
+{
+ LogFlow(("==>vboxNetFltOsMaybeRediscovered: instance=%p\n", pThis));
+ LogFlow(("<==vboxNetFltOsMaybeRediscovered: return %RTbool\n", !ASMAtomicUoReadBool(&pThis->fDisconnectedFromHost)));
+ /* AttachToInterface true if disconnected */
+ return !ASMAtomicUoReadBool(&pThis->fDisconnectedFromHost);
+}
+
+int vboxNetFltPortOsXmit(PVBOXNETFLTINS pThis, void *pvIfData, PINTNETSG pSG, uint32_t fDst)
+{
+ RT_NOREF1(pvIfData);
+ int rc = VINF_SUCCESS;
+
+ PVBOXNETLWF_MODULE pModule = (PVBOXNETLWF_MODULE)pThis->u.s.WinIf.hModuleCtx;
+ LogFlow(("==>vboxNetFltPortOsXmit: instance=%p module=%p\n", pThis, pModule));
+ if (!pModule)
+ {
+ LogFlow(("<==vboxNetFltPortOsXmit: pModule is null, return %d\n", VERR_INTERNAL_ERROR));
+ return VERR_INTERNAL_ERROR;
+ }
+ /* Prevent going into "paused" state until all transmissions have been completed. */
+ NDIS_WAIT_FOR_MUTEX(&pModule->InTransmit);
+ /* Ignore all sends if the stack is paused or being paused, etc... */
+ if (!vboxNetLwfWinIsRunning(pModule))
+ {
+ NDIS_RELEASE_MUTEX(&pModule->InTransmit);
+ return VINF_SUCCESS;
+ }
+
+ vboxNetLwfWinDumpPacket(pSG, !(fDst & INTNETTRUNKDIR_WIRE) ? "intnet --> host"
+ : !(fDst & INTNETTRUNKDIR_HOST) ? "intnet --> wire" : "intnet --> all");
+
+ /*
+ * There are two possible strategies to deal with incoming SGs:
+ * 1) make a copy of data and complete asynchronously;
+ * 2) complete synchronously using the original data buffers.
+ * Before we consider implementing (1) it is quite interesting to see
+ * how well (2) performs. So we block until our requests are complete.
+ * Actually there is third possibility -- to use SG retain/release
+ * callbacks, but those seem not be fully implemented yet.
+ * Note that ansynchronous completion will require different implementation
+ * of vboxNetLwfWinPause(), not relying on InTransmit mutex.
+ */
+#ifdef VBOXNETLWF_SYNC_SEND
+ PVOID aEvents[2]; /* To wire and to host */
+ ULONG nEvents = 0;
+ LARGE_INTEGER timeout;
+ timeout.QuadPart = -(LONGLONG)10000000; /* 1 sec */
+#endif /* VBOXNETLWF_SYNC_SEND */
+ if (fDst & INTNETTRUNKDIR_WIRE)
+ {
+ PNET_BUFFER_LIST pBufList = vboxNetLwfWinSGtoNB(pModule, pSG);
+ if (pBufList)
+ {
+ vboxNetLwfWinDumpPackets("vboxNetFltPortOsXmit: sending down", pBufList);
+#ifdef VBOXNETLWF_SYNC_SEND
+ aEvents[nEvents++] = &pModule->EventWire;
+#else /* !VBOXNETLWF_SYNC_SEND */
+ if (ASMAtomicIncS32(&pModule->cPendingBuffers) == 1)
+ NdisResetEvent(&pModule->EventSendComplete);
+#endif /* !VBOXNETLWF_SYNC_SEND */
+ NdisFSendNetBufferLists(pModule->hFilter, pBufList, NDIS_DEFAULT_PORT_NUMBER, 0); /** @todo sendFlags! */
+ }
+ }
+ if (fDst & INTNETTRUNKDIR_HOST)
+ {
+ PNET_BUFFER_LIST pBufList = vboxNetLwfWinSGtoNB(pModule, pSG);
+ if (pBufList)
+ {
+ vboxNetLwfWinDumpPackets("vboxNetFltPortOsXmit: sending up", pBufList);
+#ifdef VBOXNETLWF_SYNC_SEND
+ aEvents[nEvents++] = &pModule->EventHost;
+#else /* !VBOXNETLWF_SYNC_SEND */
+ if (ASMAtomicIncS32(&pModule->cPendingBuffers) == 1)
+ NdisResetEvent(&pModule->EventSendComplete);
+#endif /* !VBOXNETLWF_SYNC_SEND */
+ NdisFIndicateReceiveNetBufferLists(pModule->hFilter, pBufList, NDIS_DEFAULT_PORT_NUMBER, 1, 0);
+ }
+ }
+#ifdef VBOXNETLWF_SYNC_SEND
+ if (nEvents)
+ {
+ NTSTATUS Status = KeWaitForMultipleObjects(nEvents, aEvents, WaitAll, Executive, KernelMode, FALSE, &timeout, NULL);
+ if (Status != STATUS_SUCCESS)
+ {
+ LogError(("vboxNetFltPortOsXmit: KeWaitForMultipleObjects() failed with 0x%x\n", Status));
+ if (Status == STATUS_TIMEOUT)
+ rc = VERR_TIMEOUT;
+ else
+ rc = RTErrConvertFromNtStatus(Status);
+ }
+ }
+#endif /* VBOXNETLWF_SYNC_SEND */
+ NDIS_RELEASE_MUTEX(&pModule->InTransmit);
+
+ LogFlow(("<==vboxNetFltPortOsXmit: return %d\n", rc));
+ return rc;
+}
+
+
+NDIS_IO_WORKITEM_FUNCTION vboxNetLwfWinToggleOffloading;
+
+VOID vboxNetLwfWinToggleOffloading(PVOID WorkItemContext, NDIS_HANDLE NdisIoWorkItemHandle)
+{
+ /* WARNING! Call this with IRQL=Passive! */
+ RT_NOREF1(NdisIoWorkItemHandle);
+ PVBOXNETLWF_MODULE pModuleCtx = (PVBOXNETLWF_MODULE)WorkItemContext;
+
+ if (ASMAtomicReadBool(&pModuleCtx->fActive))
+ {
+ /* Disable offloading temporarily by indicating offload config change. */
+ /** @todo Be sure to revise this when implementing offloading support! */
+ vboxNetLwfWinIndicateOffload(pModuleCtx, pModuleCtx->pDisabledOffloadConfig);
+ Log(("vboxNetLwfWinToggleOffloading: set offloading off\n"));
+ }
+ else
+ {
+ /* The filter is inactive -- restore offloading configuration. */
+ if (pModuleCtx->fOffloadConfigValid)
+ {
+ vboxNetLwfWinIndicateOffload(pModuleCtx, pModuleCtx->pSavedOffloadConfig);
+ Log(("vboxNetLwfWinToggleOffloading: restored offloading config\n"));
+ }
+ else
+ DbgPrint("VBoxNetLwf: no saved offload config to restore for %s\n", pModuleCtx->szMiniportName);
+ }
+}
+
+
+void vboxNetFltPortOsSetActive(PVBOXNETFLTINS pThis, bool fActive)
+{
+ PVBOXNETLWF_MODULE pModuleCtx = (PVBOXNETLWF_MODULE)pThis->u.s.WinIf.hModuleCtx;
+ LogFlow(("==>vboxNetFltPortOsSetActive: instance=%p module=%p fActive=%RTbool\n", pThis, pModuleCtx, fActive));
+ if (!pModuleCtx)
+ {
+ LogFlow(("<==vboxNetFltPortOsSetActive: pModuleCtx is null\n"));
+ return;
+ }
+
+ NDIS_STATUS Status = STATUS_SUCCESS;
+ bool fOldActive = ASMAtomicXchgBool(&pModuleCtx->fActive, fActive);
+ if (fOldActive != fActive)
+ {
+ NdisQueueIoWorkItem(pModuleCtx->hWorkItem, vboxNetLwfWinToggleOffloading, pModuleCtx);
+ Status = vboxNetLwfWinSetPacketFilter(pModuleCtx, fActive);
+ LogFlow(("<==vboxNetFltPortOsSetActive: vboxNetLwfWinSetPacketFilter() returned 0x%x\n", Status));
+ }
+ else
+ LogFlow(("<==vboxNetFltPortOsSetActive: no change, remain %sactive\n", fActive ? "":"in"));
+}
+
+int vboxNetFltOsDisconnectIt(PVBOXNETFLTINS pThis)
+{
+ RT_NOREF1(pThis);
+ LogFlow(("==>vboxNetFltOsDisconnectIt: instance=%p\n", pThis));
+ LogFlow(("<==vboxNetFltOsDisconnectIt: return 0\n"));
+ return VINF_SUCCESS;
+}
+
+int vboxNetFltOsConnectIt(PVBOXNETFLTINS pThis)
+{
+ RT_NOREF1(pThis);
+ LogFlow(("==>vboxNetFltOsConnectIt: instance=%p\n", pThis));
+ LogFlow(("<==vboxNetFltOsConnectIt: return 0\n"));
+ return VINF_SUCCESS;
+}
+
+/*
+ * Uncommenting the following line produces debug log messages on IP address changes,
+ * including wired interfaces. No actual calls to a switch port are made. This is for
+ * debug purposes only!
+ * #define VBOXNETLWFWIN_DEBUGIPADDRNOTIF 1
+ */
+static void __stdcall vboxNetLwfWinIpAddrChangeCallback(IN PVOID pvCtx,
+ IN PMIB_UNICASTIPADDRESS_ROW pRow,
+ IN MIB_NOTIFICATION_TYPE enmNotifType)
+{
+ PVBOXNETFLTINS pThis = (PVBOXNETFLTINS)pvCtx;
+
+ /* We are only interested in add or remove notifications. */
+ bool fAdded;
+ if (enmNotifType == MibAddInstance)
+ fAdded = true;
+ else if (enmNotifType == MibDeleteInstance)
+ fAdded = false;
+ else
+ return;
+
+ if ( pRow
+#ifndef VBOXNETLWFWIN_DEBUGIPADDRNOTIF
+ && pThis->pSwitchPort->pfnNotifyHostAddress
+#endif /* !VBOXNETLWFWIN_DEBUGIPADDRNOTIF */
+ )
+ {
+ switch (pRow->Address.si_family)
+ {
+ case AF_INET:
+ if ( IN4_IS_ADDR_LINKLOCAL(&pRow->Address.Ipv4.sin_addr)
+ || pRow->Address.Ipv4.sin_addr.s_addr == IN4ADDR_LOOPBACK)
+ {
+ Log(("vboxNetLwfWinIpAddrChangeCallback: ignoring %s address (%RTnaipv4)\n",
+ pRow->Address.Ipv4.sin_addr.s_addr == IN4ADDR_LOOPBACK ? "loopback" : "link-local",
+ pRow->Address.Ipv4.sin_addr));
+ break;
+ }
+ Log(("vboxNetLwfWinIpAddrChangeCallback: %s IPv4 addr=%RTnaipv4 on luid=(%u,%u)\n",
+ fAdded ? "add" : "remove", pRow->Address.Ipv4.sin_addr,
+ pRow->InterfaceLuid.Info.IfType, pRow->InterfaceLuid.Info.NetLuidIndex));
+#ifndef VBOXNETLWFWIN_DEBUGIPADDRNOTIF
+ pThis->pSwitchPort->pfnNotifyHostAddress(pThis->pSwitchPort, fAdded, kIntNetAddrType_IPv4,
+ &pRow->Address.Ipv4.sin_addr);
+#endif /* !VBOXNETLWFWIN_DEBUGIPADDRNOTIF */
+ break;
+ case AF_INET6:
+ if (Ipv6AddressScope(pRow->Address.Ipv6.sin6_addr.u.Byte) <= ScopeLevelLink)
+ {
+ Log(("vboxNetLwfWinIpAddrChangeCallback: ignoring link-local address (%RTnaipv6)\n",
+ &pRow->Address.Ipv6.sin6_addr));
+ break;
+ }
+ Log(("vboxNetLwfWinIpAddrChangeCallback: %s IPv6 addr=%RTnaipv6 scope=%d luid=(%u,%u)\n",
+ fAdded ? "add" : "remove", &pRow->Address.Ipv6.sin6_addr,
+ Ipv6AddressScope(pRow->Address.Ipv6.sin6_addr.u.Byte),
+ pRow->InterfaceLuid.Info.IfType, pRow->InterfaceLuid.Info.NetLuidIndex));
+#ifndef VBOXNETLWFWIN_DEBUGIPADDRNOTIF
+ pThis->pSwitchPort->pfnNotifyHostAddress(pThis->pSwitchPort, fAdded, kIntNetAddrType_IPv6,
+ &pRow->Address.Ipv6.sin6_addr);
+#endif /* !VBOXNETLWFWIN_DEBUGIPADDRNOTIF */
+ break;
+ }
+ }
+ else
+ Log(("vboxNetLwfWinIpAddrChangeCallback: pRow=%p pfnNotifyHostAddress=%p\n",
+ pRow, pThis->pSwitchPort->pfnNotifyHostAddress));
+}
+
+void vboxNetLwfWinRegisterIpAddrNotifier(PVBOXNETFLTINS pThis)
+{
+ LogFlow(("==>vboxNetLwfWinRegisterIpAddrNotifier: instance=%p\n", pThis));
+ if ( pThis->pSwitchPort
+#ifndef VBOXNETLWFWIN_DEBUGIPADDRNOTIF
+ && pThis->pSwitchPort->pfnNotifyHostAddress
+#endif /* !VBOXNETLWFWIN_DEBUGIPADDRNOTIF */
+ )
+ {
+ NETIO_STATUS Status;
+ /* First we need to go over all host IP addresses and add them via pfnNotifyHostAddress. */
+ PMIB_UNICASTIPADDRESS_TABLE HostIpAddresses = NULL;
+ Status = GetUnicastIpAddressTable(AF_UNSPEC, &HostIpAddresses);
+ if (NETIO_SUCCESS(Status))
+ {
+ for (unsigned i = 0; i < HostIpAddresses->NumEntries; i++)
+ vboxNetLwfWinIpAddrChangeCallback(pThis, &HostIpAddresses->Table[i], MibAddInstance);
+ }
+ else
+ LogError(("vboxNetLwfWinRegisterIpAddrNotifier: GetUnicastIpAddressTable failed with %x\n", Status));
+ /* Now we can register a callback function to keep track of address changes. */
+ Status = NotifyUnicastIpAddressChange(AF_UNSPEC, vboxNetLwfWinIpAddrChangeCallback,
+ pThis, false, &pThis->u.s.WinIf.hNotifier);
+ if (NETIO_SUCCESS(Status))
+ Log(("vboxNetLwfWinRegisterIpAddrNotifier: notifier=%p\n", pThis->u.s.WinIf.hNotifier));
+ else
+ LogError(("vboxNetLwfWinRegisterIpAddrNotifier: NotifyUnicastIpAddressChange failed with %x\n", Status));
+ }
+ else
+ pThis->u.s.WinIf.hNotifier = NULL;
+ LogFlow(("<==vboxNetLwfWinRegisterIpAddrNotifier\n"));
+}
+
+void vboxNetLwfWinUnregisterIpAddrNotifier(PVBOXNETFLTINS pThis)
+{
+ Log(("vboxNetLwfWinUnregisterIpAddrNotifier: notifier=%p\n", pThis->u.s.WinIf.hNotifier));
+ if (pThis->u.s.WinIf.hNotifier)
+ CancelMibChangeNotify2(pThis->u.s.WinIf.hNotifier);
+}
+
+void vboxNetFltOsDeleteInstance(PVBOXNETFLTINS pThis)
+{
+ PVBOXNETLWF_MODULE pModuleCtx = (PVBOXNETLWF_MODULE)pThis->u.s.WinIf.hModuleCtx;
+ LogFlow(("==>vboxNetFltOsDeleteInstance: instance=%p module=%p\n", pThis, pModuleCtx));
+ /* Cancel IP address change notifications */
+ vboxNetLwfWinUnregisterIpAddrNotifier(pThis);
+ /* Technically it is possible that the module has already been gone by now. */
+ if (pModuleCtx)
+ {
+ Assert(!pModuleCtx->fActive); /* Deactivation ensures bypass mode */
+ pModuleCtx->pNetFlt = NULL;
+ pThis->u.s.WinIf.hModuleCtx = NULL;
+ }
+ LogFlow(("<==vboxNetFltOsDeleteInstance\n"));
+}
+
+static void vboxNetLwfWinReportCapabilities(PVBOXNETFLTINS pThis, PVBOXNETLWF_MODULE pModuleCtx)
+{
+ if (pThis->pSwitchPort
+ && vboxNetFltTryRetainBusyNotDisconnected(pThis))
+ {
+ pThis->pSwitchPort->pfnReportMacAddress(pThis->pSwitchPort, &pModuleCtx->MacAddr);
+ pThis->pSwitchPort->pfnReportPromiscuousMode(pThis->pSwitchPort,
+ vboxNetLwfWinIsPromiscuous(pModuleCtx));
+ pThis->pSwitchPort->pfnReportGsoCapabilities(pThis->pSwitchPort, 0,
+ INTNETTRUNKDIR_WIRE | INTNETTRUNKDIR_HOST);
+ pThis->pSwitchPort->pfnReportNoPreemptDsts(pThis->pSwitchPort, 0 /* none */);
+ vboxNetFltRelease(pThis, true /*fBusy*/);
+ }
+}
+
+int vboxNetFltOsInitInstance(PVBOXNETFLTINS pThis, void *pvContext)
+{
+ RT_NOREF1(pvContext);
+ LogFlow(("==>vboxNetFltOsInitInstance: instance=%p context=%p\n", pThis, pvContext));
+ AssertReturn(pThis, VERR_INVALID_PARAMETER);
+ Log(("vboxNetFltOsInitInstance: trunk name=%s\n", pThis->szName));
+ NdisAcquireSpinLock(&g_VBoxNetLwfGlobals.Lock);
+ PVBOXNETLWF_MODULE pModuleCtx;
+ RTListForEach(&g_VBoxNetLwfGlobals.listModules, pModuleCtx, VBOXNETLWF_MODULE, node)
+ {
+ DbgPrint("vboxNetFltOsInitInstance: evaluating module, name=%s\n", pModuleCtx->szMiniportName);
+ if (!RTStrICmp(pThis->szName, pModuleCtx->szMiniportName))
+ {
+ NdisReleaseSpinLock(&g_VBoxNetLwfGlobals.Lock);
+ Log(("vboxNetFltOsInitInstance: found matching module, name=%s\n", pThis->szName));
+ pThis->u.s.WinIf.hModuleCtx = pModuleCtx;
+ pModuleCtx->pNetFlt = pThis;
+ vboxNetLwfWinReportCapabilities(pThis, pModuleCtx);
+ vboxNetLwfWinRegisterIpAddrNotifier(pThis);
+ LogFlow(("<==vboxNetFltOsInitInstance: return 0\n"));
+ return VINF_SUCCESS;
+ }
+ }
+ NdisReleaseSpinLock(&g_VBoxNetLwfGlobals.Lock);
+ // Internal network code will try to reconnect periodically, we should not spam in event log
+ //vboxNetLwfLogErrorEvent(IO_ERR_INTERNAL_ERROR, STATUS_SUCCESS, 6);
+ LogFlow(("<==vboxNetFltOsInitInstance: return VERR_INTNET_FLT_IF_NOT_FOUND\n"));
+ return VERR_INTNET_FLT_IF_NOT_FOUND;
+}
+
+int vboxNetFltOsPreInitInstance(PVBOXNETFLTINS pThis)
+{
+ LogFlow(("==>vboxNetFltOsPreInitInstance: instance=%p\n", pThis));
+ pThis->u.s.WinIf.hModuleCtx = 0;
+ pThis->u.s.WinIf.hNotifier = NULL;
+ LogFlow(("<==vboxNetFltOsPreInitInstance: return 0\n"));
+ return VINF_SUCCESS;
+}
+
+void vboxNetFltPortOsNotifyMacAddress(PVBOXNETFLTINS pThis, void *pvIfData, PCRTMAC pMac)
+{
+ RT_NOREF3(pThis, pvIfData, pMac);
+ LogFlow(("==>vboxNetFltPortOsNotifyMacAddress: instance=%p data=%p mac=%RTmac\n", pThis, pvIfData, pMac));
+ LogFlow(("<==vboxNetFltPortOsNotifyMacAddress\n"));
+}
+
+int vboxNetFltPortOsConnectInterface(PVBOXNETFLTINS pThis, void *pvIf, void **ppvIfData)
+{
+ RT_NOREF3(pThis, pvIf, ppvIfData);
+ LogFlow(("==>vboxNetFltPortOsConnectInterface: instance=%p if=%p data=%p\n", pThis, pvIf, ppvIfData));
+ LogFlow(("<==vboxNetFltPortOsConnectInterface: return 0\n"));
+ /* Nothing to do */
+ return VINF_SUCCESS;
+}
+
+int vboxNetFltPortOsDisconnectInterface(PVBOXNETFLTINS pThis, void *pvIfData)
+{
+ RT_NOREF2(pThis, pvIfData);
+ LogFlow(("==>vboxNetFltPortOsDisconnectInterface: instance=%p data=%p\n", pThis, pvIfData));
+ LogFlow(("<==vboxNetFltPortOsDisconnectInterface: return 0\n"));
+ /* Nothing to do */
+ return VINF_SUCCESS;
+}
+
diff --git a/src/VBox/HostDrivers/VBoxNetFlt/win/drv/VBoxNetLwf-win.h b/src/VBox/HostDrivers/VBoxNetFlt/win/drv/VBoxNetLwf-win.h
new file mode 100644
index 00000000..1a0a9e7e
--- /dev/null
+++ b/src/VBox/HostDrivers/VBoxNetFlt/win/drv/VBoxNetLwf-win.h
@@ -0,0 +1,55 @@
+/* $Id: VBoxNetLwf-win.h $ */
+/** @file
+ * VBoxNetLwf-win.h - Bridged Networking Driver, Windows-specific code.
+ */
+/*
+ * Copyright (C) 2014-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+#ifndef VBOX_INCLUDED_SRC_VBoxNetFlt_win_drv_VBoxNetLwf_win_h
+#define VBOX_INCLUDED_SRC_VBoxNetFlt_win_drv_VBoxNetLwf_win_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+#define VBOXNETLWF_VERSION_NDIS_MAJOR 6
+#define VBOXNETLWF_VERSION_NDIS_MINOR 0
+
+#define VBOXNETLWF_NAME_FRIENDLY L"VirtualBox NDIS Light-Weight Filter"
+#define VBOXNETLWF_NAME_UNIQUE L"{7af6b074-048d-4444-bfce-1ecc8bc5cb76}"
+#define VBOXNETLWF_NAME_SERVICE L"VBoxNetLwf"
+
+#define VBOXNETLWF_NAME_LINK L"\\DosDevices\\Global\\VBoxNetLwf"
+#define VBOXNETLWF_NAME_DEVICE L"\\Device\\VBoxNetLwf"
+
+#define VBOXNETLWF_MEM_TAG 'FLBV'
+#define VBOXNETLWF_REQ_ID 'fLBV'
+
+#endif /* !VBOX_INCLUDED_SRC_VBoxNetFlt_win_drv_VBoxNetLwf_win_h */
diff --git a/src/VBox/HostDrivers/VBoxNetFlt/win/drv/VBoxNetLwf.inf b/src/VBox/HostDrivers/VBoxNetFlt/win/drv/VBoxNetLwf.inf
new file mode 100644
index 00000000..f5d9cdca
--- /dev/null
+++ b/src/VBox/HostDrivers/VBoxNetFlt/win/drv/VBoxNetLwf.inf
@@ -0,0 +1,120 @@
+; $Id: VBoxNetLwf.inf $
+; @file
+; VBoxNetLwf.inf - VirtualBox Bridged Networking Driver inf file
+;
+
+;
+; Copyright (C) 2014-2023 Oracle and/or its affiliates.
+;
+; This file is part of VirtualBox base platform packages, as
+; available from https://www.virtualbox.org.
+;
+; This program is free software; you can redistribute it and/or
+; modify it under the terms of the GNU General Public License
+; as published by the Free Software Foundation, in version 3 of the
+; License.
+;
+; This program is distributed in the hope that it will be useful, but
+; WITHOUT ANY WARRANTY; without even the implied warranty of
+; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+; General Public License for more details.
+;
+; You should have received a copy of the GNU General Public License
+; along with this program; if not, see <https://www.gnu.org/licenses>.
+;
+; The contents of this file may alternatively be used under the terms
+; of the Common Development and Distribution License Version 1.0
+; (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+; in the VirtualBox distribution, in which case the provisions of the
+; CDDL are applicable instead of those of the GPL.
+;
+; You may elect to license modified versions of this file under the
+; terms and conditions of either the GPL or the CDDL or both.
+;
+; SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+;
+
+[Version]
+Signature = "$Windows NT$"
+;cat CatalogFile = VBoxNetLwf.cat
+Class = NetService
+ClassGUID = {4D36E974-E325-11CE-BFC1-08002BE10318}
+Provider = %ORACLE%
+;edit-DriverVer=10/23/2014,1.0.1.0
+
+
+[Manufacturer]
+%ORACLE% = VBoxNetLwf@COMMA-NT-ARCH@
+
+[ControlFlags]
+
+[VBoxNetLwf@DOT-NT-ARCH@]
+%VBoxNetLwf_Desc% = VBoxNetLwf.ndi, oracle_VBoxNetLwf
+
+[VBoxNetLwf.ndi]
+AddReg = VBoxNetLwf.ndi.AddReg, VBoxNetLwf.AddReg
+Characteristics = 0x40000 ; NCF_LW_FILTER
+CopyFiles = VBoxNetLwf.Files.Sys
+NetCfgInstanceId = "{7af6b074-048d-4444-bfce-1ecc8bc5cb76}"
+
+[VBoxNetLwf.ndi.Remove.Services]
+DelService = VBoxNetLwf,0x200 ; Stop the service before uninstalling
+
+[VBoxNetLwf.ndi.Services]
+AddService = VBoxNetLwf,, VBoxNetLwf.AddService, VBoxNetLwf.AddEventLog
+
+[VBoxNetLwf.AddService]
+DisplayName = %VBoxNetLwfService_Desc%
+ServiceType = 1 ;SERVICE_KERNEL_DRIVER
+StartType = 1 ;SERVICE_SYSTEM_START
+ErrorControl = 1 ;SERVICE_ERROR_NORMAL
+ServiceBinary = %12%\VBoxNetLwf.sys
+LoadOrderGroup = NDIS
+AddReg = VBoxNetLwf.AddService.AddReg
+
+[VBoxNetLwf.AddService.AddReg]
+
+[VBoxNetLwf.AddEventLog]
+AddReg = VBoxNetLwf.AddEventLog.AddReg
+
+[VBoxNetLwf.AddEventLog.AddReg]
+HKR,,EventMessageFile,0x00020000,"%%SystemRoot%%\System32\IoLogMsg.dll"
+HKR,,TypesSupported,0x00010001,7
+
+
+[SourceDisksNames]
+1=%DiskDescription%,"",,
+
+[SourceDisksFiles]
+VBoxNetLwf.sys=1
+
+[DestinationDirs]
+DefaultDestDir = 12
+VBoxNetLwf.Files.Sys = 12 ; %windir%\System32\drivers
+
+[VBoxNetLwf.Files.Sys]
+VBoxNetLwf.sys,,,2
+
+
+[VBoxNetLwf.ndi.AddReg]
+HKR, Ndi, HelpText, , %VBoxNetLwf_HELP%
+;HKR, Ndi, ClsID, 0, {f374d1a0-bf08-4bdc-9cb2-c15ddaeef955}
+;HKR, Ndi, ComponentDll, , VBoxNetLwfNobj.dll
+HKR, Ndi, FilterClass, , compression
+HKR, Ndi, FilterType, 0x10001, 0x2
+HKR, Ndi, FilterRunType,0x10001, 2 ; OPTIONAL, to prevent unbinding of protocol drivers
+HKR, Ndi, Service, , VBoxNetLwf
+HKR, Ndi, CoServices, 0x10000, VBoxNetLwf
+HKR, Ndi\Interfaces, UpperRange, , noupper
+HKR, Ndi\Interfaces, LowerRange, , nolower
+HKR, Ndi\Interfaces, FilterMediaTypes, , ethernet
+
+[VBoxNetLwf.AddReg]
+;HKR, Parameters, Param1, 0, 4
+
+[Strings]
+ORACLE = "Oracle Corporation"
+DiskDescription = "VirtualBox NDIS6 Bridged Networking Driver"
+VBoxNetLwf_Desc = "VirtualBox NDIS6 Bridged Networking Driver"
+VBoxNetLwf_HELP = "VirtualBox NDIS6 Bridged Networking Driver"
+VBoxNetLwfService_Desc = "VirtualBox NDIS6 Bridged Networking Service"
diff --git a/src/VBox/HostDrivers/VBoxNetFlt/win/nobj/Makefile.kup b/src/VBox/HostDrivers/VBoxNetFlt/win/nobj/Makefile.kup
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/src/VBox/HostDrivers/VBoxNetFlt/win/nobj/Makefile.kup
diff --git a/src/VBox/HostDrivers/VBoxNetFlt/win/nobj/VBoxNetFltNobj.cpp b/src/VBox/HostDrivers/VBoxNetFlt/win/nobj/VBoxNetFltNobj.cpp
new file mode 100644
index 00000000..a5cf004c
--- /dev/null
+++ b/src/VBox/HostDrivers/VBoxNetFlt/win/nobj/VBoxNetFltNobj.cpp
@@ -0,0 +1,747 @@
+/* $Id: VBoxNetFltNobj.cpp $ */
+/** @file
+ * VBoxNetFltNobj.cpp - Notify Object for Bridged Networking Driver.
+ *
+ * Used to filter Bridged Networking Driver bindings
+ */
+
+/*
+ * Copyright (C) 2011-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include "VBoxNetFltNobj.h"
+#include <iprt/win/ntddndis.h>
+#include <iprt/win/windows.h>
+#include <winreg.h>
+#include <Olectl.h>
+
+#include <VBoxNetFltNobjT_i.c>
+
+#include <iprt/assert.h>
+#include <iprt/utf16.h>
+#include <iprt/string.h>
+
+
+/*********************************************************************************************************************************
+* Defined Constants And Macros *
+*********************************************************************************************************************************/
+//# define VBOXNETFLTNOTIFY_DEBUG_BIND
+
+#ifdef DEBUG
+# define NonStandardAssert(a) Assert(a)
+# define NonStandardAssertBreakpoint() AssertFailed()
+#else
+# define NonStandardAssert(a) do{}while (0)
+# define NonStandardAssertBreakpoint() do{}while (0)
+#endif
+
+
+/*********************************************************************************************************************************
+* Global Variables *
+*********************************************************************************************************************************/
+static HMODULE g_hModSelf = (HMODULE)~(uintptr_t)0;
+
+
+VBoxNetFltNobj::VBoxNetFltNobj()
+ : mpNetCfg(NULL)
+ , mpNetCfgComponent(NULL)
+ , mbInstalling(FALSE)
+{
+}
+
+VBoxNetFltNobj::~VBoxNetFltNobj()
+{
+ cleanup();
+}
+
+void VBoxNetFltNobj::cleanup()
+{
+ if (mpNetCfg)
+ {
+ mpNetCfg->Release();
+ mpNetCfg = NULL;
+ }
+
+ if (mpNetCfgComponent)
+ {
+ mpNetCfgComponent->Release();
+ mpNetCfgComponent = NULL;
+ }
+}
+
+void VBoxNetFltNobj::init(IN INetCfgComponent *pNetCfgComponent, IN INetCfg *pNetCfg, IN BOOL bInstalling)
+{
+ cleanup();
+
+ NonStandardAssert(pNetCfg);
+ NonStandardAssert(pNetCfgComponent);
+ if (pNetCfg)
+ {
+ pNetCfg->AddRef();
+ mpNetCfg = pNetCfg;
+ }
+
+ if (pNetCfgComponent)
+ {
+ pNetCfgComponent->AddRef();
+ mpNetCfgComponent = pNetCfgComponent;
+ }
+
+ mbInstalling = bInstalling;
+}
+
+/* INetCfgComponentControl methods */
+STDMETHODIMP VBoxNetFltNobj::Initialize(IN INetCfgComponent *pNetCfgComponent, IN INetCfg *pNetCfg, IN BOOL bInstalling)
+{
+ init(pNetCfgComponent, pNetCfg, bInstalling);
+ return S_OK;
+}
+
+STDMETHODIMP VBoxNetFltNobj::ApplyRegistryChanges()
+{
+ return S_OK;
+}
+
+STDMETHODIMP VBoxNetFltNobj::ApplyPnpChanges(IN INetCfgPnpReconfigCallback *pCallback)
+{
+ RT_NOREF1(pCallback);
+ return S_OK;
+}
+
+STDMETHODIMP VBoxNetFltNobj::CancelChanges()
+{
+ return S_OK;
+}
+
+static HRESULT vboxNetFltWinQueryInstanceKey(IN INetCfgComponent *pComponent, OUT PHKEY phKey)
+{
+ LPWSTR pwszPnpId;
+ HRESULT hrc = pComponent->GetPnpDevNodeId(&pwszPnpId);
+ if (hrc == S_OK)
+ {
+ WCHAR wszKeyName[MAX_PATH];
+ RTUtf16Copy(wszKeyName, MAX_PATH, L"SYSTEM\\CurrentControlSet\\Enum\\");
+ int rc = RTUtf16Cat(wszKeyName, MAX_PATH, pwszPnpId);
+ if (RT_SUCCESS(rc))
+ {
+ LSTATUS lrc = RegOpenKeyExW(HKEY_LOCAL_MACHINE, wszKeyName, 0 /*ulOptions*/, KEY_READ, phKey);
+ if (lrc != ERROR_SUCCESS)
+ {
+ hrc = HRESULT_FROM_WIN32(lrc);
+ NonStandardAssertBreakpoint();
+ }
+ }
+ else
+ AssertRCStmt(rc, hrc = ERROR_BUFFER_OVERFLOW);
+
+ CoTaskMemFree(pwszPnpId);
+ }
+ else
+ NonStandardAssertBreakpoint();
+ return hrc;
+}
+
+static HRESULT vboxNetFltWinQueryDriverKey(IN HKEY InstanceKey, OUT PHKEY phKey)
+{
+ HRESULT hrc = S_OK;
+
+ WCHAR wszValue[MAX_PATH];
+ DWORD cbValue = sizeof(wszValue) - sizeof(WCHAR);
+ DWORD dwType = REG_SZ;
+ LSTATUS lrc = RegQueryValueExW(InstanceKey, L"Driver", NULL /*lpReserved*/, &dwType, (LPBYTE)wszValue, &cbValue);
+ if (lrc == ERROR_SUCCESS)
+ {
+ if (dwType == REG_SZ)
+ {
+ wszValue[RT_ELEMENTS(wszValue) - 1] = '\0'; /* registry strings does not need to be zero terminated. */
+
+ WCHAR wszKeyName[MAX_PATH];
+ RTUtf16Copy(wszKeyName, MAX_PATH, L"SYSTEM\\CurrentControlSet\\Control\\Class\\");
+ int rc = RTUtf16Cat(wszKeyName, MAX_PATH, wszValue);
+ if (RT_SUCCESS(rc))
+ {
+ lrc = RegOpenKeyExW(HKEY_LOCAL_MACHINE, wszKeyName, 0 /*ulOptions*/, KEY_READ, phKey);
+ if (lrc != ERROR_SUCCESS)
+ {
+ hrc = HRESULT_FROM_WIN32(lrc);
+ NonStandardAssertBreakpoint();
+ }
+ }
+ else
+ AssertRCStmt(rc, hrc = ERROR_BUFFER_OVERFLOW);
+ }
+ else
+ {
+ hrc = HRESULT_FROM_WIN32(ERROR_DATATYPE_MISMATCH);
+ NonStandardAssertBreakpoint();
+ }
+ }
+ else
+ {
+ hrc = HRESULT_FROM_WIN32(lrc);
+ NonStandardAssertBreakpoint();
+ }
+
+ return hrc;
+}
+
+static HRESULT vboxNetFltWinQueryDriverKey(IN INetCfgComponent *pComponent, OUT PHKEY phKey)
+{
+ HKEY hKeyInstance = NULL;
+ HRESULT hrc = vboxNetFltWinQueryInstanceKey(pComponent, &hKeyInstance);
+ if (hrc == S_OK)
+ {
+ hrc = vboxNetFltWinQueryDriverKey(hKeyInstance, phKey);
+ if (hrc != S_OK)
+ NonStandardAssertBreakpoint();
+ RegCloseKey(hKeyInstance);
+ }
+ else
+ NonStandardAssertBreakpoint();
+ return hrc;
+}
+
+static HRESULT vboxNetFltWinNotifyCheckNetAdp(IN INetCfgComponent *pComponent, OUT bool *pfShouldBind)
+{
+ *pfShouldBind = false;
+
+ LPWSTR pwszDevId = NULL;
+ HRESULT hrc = pComponent->GetId(&pwszDevId);
+ if (hrc == S_OK)
+ {
+ /** @todo r=bird: This was _wcsnicmp(pwszDevId, L"sun_VBoxNetAdp", sizeof(L"sun_VBoxNetAdp")/2))
+ * which includes the terminator, so it translates to a full compare. Goes way back. */
+ if (RTUtf16ICmpAscii(pwszDevId, "sun_VBoxNetAdp") == 0)
+ *pfShouldBind = false;
+ else
+ hrc = S_FALSE;
+ CoTaskMemFree(pwszDevId);
+ }
+ else
+ NonStandardAssertBreakpoint();
+
+ return hrc;
+}
+
+static HRESULT vboxNetFltWinNotifyCheckMsLoop(IN INetCfgComponent *pComponent, OUT bool *pfShouldBind)
+{
+ *pfShouldBind = false;
+
+ LPWSTR pwszDevId = NULL;
+ HRESULT hrc = pComponent->GetId(&pwszDevId);
+ if (hrc == S_OK)
+ {
+ /** @todo r=bird: This was _wcsnicmp(pwszDevId, L"*msloop", sizeof(L"*msloop")/2)
+ * which includes the terminator, making it a full compare. Goes way back. */
+ if (RTUtf16ICmpAscii(pwszDevId, "*msloop") == 0)
+ {
+ /* we need to detect the medium the adapter is presenting
+ * to do that we could examine in the registry the *msloop params */
+ HKEY hKeyDriver;
+ hrc = vboxNetFltWinQueryDriverKey(pComponent, &hKeyDriver);
+ if (hrc == S_OK)
+ {
+ WCHAR wszValue[64]; /* 2 should be enough actually, paranoid check for extra spaces */
+ DWORD cbValue = sizeof(wszValue) - sizeof(WCHAR);
+ DWORD dwType = REG_SZ;
+ LSTATUS lrc = RegQueryValueExW(hKeyDriver, L"Medium", NULL /*lpReserved*/, &dwType, (LPBYTE)wszValue, &cbValue);
+ if (lrc == ERROR_SUCCESS)
+ {
+ if (dwType == REG_SZ)
+ {
+ wszValue[RT_ELEMENTS(wszValue) - 1] = '\0';
+
+ char szUtf8[256];
+ char *pszUtf8 = szUtf8;
+ RTUtf16ToUtf8Ex(wszValue, RTSTR_MAX, &pszUtf8, sizeof(szUtf8), NULL);
+ pszUtf8 = RTStrStrip(pszUtf8);
+
+ uint64_t uValue = 0;
+ int rc = RTStrToUInt64Ex(pszUtf8, NULL, 0, &uValue);
+ if (RT_SUCCESS(rc))
+ {
+ if (uValue == 0) /* 0 is Ethernet */
+ *pfShouldBind = true;
+ else
+ *pfShouldBind = false;
+ }
+ else
+ {
+ NonStandardAssertBreakpoint();
+ *pfShouldBind = true;
+ }
+ }
+ else
+ NonStandardAssertBreakpoint();
+ }
+ else
+ {
+ /** @todo we should check the default medium in HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Class\{4D36E972-E325-11CE-BFC1-08002bE10318}\<driver_id>\Ndi\Params\Medium, REG_SZ "Default" value */
+ NonStandardAssertBreakpoint();
+ *pfShouldBind = true;
+ }
+
+ RegCloseKey(hKeyDriver);
+ }
+ else
+ NonStandardAssertBreakpoint();
+ }
+ else
+ hrc = S_FALSE;
+ CoTaskMemFree(pwszDevId);
+ }
+ else
+ NonStandardAssertBreakpoint();
+ return hrc;
+}
+
+static HRESULT vboxNetFltWinNotifyCheckLowerRange(IN INetCfgComponent *pComponent, OUT bool *pfShouldBind)
+{
+ *pfShouldBind = false;
+
+ HKEY hKeyDriver = NULL;
+ HRESULT hrc = vboxNetFltWinQueryDriverKey(pComponent, &hKeyDriver);
+ if (hrc == S_OK)
+ {
+ HKEY hKeyInterfaces = NULL;
+ LSTATUS lrc = RegOpenKeyExW(hKeyDriver, L"Ndi\\Interfaces", 0 /*ulOptions*/, KEY_READ, &hKeyInterfaces);
+ if (lrc == ERROR_SUCCESS)
+ {
+ WCHAR wszValue[MAX_PATH];
+ DWORD cbValue = sizeof(wszValue) - sizeof(WCHAR);
+ DWORD dwType = REG_SZ;
+ lrc = RegQueryValueExW(hKeyInterfaces, L"LowerRange", NULL /*lpReserved*/, &dwType, (LPBYTE)wszValue, &cbValue);
+ if (lrc == ERROR_SUCCESS)
+ {
+ if (dwType == REG_SZ)
+ {
+ if (RTUtf16FindAscii(wszValue, "ethernet") >= 0 || RTUtf16FindAscii(wszValue, "wan") >= 0)
+ *pfShouldBind = true;
+ else
+ *pfShouldBind = false;
+ }
+ }
+ else
+ {
+ /* do not set err status to it */
+ *pfShouldBind = false;
+ NonStandardAssertBreakpoint();
+ }
+
+ RegCloseKey(hKeyInterfaces);
+ }
+ else
+ {
+ hrc = HRESULT_FROM_WIN32(lrc);
+ NonStandardAssertBreakpoint();
+ }
+
+ RegCloseKey(hKeyDriver);
+ }
+ else
+ NonStandardAssertBreakpoint();
+ return hrc;
+}
+
+static HRESULT vboxNetFltWinNotifyShouldBind(IN INetCfgComponent *pComponent, OUT bool *pfShouldBind)
+{
+ *pfShouldBind = false;
+
+ /* filter out only physical adapters */
+ DWORD fCharacteristics = 0;
+ HRESULT hrc = pComponent->GetCharacteristics(&fCharacteristics);
+ if (hrc != S_OK)
+ {
+ NonStandardAssertBreakpoint();
+ return hrc;
+ }
+
+ /* we are not binding to hidden adapters */
+ if (fCharacteristics & NCF_HIDDEN)
+ return S_OK;
+
+ hrc = vboxNetFltWinNotifyCheckMsLoop(pComponent, pfShouldBind);
+ if ( hrc == S_OK /* this is a loopback adapter, the pfShouldBind already contains the result */
+ || hrc != S_FALSE /* error occurred */)
+ return hrc;
+
+ hrc = vboxNetFltWinNotifyCheckNetAdp(pComponent, pfShouldBind);
+ if ( hrc == S_OK /* this is a VBoxNetAdp adapter, the pfShouldBind already contains the result */
+ || hrc != S_FALSE /* error occurred */)
+ return hrc;
+
+ //if (!(fCharacteristics & NCF_PHYSICAL))
+ //{
+ // *pfShouldBind = false; /* we are binding to physical adapters only */
+ // return S_OK;
+ //}
+
+ return vboxNetFltWinNotifyCheckLowerRange(pComponent, pfShouldBind);
+}
+
+
+static HRESULT vboxNetFltWinNotifyShouldBind(IN INetCfgBindingInterface *pIf, OUT bool *pfShouldBind)
+{
+ INetCfgComponent *pAdapterComponent = NULL;
+ HRESULT hrc = pIf->GetLowerComponent(&pAdapterComponent);
+ if (hrc == S_OK)
+ {
+ hrc = vboxNetFltWinNotifyShouldBind(pAdapterComponent, pfShouldBind);
+
+ pAdapterComponent->Release();
+ }
+ else
+ {
+ NonStandardAssertBreakpoint();
+ *pfShouldBind = false;
+ }
+ return hrc;
+}
+
+static HRESULT vboxNetFltWinNotifyShouldBind(IN INetCfgBindingPath *pPath, OUT bool *pfShouldBind)
+{
+ *pfShouldBind = false;
+
+ IEnumNetCfgBindingInterface *pIEnumBinding = NULL;
+ HRESULT hrc = pPath->EnumBindingInterfaces(&pIEnumBinding);
+ if (hrc == S_OK)
+ {
+ hrc = pIEnumBinding->Reset();
+ if (hrc == S_OK)
+ {
+ for (;;)
+ {
+ ULONG uCount = 0;
+ INetCfgBindingInterface *pIBinding = NULL;
+ hrc = pIEnumBinding->Next(1, &pIBinding, &uCount);
+ if (hrc == S_OK)
+ {
+ hrc = vboxNetFltWinNotifyShouldBind(pIBinding, pfShouldBind);
+ pIBinding->Release();
+
+ if (hrc != S_OK)
+ break; /* break on failure. */
+ if (!*pfShouldBind)
+ break;
+ }
+ else if (hrc == S_FALSE)
+ {
+ /* no more elements */
+ hrc = S_OK;
+ break;
+ }
+ else
+ {
+ NonStandardAssertBreakpoint();
+ /* break on falure */
+ break;
+ }
+ }
+ }
+ else
+ NonStandardAssertBreakpoint();
+
+ pIEnumBinding->Release();
+ }
+ else
+ NonStandardAssertBreakpoint();
+ return hrc;
+}
+
+static bool vboxNetFltWinNotifyShouldBind(IN INetCfgBindingPath *pPath)
+{
+#ifdef VBOXNETFLTNOTIFY_DEBUG_BIND
+ return VBOXNETFLTNOTIFY_DEBUG_BIND;
+#else
+ bool fShouldBind;
+ HRESULT hrc = vboxNetFltWinNotifyShouldBind(pPath, &fShouldBind);
+ if (hrc != S_OK)
+ fShouldBind = VBOXNETFLTNOTIFY_ONFAIL_BINDDEFAULT;
+
+ return fShouldBind;
+#endif
+}
+
+
+/* INetCfgComponentNotifyBinding methods */
+STDMETHODIMP VBoxNetFltNobj::NotifyBindingPath(IN DWORD dwChangeFlag, IN INetCfgBindingPath *pNetCfgBP)
+{
+ if (!(dwChangeFlag & NCN_ENABLE) || (dwChangeFlag & NCN_REMOVE) || vboxNetFltWinNotifyShouldBind(pNetCfgBP))
+ return S_OK;
+ return NETCFG_S_DISABLE_QUERY;
+}
+
+STDMETHODIMP VBoxNetFltNobj::QueryBindingPath(IN DWORD dwChangeFlag, IN INetCfgBindingPath *pNetCfgBP)
+{
+ RT_NOREF1(dwChangeFlag);
+ if (vboxNetFltWinNotifyShouldBind(pNetCfgBP))
+ return S_OK;
+ return NETCFG_S_DISABLE_QUERY;
+}
+
+
+static ATL::CComModule _Module;
+
+BEGIN_OBJECT_MAP(ObjectMap)
+ OBJECT_ENTRY(CLSID_VBoxNetFltNobj, VBoxNetFltNobj)
+END_OBJECT_MAP()
+
+
+extern "C"
+BOOL WINAPI DllMain(HINSTANCE hInstance, DWORD dwReason, LPVOID /*lpReserved*/)
+{
+ if (dwReason == DLL_PROCESS_ATTACH)
+ {
+ g_hModSelf = (HMODULE)hInstance;
+
+ _Module.Init(ObjectMap, hInstance);
+ DisableThreadLibraryCalls(hInstance);
+ }
+ else if (dwReason == DLL_PROCESS_DETACH)
+ {
+ _Module.Term();
+ }
+ return TRUE;
+}
+
+STDAPI DllCanUnloadNow(void)
+{
+ return _Module.GetLockCount() == 0 ? S_OK : S_FALSE;
+}
+
+STDAPI DllGetClassObject(REFCLSID rclsid, REFIID riid, LPVOID *ppv)
+{
+ return _Module.GetClassObject(rclsid, riid, ppv);
+}
+
+
+/*
+ * ATL::CComModule does not suport server registration/unregistration methods,
+ * so we need to do it manually. Since this is the only place we do registraton
+ * manually, we do it the quick-and-dirty way.
+ */
+
+#ifdef RT_EXCEPTIONS_ENABLED
+/* Someday we may want to log errors. */
+class AdHocRegError
+{
+public:
+ AdHocRegError(LSTATUS rc) { RT_NOREF1(rc); };
+};
+#endif
+
+/**
+ * A simple wrapper on Windows registry functions.
+ */
+class AdHocRegKey
+{
+public:
+ AdHocRegKey(HKEY hKey) : m_hKey(hKey) {};
+ AdHocRegKey(LPCWSTR pcwszName, HKEY hParent = HKEY_CLASSES_ROOT);
+ ~AdHocRegKey() { RegCloseKey(m_hKey); };
+
+ AdHocRegKey *create(LPCWSTR pcwszSubkey);
+ LSTATUS setValue(LPCWSTR pcwszName, LPCWSTR pcwszValue);
+ HKEY getKey(void) { return m_hKey; };
+private:
+ HKEY m_hKey;
+};
+
+AdHocRegKey::AdHocRegKey(LPCWSTR pcwszName, HKEY hParent) : m_hKey(NULL)
+{
+ LSTATUS rc = RegOpenKeyExW(hParent, pcwszName, 0, KEY_ALL_ACCESS, &m_hKey);
+ if (rc != ERROR_SUCCESS)
+#ifdef RT_EXCEPTIONS_ENABLED
+ throw AdHocRegError(rc);
+#else
+ m_hKey = NULL;
+#endif
+}
+
+AdHocRegKey *AdHocRegKey::create(LPCWSTR pcwszSubkey)
+{
+ HKEY hSubkey;
+ LSTATUS rc = RegCreateKeyExW(m_hKey, pcwszSubkey,
+ 0 /*Reserved*/, NULL /*pszClass*/, 0 /*fOptions*/,
+ KEY_ALL_ACCESS, NULL /*pSecAttr*/, &hSubkey, NULL /*pdwDisposition*/);
+ if (rc != ERROR_SUCCESS)
+#ifdef RT_EXCEPTIONS_ENABLED
+ throw AdHocRegError(rc);
+#else
+ return NULL;
+#endif
+ AdHocRegKey *pSubkey = new AdHocRegKey(hSubkey);
+ if (!pSubkey)
+ RegCloseKey(hSubkey);
+ return pSubkey;
+}
+
+LSTATUS AdHocRegKey::setValue(LPCWSTR pcwszName, LPCWSTR pcwszValue)
+{
+ LSTATUS rc = RegSetValueExW(m_hKey, pcwszName, 0, REG_SZ, (const BYTE *)pcwszValue,
+ (DWORD)((RTUtf16Len(pcwszValue) + 1) * sizeof(WCHAR)));
+#ifdef RT_EXCEPTIONS_ENABLED
+ if (rc != ERROR_SUCCESS)
+ throw AdHocRegError(rc);
+#endif
+ return rc;
+}
+
+/**
+ * Auxiliary class that facilitates automatic destruction of AdHocRegKey objects
+ * allocated in heap. No reference counting here!
+ */
+class AdHocRegKeyPtr
+{
+public:
+ AdHocRegKeyPtr(AdHocRegKey *pKey) : m_pKey(pKey) {};
+ ~AdHocRegKeyPtr()
+ {
+ if (m_pKey)
+ {
+ delete m_pKey;
+ m_pKey = NULL;
+ }
+ }
+
+ AdHocRegKey *create(LPCWSTR pcwszSubkey)
+ { return m_pKey ? m_pKey->create(pcwszSubkey) : NULL; };
+
+ LSTATUS setValue(LPCWSTR pcwszName, LPCWSTR pcwszValue)
+ { return m_pKey ? m_pKey->setValue(pcwszName, pcwszValue) : ERROR_INVALID_STATE; };
+
+private:
+ AdHocRegKey *m_pKey;
+ /* Prevent copying, since we do not support reference counting */
+ AdHocRegKeyPtr(const AdHocRegKeyPtr&);
+ AdHocRegKeyPtr& operator=(const AdHocRegKeyPtr&);
+};
+
+
+STDAPI DllRegisterServer(void)
+{
+ /* Get the path to the DLL we're running inside. */
+ WCHAR wszModule[MAX_PATH + 1];
+ UINT cwcModule = GetModuleFileNameW(g_hModSelf, wszModule, MAX_PATH);
+ if (cwcModule == 0 || cwcModule > MAX_PATH)
+ return SELFREG_E_CLASS;
+ wszModule[MAX_PATH] = '\0';
+
+ /*
+ * Create registry keys and values. When exceptions are disabled, we depend
+ * on setValue() to propagate fail key creation failures.
+ */
+#ifdef RT_EXCEPTIONS_ENABLED
+ try
+#endif
+ {
+ AdHocRegKey keyCLSID(L"CLSID");
+ AdHocRegKeyPtr pkeyNobjClass(keyCLSID.create(L"{f374d1a0-bf08-4bdc-9cb2-c15ddaeef955}"));
+ LSTATUS lrc = pkeyNobjClass.setValue(NULL, L"VirtualBox Bridged Networking Driver Notify Object v1.1");
+ if (lrc != ERROR_SUCCESS)
+ return SELFREG_E_CLASS;
+
+ AdHocRegKeyPtr pkeyNobjSrv(pkeyNobjClass.create(L"InProcServer32"));
+ lrc = pkeyNobjSrv.setValue(NULL, wszModule);
+ if (lrc != ERROR_SUCCESS)
+ return SELFREG_E_CLASS;
+ lrc = pkeyNobjSrv.setValue(L"ThreadingModel", L"Both");
+ if (lrc != ERROR_SUCCESS)
+ return SELFREG_E_CLASS;
+ }
+#ifdef RT_EXCEPTIONS_ENABLED
+ catch (AdHocRegError) { return SELFREG_E_CLASS; }
+#endif
+
+#ifdef RT_EXCEPTIONS_ENABLED
+ try
+#endif
+ {
+ AdHocRegKey keyTypeLib(L"TypeLib");
+ AdHocRegKeyPtr pkeyNobjLib(keyTypeLib.create(L"{2A0C94D1-40E1-439C-8FE8-24107CAB0840}\\1.1"));
+ LSTATUS lrc = pkeyNobjLib.setValue(NULL, L"VirtualBox Bridged Networking Driver Notify Object v1.1 Type Library");
+ if (lrc != ERROR_SUCCESS)
+ return SELFREG_E_TYPELIB;
+
+ AdHocRegKeyPtr pkeyNobjLib0(pkeyNobjLib.create(L"0\\win64"));
+ lrc = pkeyNobjLib0.setValue(NULL, wszModule);
+ if (lrc != ERROR_SUCCESS)
+ return SELFREG_E_TYPELIB;
+ AdHocRegKeyPtr pkeyNobjLibFlags(pkeyNobjLib.create(L"FLAGS"));
+ lrc = pkeyNobjLibFlags.setValue(NULL, L"0");
+ if (lrc != ERROR_SUCCESS)
+ return SELFREG_E_TYPELIB;
+
+ if (GetSystemDirectoryW(wszModule, MAX_PATH) == 0)
+ return SELFREG_E_TYPELIB;
+ AdHocRegKeyPtr pkeyNobjLibHelpDir(pkeyNobjLib.create(L"HELPDIR"));
+ lrc = pkeyNobjLibHelpDir.setValue(NULL, wszModule);
+ if (lrc != ERROR_SUCCESS)
+ return SELFREG_E_TYPELIB;
+ }
+#ifdef RT_EXCEPTIONS_ENABLED
+ catch (AdHocRegError) { return SELFREG_E_TYPELIB; }
+#endif
+
+ return S_OK;
+}
+
+
+STDAPI DllUnregisterServer(void)
+{
+ static struct { HKEY hKeyRoot; wchar_t const *pwszParentKey; wchar_t const *pwszKeyToDelete; HRESULT hrcFail; }
+ s_aKeys[] =
+ {
+ { HKEY_CLASSES_ROOT, L"TypeLib", L"{2A0C94D1-40E1-439C-8FE8-24107CAB0840}", SELFREG_E_TYPELIB },
+ { HKEY_CLASSES_ROOT, L"CLSID", L"{f374d1a0-bf08-4bdc-9cb2-c15ddaeef955}", SELFREG_E_CLASS },
+ };
+
+ HRESULT hrc = S_OK;
+ for (size_t i = 0; i < RT_ELEMENTS(s_aKeys); i++)
+ {
+ HKEY hKey = NULL;
+ LSTATUS lrc = RegOpenKeyExW(s_aKeys[i].hKeyRoot, s_aKeys[i].pwszParentKey, 0, KEY_ALL_ACCESS, &hKey);
+ if (lrc == ERROR_SUCCESS)
+ {
+ lrc = RegDeleteTreeW(hKey, s_aKeys[i].pwszKeyToDelete); /* Vista and later */
+ RegCloseKey(hKey);
+ }
+
+ if (lrc != ERROR_SUCCESS && lrc != ERROR_FILE_NOT_FOUND && hrc == S_OK)
+ hrc = s_aKeys[i].hrcFail;
+ }
+
+ return S_OK;
+}
+
diff --git a/src/VBox/HostDrivers/VBoxNetFlt/win/nobj/VBoxNetFltNobj.def b/src/VBox/HostDrivers/VBoxNetFlt/win/nobj/VBoxNetFltNobj.def
new file mode 100644
index 00000000..abfe3834
--- /dev/null
+++ b/src/VBox/HostDrivers/VBoxNetFlt/win/nobj/VBoxNetFltNobj.def
@@ -0,0 +1,42 @@
+; $Id: VBoxNetFltNobj.def $
+; @file
+; VBoxNetFltNobj.def - Notify Object for Bridged Networking Driver.
+; Library def file
+;
+
+;
+; Copyright (C) 2011-2023 Oracle and/or its affiliates.
+;
+; This file is part of VirtualBox base platform packages, as
+; available from https://www.virtualbox.org.
+;
+; This program is free software; you can redistribute it and/or
+; modify it under the terms of the GNU General Public License
+; as published by the Free Software Foundation, in version 3 of the
+; License.
+;
+; This program is distributed in the hope that it will be useful, but
+; WITHOUT ANY WARRANTY; without even the implied warranty of
+; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+; General Public License for more details.
+;
+; You should have received a copy of the GNU General Public License
+; along with this program; if not, see <https://www.gnu.org/licenses>.
+;
+; The contents of this file may alternatively be used under the terms
+; of the Common Development and Distribution License Version 1.0
+; (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+; in the VirtualBox distribution, in which case the provisions of the
+; CDDL are applicable instead of those of the GPL.
+;
+; You may elect to license modified versions of this file under the
+; terms and conditions of either the GPL or the CDDL or both.
+;
+; SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+;
+LIBRARY VBoxNetFltNobj
+EXPORTS
+ DllCanUnloadNow PRIVATE
+ DllGetClassObject PRIVATE
+ DllRegisterServer PRIVATE
+ DllUnregisterServer PRIVATE
diff --git a/src/VBox/HostDrivers/VBoxNetFlt/win/nobj/VBoxNetFltNobj.h b/src/VBox/HostDrivers/VBoxNetFlt/win/nobj/VBoxNetFltNobj.h
new file mode 100644
index 00000000..87eed992
--- /dev/null
+++ b/src/VBox/HostDrivers/VBoxNetFlt/win/nobj/VBoxNetFltNobj.h
@@ -0,0 +1,96 @@
+/* $Id: VBoxNetFltNobj.h $ */
+/** @file
+ * VBoxNetFltNobj.h - Notify Object for Bridged Networking Driver.
+ * Used to filter Bridged Networking Driver bindings
+ */
+/*
+ * Copyright (C) 2011-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+#ifndef VBOX_INCLUDED_SRC_VBoxNetFlt_win_nobj_VBoxNetFltNobj_h
+#define VBOX_INCLUDED_SRC_VBoxNetFlt_win_nobj_VBoxNetFltNobj_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+#include <iprt/win/windows.h>
+
+#include "VBox/com/defs.h"
+#include "VBoxNetFltNobjT.h"
+#include "VBoxNetFltNobjRc.h"
+
+#define VBOXNETFLTNOTIFY_ONFAIL_BINDDEFAULT false
+
+/*
+ * VirtualBox Bridging driver notify object.
+ * Needed to make our driver bind to "real" host adapters only
+ */
+class ATL_NO_VTABLE VBoxNetFltNobj
+ : public ATL::CComObjectRootEx<ATL::CComMultiThreadModel>
+ , public ATL::CComCoClass<VBoxNetFltNobj, &CLSID_VBoxNetFltNobj>
+ , public INetCfgComponentControl
+ , public INetCfgComponentNotifyBinding
+{
+public:
+ VBoxNetFltNobj();
+ virtual ~VBoxNetFltNobj();
+
+ BEGIN_COM_MAP(VBoxNetFltNobj)
+ COM_INTERFACE_ENTRY(INetCfgComponentControl)
+ COM_INTERFACE_ENTRY(INetCfgComponentNotifyBinding)
+ END_COM_MAP()
+
+ // this is a "just in case" conditional, which is not defined
+#ifdef VBOX_FORCE_REGISTER_SERVER
+ DECLARE_REGISTRY_RESOURCEID(IDR_VBOXNETFLT_NOBJ)
+#endif
+
+ /* INetCfgComponentControl methods */
+ STDMETHOD(Initialize)(IN INetCfgComponent *pNetCfgComponent, IN INetCfg *pNetCfg, IN BOOL bInstalling);
+ STDMETHOD(ApplyRegistryChanges)();
+ STDMETHOD(ApplyPnpChanges)(IN INetCfgPnpReconfigCallback *pCallback);
+ STDMETHOD(CancelChanges)();
+
+ /* INetCfgComponentNotifyBinding methods */
+ STDMETHOD(NotifyBindingPath)(IN DWORD dwChangeFlag, IN INetCfgBindingPath *pNetCfgBP);
+ STDMETHOD(QueryBindingPath)(IN DWORD dwChangeFlag, IN INetCfgBindingPath *pNetCfgBP);
+private:
+
+ void init(IN INetCfgComponent *pNetCfgComponent, IN INetCfg *pNetCfg, IN BOOL bInstalling);
+ void cleanup();
+
+ /* these two used to maintain the component info passed to
+ * INetCfgComponentControl::Initialize */
+ INetCfg *mpNetCfg;
+ INetCfgComponent *mpNetCfgComponent;
+ BOOL mbInstalling;
+};
+
+#endif /* !VBOX_INCLUDED_SRC_VBoxNetFlt_win_nobj_VBoxNetFltNobj_h */
diff --git a/src/VBox/HostDrivers/VBoxNetFlt/win/nobj/VBoxNetFltNobj.rc b/src/VBox/HostDrivers/VBoxNetFlt/win/nobj/VBoxNetFltNobj.rc
new file mode 100644
index 00000000..4a1398c6
--- /dev/null
+++ b/src/VBox/HostDrivers/VBoxNetFlt/win/nobj/VBoxNetFltNobj.rc
@@ -0,0 +1,78 @@
+/* $Id: VBoxNetFltNobj.rc $ */
+/** @file
+ * VBoxNetFltNobj - Resource file containing version info and icon.
+ */
+/*
+ * Copyright (C) 2011-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+#include <windows.h>
+#include <VBox/version.h>
+
+#include "VBoxNetFltNobjRc.h"
+
+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 "040904E4" // Lang=US English, CharSet=Windows Multilingual
+ BEGIN
+ VALUE "FileDescription", "VirtualBox Bridged Networking Driver Notify Object v1.1\0"
+ VALUE "InternalName", "VBoxNetFltNobj\0"
+ VALUE "OriginalFilename", "VBoxNetFltNobj.dll\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_STR
+ VALUE "ProductVersion", VBOX_RC_PRODUCT_VERSION_STR
+ VBOX_RC_MORE_STRINGS
+ END
+ END
+ BLOCK "VarFileInfo"
+ BEGIN
+ VALUE "Translation", 0x409, 1252
+ END
+END
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// REGISTRY
+//
+
+IDR_VBOXNETFLT_NOBJ REGISTRY "VBoxNetFltNobj.rgs"
+
+1 TYPELIB "VBoxNetFltNobjT.tlb"
diff --git a/src/VBox/HostDrivers/VBoxNetFlt/win/nobj/VBoxNetFltNobj.rgs b/src/VBox/HostDrivers/VBoxNetFlt/win/nobj/VBoxNetFltNobj.rgs
new file mode 100644
index 00000000..d6f9bae3
--- /dev/null
+++ b/src/VBox/HostDrivers/VBoxNetFlt/win/nobj/VBoxNetFltNobj.rgs
@@ -0,0 +1,13 @@
+HKCR
+{
+ NoRemove CLSID
+ {
+ ForceRemove {f374d1a0-bf08-4bdc-9cb2-c15ddaeef955} = s 'VirtualBox Bridged Networking Driver Notify Object v1.1'
+ {
+ InProcServer32 = s '%MODULE%'
+ {
+ val ThreadingModel = s 'Both'
+ }
+ }
+ }
+} \ No newline at end of file
diff --git a/src/VBox/HostDrivers/VBoxNetFlt/win/nobj/VBoxNetFltNobjRc.h b/src/VBox/HostDrivers/VBoxNetFlt/win/nobj/VBoxNetFltNobjRc.h
new file mode 100644
index 00000000..187af500
--- /dev/null
+++ b/src/VBox/HostDrivers/VBoxNetFlt/win/nobj/VBoxNetFltNobjRc.h
@@ -0,0 +1,46 @@
+/* $Id: VBoxNetFltNobjRc.h $ */
+/** @file
+ * VBoxNetFltNobjRc.h - Notify Object for Bridged Networking Driver.
+ * Resource definitions
+ */
+/*
+ * Copyright (C) 2011-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+#ifndef VBOX_INCLUDED_SRC_VBoxNetFlt_win_nobj_VBoxNetFltNobjRc_h
+#define VBOX_INCLUDED_SRC_VBoxNetFlt_win_nobj_VBoxNetFltNobjRc_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+/* registry script rc ID */
+#define IDR_VBOXNETFLT_NOBJ 101
+
+#endif /* !VBOX_INCLUDED_SRC_VBoxNetFlt_win_nobj_VBoxNetFltNobjRc_h */
diff --git a/src/VBox/HostDrivers/VBoxNetFlt/win/nobj/VBoxNetFltNobjT.idl b/src/VBox/HostDrivers/VBoxNetFlt/win/nobj/VBoxNetFltNobjT.idl
new file mode 100644
index 00000000..9bf8b6f9
--- /dev/null
+++ b/src/VBox/HostDrivers/VBoxNetFlt/win/nobj/VBoxNetFltNobjT.idl
@@ -0,0 +1,55 @@
+/* $Id: VBoxNetFltNobjT.idl $ */
+/** @file
+ * VBoxNetFltNobjT.idl - Notify Object for Bridged Networking Driver, typelib definition.
+ */
+
+/*
+ * Copyright (C) 2011-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+#include <netcfgn.idl>
+
+[
+ uuid(2a0c94d1-40e1-439c-8fe8-24107cab0840),
+ version(1.1),
+ helpstring("VirtualBox Bridged Networking Driver Notify Object v1.1 Type Library")
+]
+library VBoxNetFltNobjLib
+{
+ [
+ uuid(f374d1a0-bf08-4bdc-9cb2-c15ddaeef955),
+ helpstring("VirtualBox Bridged Networking Driver Notify Object Class")
+ ]
+ coclass VBoxNetFltNobj
+ {
+ [restricted] interface INetCfgComponentControl;
+ [restricted] interface INetCfgComponentNotifyBinding;
+ };
+};
diff --git a/src/VBox/HostDrivers/VBoxNetFlt/win/tools/Makefile.kup b/src/VBox/HostDrivers/VBoxNetFlt/win/tools/Makefile.kup
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/src/VBox/HostDrivers/VBoxNetFlt/win/tools/Makefile.kup
diff --git a/src/VBox/HostDrivers/VBoxNetFlt/win/tools/VBoxNetAdpInstall.cpp b/src/VBox/HostDrivers/VBoxNetFlt/win/tools/VBoxNetAdpInstall.cpp
new file mode 100644
index 00000000..40a79b1a
--- /dev/null
+++ b/src/VBox/HostDrivers/VBoxNetFlt/win/tools/VBoxNetAdpInstall.cpp
@@ -0,0 +1,341 @@
+/* $Id: VBoxNetAdpInstall.cpp $ */
+/** @file
+ * NetAdpInstall - VBoxNetAdp installer command line tool.
+ */
+
+/*
+ * Copyright (C) 2009-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include <VBox/VBoxNetCfg-win.h>
+#include <VBox/VBoxDrvCfg-win.h>
+#include <devguid.h>
+
+#include <iprt/initterm.h>
+#include <iprt/message.h>
+#include <iprt/process.h>
+#include <iprt/stream.h>
+
+
+/*********************************************************************************************************************************
+* Defined Constants And Macros *
+*********************************************************************************************************************************/
+#define VBOX_NETADP_APP_NAME L"NetAdpInstall"
+
+#define VBOX_NETADP_HWID L"sun_VBoxNetAdp"
+#ifdef NDIS60
+# define VBOX_NETADP_INF L"VBoxNetAdp6.inf"
+#else
+# define VBOX_NETADP_INF L"VBoxNetAdp.inf"
+#endif
+
+
+static DECLCALLBACK(void) winNetCfgLogger(const char *pszString)
+{
+ RTMsgInfo("%s", pszString);
+}
+
+
+/** Wrapper around GetfullPathNameW that will try an alternative INF location.
+ *
+ * The default location is the current directory. If not found there, the
+ * alternative location is the executable directory. If not found there either,
+ * the first alternative is present to the caller.
+ */
+static DWORD MyGetfullPathNameW(LPCWSTR pwszName, size_t cchFull, LPWSTR pwszFull)
+{
+ LPWSTR pwszFilePart;
+ DWORD dwSize = GetFullPathNameW(pwszName, (DWORD)cchFull, pwszFull, &pwszFilePart);
+ if (dwSize <= 0)
+ return dwSize;
+
+ /* if it doesn't exist, see if the file exists in the same directory as the executable. */
+ if (GetFileAttributesW(pwszFull) == INVALID_FILE_ATTRIBUTES)
+ {
+ WCHAR wsz[512];
+ DWORD cch = GetModuleFileNameW(GetModuleHandle(NULL), &wsz[0], RT_ELEMENTS(wsz));
+ if (cch > 0)
+ {
+ while (cch > 0 && wsz[cch - 1] != '/' && wsz[cch - 1] != '\\' && wsz[cch - 1] != ':')
+ cch--;
+ unsigned i = 0;
+ while (cch < sizeof(wsz) / sizeof(wsz[0]))
+ {
+ wsz[cch] = pwszFilePart[i++];
+ if (!wsz[cch])
+ {
+ dwSize = GetFullPathNameW(wsz, (DWORD)cchFull, pwszFull, NULL);
+ if (dwSize > 0 && GetFileAttributesW(pwszFull) != INVALID_FILE_ATTRIBUTES)
+ return dwSize;
+ break;
+ }
+ cch++;
+ }
+ }
+ }
+
+ /* fallback */
+ return GetFullPathNameW(pwszName, (DWORD)cchFull, pwszFull, NULL);
+}
+
+
+static int VBoxNetAdpInstall(void)
+{
+ RTMsgInfo("Adding host-only interface...");
+ VBoxNetCfgWinSetLogging(winNetCfgLogger);
+
+ HRESULT hr = CoInitialize(NULL);
+ if (SUCCEEDED(hr))
+ {
+ WCHAR wszInfFile[MAX_PATH];
+ DWORD cwcInfFile = MyGetfullPathNameW(VBOX_NETADP_INF, RT_ELEMENTS(wszInfFile), wszInfFile);
+ if (cwcInfFile > 0)
+ {
+ INetCfg *pnc;
+ LPWSTR lpszLockedBy = NULL;
+ hr = VBoxNetCfgWinQueryINetCfg(&pnc, TRUE, VBOX_NETADP_APP_NAME, 10000, &lpszLockedBy);
+ if (hr == S_OK)
+ {
+
+ hr = VBoxNetCfgWinNetAdpInstall(pnc, wszInfFile);
+
+ if (hr == S_OK)
+ RTMsgInfo("Installed successfully!");
+ else
+ RTMsgError("failed to install VBoxNetAdp: %Rhrc", hr);
+
+ VBoxNetCfgWinReleaseINetCfg(pnc, TRUE);
+ }
+ else
+ RTMsgError("VBoxNetCfgWinQueryINetCfg failed: %Rhrc", hr);
+ /*
+ hr = VBoxDrvCfgInfInstall(MpInf);
+ if (FAILED(hr))
+ printf("VBoxDrvCfgInfInstall failed %#x\n", hr);
+
+ GUID guid;
+ BSTR name, errMsg;
+
+ hr = VBoxNetCfgWinCreateHostOnlyNetworkInterface (MpInf, true, &guid, &name, &errMsg);
+ if (SUCCEEDED(hr))
+ {
+ ULONG ip, mask;
+ hr = VBoxNetCfgWinGenHostOnlyNetworkNetworkIp(&ip, &mask);
+ if (SUCCEEDED(hr))
+ {
+ // ip returned by VBoxNetCfgWinGenHostOnlyNetworkNetworkIp is a network ip,
+ // i.e. 192.168.xxx.0, assign 192.168.xxx.1 for the hostonly adapter
+ ip = ip | (1 << 24);
+ hr = VBoxNetCfgWinEnableStaticIpConfig(&guid, ip, mask);
+ if (SUCCEEDED(hr))
+ printf("installation successful\n");
+ else
+ printf("VBoxNetCfgWinEnableStaticIpConfig failed: hr=%#lx\n", hr);
+ }
+ else
+ printf("VBoxNetCfgWinGenHostOnlyNetworkNetworkIp failed: hr=%#lx\n", hr);
+ }
+ else
+ printf("VBoxNetCfgWinCreateHostOnlyNetworkInterface failed: hr=%#lx\n", hr);
+ */
+ }
+ else
+ {
+ DWORD dwErr = GetLastError();
+ RTMsgError("MyGetfullPathNameW failed: %Rwc", dwErr);
+ hr = HRESULT_FROM_WIN32(dwErr);
+ }
+ CoUninitialize();
+ }
+ else
+ RTMsgError("Failed initializing COM: %Rhrc", hr);
+
+ VBoxNetCfgWinSetLogging(NULL);
+
+ return SUCCEEDED(hr) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
+}
+
+static int VBoxNetAdpUninstall(void)
+{
+ RTMsgInfo("Uninstalling all host-only interfaces...");
+ VBoxNetCfgWinSetLogging(winNetCfgLogger);
+
+ HRESULT hr = CoInitialize(NULL);
+ if (SUCCEEDED(hr))
+ {
+ hr = VBoxNetCfgWinRemoveAllNetDevicesOfId(VBOX_NETADP_HWID);
+ if (SUCCEEDED(hr))
+ {
+ hr = VBoxDrvCfgInfUninstallAllSetupDi(&GUID_DEVCLASS_NET, L"Net", VBOX_NETADP_HWID, 0/* could be SUOI_FORCEDELETE */);
+ if (SUCCEEDED(hr))
+ RTMsgInfo("Uninstallation successful!");
+ else
+ RTMsgWarning("uninstalled successfully, but failed to remove infs (%Rhrc)\n", hr);
+ }
+ else
+ RTMsgError("uninstall failed: %Rhrc", hr);
+ CoUninitialize();
+ }
+ else
+ RTMsgError("Failed initializing COM: %Rhrc", hr);
+
+ VBoxNetCfgWinSetLogging(NULL);
+
+ return SUCCEEDED(hr) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
+}
+
+static int VBoxNetAdpUpdate(void)
+{
+ RTMsgInfo("Uninstalling all host-only interfaces...");
+ VBoxNetCfgWinSetLogging(winNetCfgLogger);
+
+ HRESULT hr = CoInitialize(NULL);
+ if (SUCCEEDED(hr))
+ {
+ BOOL fRebootRequired = FALSE;
+ /*
+ * Before we can update the driver for existing adapters we need to remove
+ * all old driver packages from the driver cache. Otherwise we may end up
+ * with both NDIS5 and NDIS6 versions of VBoxNetAdp in the cache which
+ * will cause all sorts of trouble.
+ */
+ VBoxDrvCfgInfUninstallAllF(L"Net", VBOX_NETADP_HWID, SUOI_FORCEDELETE);
+ hr = VBoxNetCfgWinUpdateHostOnlyNetworkInterface(VBOX_NETADP_INF, &fRebootRequired, VBOX_NETADP_HWID);
+ if (SUCCEEDED(hr))
+ {
+ if (fRebootRequired)
+ RTMsgWarning("!!REBOOT REQUIRED!!");
+ RTMsgInfo("Updated successfully!");
+ }
+ else
+ RTMsgError("update failed: %Rhrc", hr);
+
+ CoUninitialize();
+ }
+ else
+ RTMsgError("Failed initializing COM: %Rhrc", hr);
+
+ VBoxNetCfgWinSetLogging(NULL);
+
+ return SUCCEEDED(hr) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
+}
+
+static int VBoxNetAdpDisable(void)
+{
+ RTMsgInfo("Disabling all host-only interfaces...");
+ VBoxNetCfgWinSetLogging(winNetCfgLogger);
+
+ HRESULT hr = CoInitialize(NULL);
+ if (SUCCEEDED(hr))
+ {
+ hr = VBoxNetCfgWinPropChangeAllNetDevicesOfId(VBOX_NETADP_HWID, VBOXNECTFGWINPROPCHANGE_TYPE_DISABLE);
+ if (SUCCEEDED(hr))
+ RTMsgInfo("Disabling successful");
+ else
+ RTMsgError("disable failed: %Rhrc", hr);
+
+ CoUninitialize();
+ }
+ else
+ RTMsgError("Failed initializing COM: %Rhrc", hr);
+
+ VBoxNetCfgWinSetLogging(NULL);
+
+ return SUCCEEDED(hr) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
+}
+
+static int VBoxNetAdpEnable(void)
+{
+ RTMsgInfo("Enabling all host-only interfaces...");
+ VBoxNetCfgWinSetLogging(winNetCfgLogger);
+
+ HRESULT hr = CoInitialize(NULL);
+ if (SUCCEEDED(hr))
+ {
+ hr = VBoxNetCfgWinPropChangeAllNetDevicesOfId(VBOX_NETADP_HWID, VBOXNECTFGWINPROPCHANGE_TYPE_ENABLE);
+ if (SUCCEEDED(hr))
+ RTMsgInfo("Enabling successful!");
+ else
+ RTMsgError("enabling failed: %hrc", hr);
+
+ CoUninitialize();
+ }
+ else
+ RTMsgError("Failed initializing COM: %Rhrc", hr);
+
+ VBoxNetCfgWinSetLogging(NULL);
+
+ return SUCCEEDED(hr) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
+}
+
+static void printUsage(void)
+{
+ RTPrintf("host-only network adapter configuration tool\n"
+ " Usage: %s [cmd]\n"
+ " cmd can be one of the following values:\n"
+ " i - install a new host-only interface (default command)\n"
+ " u - uninstall all host-only interfaces\n"
+ " a - update the host-only driver\n"
+ " d - disable all host-only interfaces\n"
+ " e - enable all host-only interfaces\n"
+ " h - print this message\n",
+ RTProcShortName());
+}
+
+int __cdecl main(int argc, char **argv)
+{
+ RTR3InitExe(argc, &argv, 0);
+
+ if (argc < 2)
+ return VBoxNetAdpInstall();
+ if (argc > 2)
+ {
+ printUsage();
+ return RTEXITCODE_SYNTAX;
+ }
+
+ if (!strcmp(argv[1], "i"))
+ return VBoxNetAdpInstall();
+ if (!strcmp(argv[1], "u"))
+ return VBoxNetAdpUninstall();
+ if (!strcmp(argv[1], "a"))
+ return VBoxNetAdpUpdate();
+ if (!strcmp(argv[1], "d"))
+ return VBoxNetAdpDisable();
+ if (!strcmp(argv[1], "e"))
+ return VBoxNetAdpEnable();
+
+ printUsage();
+ return !strcmp(argv[1], "h") ? RTEXITCODE_SUCCESS : RTEXITCODE_SYNTAX;
+}
diff --git a/src/VBox/HostDrivers/VBoxNetFlt/win/tools/VBoxNetAdpUninstall.cpp b/src/VBox/HostDrivers/VBoxNetFlt/win/tools/VBoxNetAdpUninstall.cpp
new file mode 100644
index 00000000..03508669
--- /dev/null
+++ b/src/VBox/HostDrivers/VBoxNetFlt/win/tools/VBoxNetAdpUninstall.cpp
@@ -0,0 +1,107 @@
+/* $Id: VBoxNetAdpUninstall.cpp $ */
+/** @file
+ * NetAdpUninstall - VBoxNetAdp uninstaller command line tool
+ */
+
+/*
+ * Copyright (C) 2009-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include <VBox/VBoxNetCfg-win.h>
+#include <VBox/VBoxDrvCfg-win.h>
+
+#include <devguid.h>
+
+#include <iprt/initterm.h>
+#include <iprt/message.h>
+
+
+/*********************************************************************************************************************************
+* Defined Constants And Macros *
+*********************************************************************************************************************************/
+#ifdef NDIS60
+# define VBOX_NETADP_HWID L"sun_VBoxNetAdp6"
+#else
+# define VBOX_NETADP_HWID L"sun_VBoxNetAdp"
+#endif
+
+
+static DECLCALLBACK(void) winNetCfgLogger(const char *pszString)
+{
+ RTMsgInfo("%s", pszString);
+}
+
+static int VBoxNetAdpUninstall(void)
+{
+ RTMsgInfo("Uninstalling all Host-Only interfaces ...");
+
+ int rcExit = RTEXITCODE_FAILURE;
+ VBoxNetCfgWinSetLogging(winNetCfgLogger);
+
+ HRESULT hr = CoInitialize(NULL);
+ if (hr == S_OK)
+ {
+ hr = VBoxNetCfgWinRemoveAllNetDevicesOfId(VBOX_NETADP_HWID);
+ if (hr == S_OK)
+ {
+ hr = VBoxDrvCfgInfUninstallAllSetupDi(&GUID_DEVCLASS_NET, L"Net", VBOX_NETADP_HWID, 0/* could be SUOI_FORCEDELETE */);
+ if (hr == S_OK)
+ RTMsgInfo("Uninstalled successfully!");
+ else
+ RTMsgError("uninstalled successfully, but failed to remove infs (%Rhrc)\n", hr);
+ rcExit = RTEXITCODE_SUCCESS;
+ }
+ else
+ RTMsgError("uninstall failed: %Rhrc", hr);
+
+ CoUninitialize();
+ }
+ else
+ RTMsgError("Failed initializing COM: %Rhrc", hr);
+
+ VBoxNetCfgWinSetLogging(NULL);
+
+ return rcExit;
+}
+
+int __cdecl main(int argc, char **argv)
+{
+ RTR3InitExeNoArguments(0);
+ if (argc != 1)
+ return RTMsgErrorExit(RTEXITCODE_SYNTAX, "This utility takes no arguments\n");
+ NOREF(argv);
+
+ return VBoxNetAdpUninstall();
+}
+
diff --git a/src/VBox/HostDrivers/VBoxNetFlt/win/tools/VBoxNetFltInstall.cpp b/src/VBox/HostDrivers/VBoxNetFlt/win/tools/VBoxNetFltInstall.cpp
new file mode 100644
index 00000000..e6c4f2de
--- /dev/null
+++ b/src/VBox/HostDrivers/VBoxNetFlt/win/tools/VBoxNetFltInstall.cpp
@@ -0,0 +1,201 @@
+/* $Id: VBoxNetFltInstall.cpp $ */
+/** @file
+ * NetFltInstall - VBoxNetFlt installer command line tool
+ */
+
+/*
+ * Copyright (C) 2008-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include <VBox/VBoxNetCfg-win.h>
+#include <devguid.h>
+#include <stdio.h>
+
+#include <iprt/initterm.h>
+#include <iprt/message.h>
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#define NETFLT_ID L"sun_VBoxNetFlt"
+#define VBOX_NETCFG_APP_NAME L"NetFltInstall"
+#define VBOX_NETFLT_PT_INF L".\\VBoxNetFlt.inf"
+#define VBOX_NETFLT_MP_INF L".\\VBoxNetFltM.inf"
+#define VBOX_NETFLT_RETRIES 10
+
+
+static DECLCALLBACK(void) winNetCfgLogger(const char *pszString)
+{
+ printf("%s", pszString);
+}
+
+/** Wrapper around GetfullPathNameW that will try an alternative INF location.
+ *
+ * The default location is the current directory. If not found there, the
+ * alternative location is the executable directory. If not found there either,
+ * the first alternative is present to the caller.
+ */
+static DWORD MyGetfullPathNameW(LPCWSTR pwszName, size_t cchFull, LPWSTR pwszFull)
+{
+ LPWSTR pwszFilePart;
+ DWORD dwSize = GetFullPathNameW(pwszName, (DWORD)cchFull, pwszFull, &pwszFilePart);
+ if (dwSize <= 0)
+ return dwSize;
+
+ /* if it doesn't exist, see if the file exists in the same directory as the executable. */
+ if (GetFileAttributesW(pwszFull) == INVALID_FILE_ATTRIBUTES)
+ {
+ WCHAR wsz[512];
+ DWORD cch = GetModuleFileNameW(GetModuleHandle(NULL), &wsz[0], RT_ELEMENTS(wsz));
+ if (cch > 0)
+ {
+ while (cch > 0 && wsz[cch - 1] != '/' && wsz[cch - 1] != '\\' && wsz[cch - 1] != ':')
+ cch--;
+ unsigned i = 0;
+ while (cch < RT_ELEMENTS(wsz))
+ {
+ wsz[cch] = pwszFilePart[i++];
+ if (!wsz[cch])
+ {
+ dwSize = GetFullPathNameW(wsz, (DWORD)cchFull, pwszFull, NULL);
+ if (dwSize > 0 && GetFileAttributesW(pwszFull) != INVALID_FILE_ATTRIBUTES)
+ return dwSize;
+ break;
+ }
+ cch++;
+ }
+ }
+ }
+
+ /* fallback */
+ return GetFullPathNameW(pwszName, (DWORD)cchFull, pwszFull, NULL);
+}
+
+static int VBoxNetFltInstall()
+{
+ WCHAR wszPtInf[MAX_PATH];
+ WCHAR wszMpInf[MAX_PATH];
+ INetCfg *pnc;
+ int rcExit = RTEXITCODE_FAILURE;
+
+ VBoxNetCfgWinSetLogging(winNetCfgLogger);
+
+ HRESULT hr = CoInitialize(NULL);
+ if (hr == S_OK)
+ {
+ for (int i = 0;; i++)
+ {
+ LPWSTR pwszLockedBy = NULL;
+ hr = VBoxNetCfgWinQueryINetCfg(&pnc, TRUE, VBOX_NETCFG_APP_NAME, 10000, &pwszLockedBy);
+ if (hr == S_OK)
+ {
+ DWORD dwSize;
+ dwSize = MyGetfullPathNameW(VBOX_NETFLT_PT_INF, RT_ELEMENTS(wszPtInf), wszPtInf);
+ if (dwSize > 0)
+ {
+ /** @todo add size check for (RT_ELEMENTS(wszPtInf) == dwSize (string length in WCHARs) */
+
+ dwSize = MyGetfullPathNameW(VBOX_NETFLT_MP_INF, RT_ELEMENTS(wszMpInf), wszMpInf);
+ if (dwSize > 0)
+ {
+ /** @todo add size check for (RT_ELEMENTS(wszMpInf) == dwSize (string length in WHCARs) */
+
+ LPCWSTR apwszInfs[] = { wszPtInf, wszMpInf };
+ hr = VBoxNetCfgWinNetFltInstall(pnc, apwszInfs, 2);
+ if (hr == S_OK)
+ {
+ wprintf(L"installed successfully\n");
+ rcExit = RTEXITCODE_SUCCESS;
+ }
+ else
+ wprintf(L"error installing VBoxNetFlt (%#lx)\n", hr);
+ }
+ else
+ {
+ hr = HRESULT_FROM_WIN32(GetLastError());
+ wprintf(L"error getting full inf path for VBoxNetFltM.inf (%#lx)\n", hr);
+ }
+ }
+ else
+ {
+ hr = HRESULT_FROM_WIN32(GetLastError());
+ wprintf(L"error getting full inf path for VBoxNetFlt.inf (%#lx)\n", hr);
+ }
+
+ VBoxNetCfgWinReleaseINetCfg(pnc, TRUE);
+ break;
+ }
+
+ if (hr == NETCFG_E_NO_WRITE_LOCK && pwszLockedBy)
+ {
+ if (i < VBOX_NETFLT_RETRIES && !wcscmp(pwszLockedBy, L"6to4svc.dll"))
+ {
+ wprintf(L"6to4svc.dll is holding the lock, retrying %d out of %d\n", i + 1, VBOX_NETFLT_RETRIES);
+ CoTaskMemFree(pwszLockedBy);
+ }
+ else
+ {
+ wprintf(L"Error: write lock is owned by another application (%s), close the application and retry installing\n", pwszLockedBy);
+ CoTaskMemFree(pwszLockedBy);
+ break;
+ }
+ }
+ else
+ {
+ wprintf(L"Error getting the INetCfg interface (%#lx)\n", hr);
+ break;
+ }
+ }
+
+ CoUninitialize();
+ }
+ else
+ wprintf(L"Error initializing COM (%#lx)\n", hr);
+
+ VBoxNetCfgWinSetLogging(NULL);
+
+ return rcExit;
+}
+
+int __cdecl main(int argc, char **argv)
+{
+ RTR3InitExeNoArguments(0);
+ if (argc != 1)
+ return RTMsgErrorExit(RTEXITCODE_SYNTAX, "This utility takes no arguments\n");
+ NOREF(argv);
+
+ return VBoxNetFltInstall();
+}
+
diff --git a/src/VBox/HostDrivers/VBoxNetFlt/win/tools/VBoxNetFltUninstall.cpp b/src/VBox/HostDrivers/VBoxNetFlt/win/tools/VBoxNetFltUninstall.cpp
new file mode 100644
index 00000000..c62d7726
--- /dev/null
+++ b/src/VBox/HostDrivers/VBoxNetFlt/win/tools/VBoxNetFltUninstall.cpp
@@ -0,0 +1,133 @@
+/* $Id: VBoxNetFltUninstall.cpp $ */
+/** @file
+ * NetFltUninstall - VBoxNetFlt uninstaller command line tool
+ */
+
+/*
+ * Copyright (C) 2008-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include <VBox/VBoxNetCfg-win.h>
+#include <stdio.h>
+
+#include <iprt/initterm.h>
+#include <iprt/message.h>
+
+
+/*********************************************************************************************************************************
+* Defined Constants And Macros *
+*********************************************************************************************************************************/
+#define NETFLT_ID L"sun_VBoxNetFlt"
+#define VBOX_NETCFG_APP_NAME L"NetFltUninstall"
+#define VBOX_NETFLT_PT_INF L".\\VBoxNetFlt.inf"
+#define VBOX_NETFLT_MP_INF L".\\VBoxNetFltM.inf"
+#define VBOX_NETFLT_RETRIES 10
+
+
+static DECLCALLBACK(void) winNetCfgLogger(const char *pszString)
+{
+ printf("%s", pszString);
+}
+
+static int VBoxNetFltUninstall()
+{
+ INetCfg *pnc;
+ int rcExit = RTEXITCODE_FAILURE;
+
+ VBoxNetCfgWinSetLogging(winNetCfgLogger);
+
+ HRESULT hr = CoInitialize(NULL);
+ if (hr == S_OK)
+ {
+ for (int i = 0;; i++)
+ {
+ LPWSTR pwszLockedBy = NULL;
+ hr = VBoxNetCfgWinQueryINetCfg(&pnc, TRUE, VBOX_NETCFG_APP_NAME, 10000, &pwszLockedBy);
+ if (hr == S_OK)
+ {
+ hr = VBoxNetCfgWinNetFltUninstall(pnc);
+ if (hr != S_OK && hr != S_FALSE)
+ wprintf(L"error uninstalling VBoxNetFlt (%#lx)\n", hr);
+ else
+ {
+ wprintf(L"uninstalled successfully\n");
+ rcExit = RTEXITCODE_SUCCESS;
+ }
+
+ VBoxNetCfgWinReleaseINetCfg(pnc, TRUE);
+ break;
+ }
+
+ if (hr == NETCFG_E_NO_WRITE_LOCK && pwszLockedBy)
+ {
+ if (i < VBOX_NETFLT_RETRIES && !wcscmp(pwszLockedBy, L"6to4svc.dll"))
+ {
+ wprintf(L"6to4svc.dll is holding the lock, retrying %d out of %d\n", i + 1, VBOX_NETFLT_RETRIES);
+ CoTaskMemFree(pwszLockedBy);
+ }
+ else
+ {
+ wprintf(L"Error: write lock is owned by another application (%s), close the application and retry uninstalling\n",
+ pwszLockedBy);
+ CoTaskMemFree(pwszLockedBy);
+ break;
+ }
+ }
+ else
+ {
+ wprintf(L"Error getting the INetCfg interface (%#lx)\n", hr);
+ break;
+ }
+ }
+
+ CoUninitialize();
+ }
+ else
+ wprintf(L"Error initializing COM (%#lx)\n", hr);
+
+ VBoxNetCfgWinSetLogging(NULL);
+
+ return rcExit;
+}
+
+int __cdecl main(int argc, char **argv)
+{
+ RTR3InitExeNoArguments(0);
+ if (argc != 1)
+ return RTMsgErrorExit(RTEXITCODE_SYNTAX, "This utility takes no arguments\n");
+ NOREF(argv);
+
+ return VBoxNetFltUninstall();
+}
+
diff --git a/src/VBox/HostDrivers/VBoxNetFlt/win/tools/VBoxNetLwfInstall.cpp b/src/VBox/HostDrivers/VBoxNetFlt/win/tools/VBoxNetLwfInstall.cpp
new file mode 100644
index 00000000..1b8cf693
--- /dev/null
+++ b/src/VBox/HostDrivers/VBoxNetFlt/win/tools/VBoxNetLwfInstall.cpp
@@ -0,0 +1,186 @@
+/* $Id: VBoxNetLwfInstall.cpp $ */
+/** @file
+ * NetLwfInstall - VBoxNetLwf installer command line tool
+ */
+
+/*
+ * Copyright (C) 2014-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include <VBox/VBoxNetCfg-win.h>
+#include <devguid.h>
+
+#include <iprt/initterm.h>
+#include <iprt/message.h>
+#include <iprt/utf16.h>
+
+
+/*********************************************************************************************************************************
+* Defined Constants And Macros *
+*********************************************************************************************************************************/
+#define VBOX_NETCFG_APP_NAME L"NetLwfInstall"
+#define VBOX_NETLWF_INF L".\\VBoxNetLwf.inf"
+#define VBOX_NETLWF_RETRIES 10
+
+
+static DECLCALLBACK(void) winNetCfgLogger(const char *pszString)
+{
+ RTMsgInfo("%s", pszString);
+}
+
+/** Wrapper around GetfullPathNameW that will try an alternative INF location.
+ *
+ * The default location is the current directory. If not found there, the
+ * alternative location is the executable directory. If not found there either,
+ * the first alternative is present to the caller.
+ */
+static DWORD MyGetfullPathNameW(LPCWSTR pwszName, size_t cchFull, LPWSTR pwszFull)
+{
+ LPWSTR pwszFilePart;
+ DWORD dwSize = GetFullPathNameW(pwszName, (DWORD)cchFull, pwszFull, &pwszFilePart);
+ if (dwSize <= 0)
+ return dwSize;
+
+ /* if it doesn't exist, see if the file exists in the same directory as the executable. */
+ if (GetFileAttributesW(pwszFull) == INVALID_FILE_ATTRIBUTES)
+ {
+ WCHAR wsz[512];
+ DWORD cch = GetModuleFileNameW(GetModuleHandle(NULL), &wsz[0], RT_ELEMENTS(wsz));
+ if (cch > 0)
+ {
+ while (cch > 0 && wsz[cch - 1] != '/' && wsz[cch - 1] != '\\' && wsz[cch - 1] != ':')
+ cch--;
+ unsigned i = 0;
+ while (cch < RT_ELEMENTS(wsz))
+ {
+ wsz[cch] = pwszFilePart[i++];
+ if (!wsz[cch])
+ {
+ dwSize = GetFullPathNameW(wsz, (DWORD)cchFull, pwszFull, NULL);
+ if (dwSize > 0 && GetFileAttributesW(pwszFull) != INVALID_FILE_ATTRIBUTES)
+ return dwSize;
+ break;
+ }
+ cch++;
+ }
+ }
+ }
+
+ /* fallback */
+ return GetFullPathNameW(pwszName, (DWORD)cchFull, pwszFull, NULL);
+}
+
+static int VBoxNetLwfInstall()
+{
+ WCHAR wszInf[MAX_PATH];
+ INetCfg *pnc;
+ int rcExit = RTEXITCODE_FAILURE;
+
+ VBoxNetCfgWinSetLogging(winNetCfgLogger);
+
+ HRESULT hr = CoInitialize(NULL);
+ if (hr == S_OK)
+ {
+ for (int i = 0;; i++)
+ {
+ LPWSTR pwszLockedBy = NULL;
+ hr = VBoxNetCfgWinQueryINetCfg(&pnc, TRUE, VBOX_NETCFG_APP_NAME, 10000, &pwszLockedBy);
+ if (hr == S_OK)
+ {
+ DWORD dwSize;
+ dwSize = MyGetfullPathNameW(VBOX_NETLWF_INF, RT_ELEMENTS(wszInf), wszInf);
+ if (dwSize > 0)
+ {
+ /** @todo add size check for (RT_ELEMENTS(wszInf) == dwSize (string length in WCHARs) */
+ hr = VBoxNetCfgWinNetLwfInstall(pnc, wszInf);
+ if (hr == S_OK)
+ {
+ RTMsgInfo("Installed successfully!");
+ rcExit = RTEXITCODE_SUCCESS;
+ }
+ else
+ RTMsgError("Failed installing VBoxNetLwf: %Rhrc", hr);
+ }
+ else
+ {
+ hr = HRESULT_FROM_WIN32(GetLastError());
+ RTMsgError("Failed getting full inf path for VBoxNetLwf.inf: %Rhrc", hr);
+ }
+
+ VBoxNetCfgWinReleaseINetCfg(pnc, TRUE);
+ break;
+ }
+
+ if (hr == NETCFG_E_NO_WRITE_LOCK && pwszLockedBy)
+ {
+ if (i < VBOX_NETLWF_RETRIES && RTUtf16ICmpAscii(pwszLockedBy, "6to4svc.dll") == 0)
+ {
+ RTMsgInfo("6to4svc.dll is holding the lock - retrying %d out of %d\n", i + 1, VBOX_NETLWF_RETRIES);
+ CoTaskMemFree(pwszLockedBy);
+ }
+ else
+ {
+ RTMsgError("write lock is owned by another application (%ls), close the application and retry installing",
+ pwszLockedBy);
+ CoTaskMemFree(pwszLockedBy);
+ break;
+ }
+ }
+ else
+ {
+ RTMsgError("Failed getting the INetCfg interface: %Rhrc", hr);
+ break;
+ }
+ }
+
+ CoUninitialize();
+ }
+ else
+ RTMsgError("Failed initializing COM: %Rhrc", hr);
+
+ VBoxNetCfgWinSetLogging(NULL);
+
+ return rcExit;
+}
+
+int __cdecl main(int argc, char **argv)
+{
+ RTR3InitExeNoArguments(0);
+ if (argc != 1)
+ return RTMsgErrorExit(RTEXITCODE_SYNTAX, "This utility takes no arguments\n");
+ NOREF(argv);
+
+ return VBoxNetLwfInstall();
+}
+
diff --git a/src/VBox/HostDrivers/VBoxNetFlt/win/tools/VBoxNetLwfUninstall.cpp b/src/VBox/HostDrivers/VBoxNetFlt/win/tools/VBoxNetLwfUninstall.cpp
new file mode 100644
index 00000000..0648a127
--- /dev/null
+++ b/src/VBox/HostDrivers/VBoxNetFlt/win/tools/VBoxNetLwfUninstall.cpp
@@ -0,0 +1,130 @@
+/* $Id: VBoxNetLwfUninstall.cpp $ */
+/** @file
+ * NetLwfUninstall - VBoxNetLwf uninstaller command line tool
+ */
+
+/*
+ * Copyright (C) 2014-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include <VBox/VBoxNetCfg-win.h>
+
+#include <iprt/initterm.h>
+#include <iprt/message.h>
+#include <iprt/utf16.h>
+
+
+/*********************************************************************************************************************************
+* Defined Constants And Macros *
+*********************************************************************************************************************************/
+#define VBOX_NETCFG_APP_NAME L"NetLwfUninstall"
+#define VBOX_NETLWF_RETRIES 10
+
+
+static DECLCALLBACK(void) winNetCfgLogger(const char *pszString)
+{
+ RTMsgInfo("%s", pszString);
+}
+
+static int VBoxNetLwfUninstall()
+{
+ int rcExit = RTEXITCODE_FAILURE;
+
+ VBoxNetCfgWinSetLogging(winNetCfgLogger);
+
+ HRESULT hr = CoInitialize(NULL);
+ if (hr == S_OK)
+ {
+ for (int i = 0;; i++)
+ {
+ LPWSTR pwszLockedBy = NULL;
+ INetCfg *pnc = NULL;
+ hr = VBoxNetCfgWinQueryINetCfg(&pnc, TRUE, VBOX_NETCFG_APP_NAME, 10000, &pwszLockedBy);
+ if (hr == S_OK)
+ {
+ hr = VBoxNetCfgWinNetLwfUninstall(pnc);
+ if (hr == S_OK)
+ {
+ RTMsgInfo("uninstalled successfully!");
+ rcExit = RTEXITCODE_SUCCESS;
+ }
+ else
+ RTMsgError("error uninstalling VBoxNetLwf: %Rhrc");
+
+ VBoxNetCfgWinReleaseINetCfg(pnc, TRUE);
+ break;
+ }
+
+ if (hr == NETCFG_E_NO_WRITE_LOCK && pwszLockedBy)
+ {
+ if (i < VBOX_NETLWF_RETRIES && RTUtf16ICmpAscii(pwszLockedBy, "6to4svc.dll") == 0)
+ {
+ RTMsgInfo("6to4svc.dll is holding the lock - retry %d out of %d ...", i + 1, VBOX_NETLWF_RETRIES);
+ CoTaskMemFree(pwszLockedBy);
+ }
+ else
+ {
+ RTMsgError("Write lock is owned by another application (%ls), close the application and retry uninstalling",
+ pwszLockedBy);
+ CoTaskMemFree(pwszLockedBy);
+ break;
+ }
+ }
+ else
+ {
+ RTMsgError("Failed getting the INetCfg interface: %Rhrc", hr);
+ break;
+ }
+ }
+
+ CoUninitialize();
+ }
+ else
+ RTMsgError("Failed initializing COM: %Rhrc", hr);
+
+ VBoxNetCfgWinSetLogging(NULL);
+
+ return rcExit;
+}
+
+int __cdecl main(int argc, char **argv)
+{
+ RTR3InitExeNoArguments(0);
+ if (argc != 1)
+ return RTMsgErrorExit(RTEXITCODE_SYNTAX, "This utility takes no arguments\n");
+ NOREF(argv);
+
+ return VBoxNetLwfUninstall();
+}
+
diff --git a/src/VBox/HostDrivers/VBoxPci/Makefile.kmk b/src/VBox/HostDrivers/VBoxPci/Makefile.kmk
new file mode 100644
index 00000000..736824d3
--- /dev/null
+++ b/src/VBox/HostDrivers/VBoxPci/Makefile.kmk
@@ -0,0 +1,93 @@
+# $Id: Makefile.kmk $
+## @file
+# Sub-Makefile for the PCI passthru driver (VBoxPci).
+#
+
+#
+# Copyright (C) 2011-2023 Oracle and/or its affiliates.
+#
+# This file is part of VirtualBox base platform packages, as
+# available from https://www.virtualbox.org.
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation, in version 3 of the
+# License.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, see <https://www.gnu.org/licenses>.
+#
+# The contents of this file may alternatively be used under the terms
+# of the Common Development and Distribution License Version 1.0
+# (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+# in the VirtualBox distribution, in which case the provisions of the
+# CDDL are applicable instead of those of the GPL.
+#
+# You may elect to license modified versions of this file under the
+# terms and conditions of either the GPL or the CDDL or both.
+#
+# SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+#
+
+SUB_DEPTH = ../../../..
+include $(KBUILD_PATH)/subheader.kmk
+if1of ($(KBUILD_TARGET), linux)
+
+ ifdef VBOX_WITH_VBOXDRV
+ #
+ # The driver.
+ #
+ SYSMODS += VBoxPci
+ VBoxPci_TEMPLATE = VBoxR0Drv
+ VBoxPci_INST = $(INST_VBOXPCI)$(if $(eq $(KBUILD_TARGET),darwin),Contents/MacOS/)
+ VBoxPci_NAME.linux = vboxpci
+ VBoxPci_DEFS = IN_RT_R0 VBOX_SVN_REV=$(VBOX_SVN_REV) IN_SUP_STATIC
+ VBoxPci_INCS = \
+ .
+ VBoxPci_SOURCES = \
+ VBoxPci.c
+ VBoxPci_LIBS += \
+ $(PATH_STAGE_LIB)/SUPR0IdcClient$(VBOX_SUFF_LIB)
+ endif
+
+ #
+ # Install the sources.
+ #
+ include $(PATH_SUB_CURRENT)/linux/files_vboxpci
+ INSTALLS += VBoxPci-src
+ VBoxPci-src_INST = bin/src/vboxpci/
+ VBoxPci-src_SOURCES = \
+ $(subst $(DQUOTE),,$(VBOX_VBOXPCI_SOURCES)) \
+ $(VBoxPci-src_0_OUTDIR)/Makefile
+ VBoxPci-src_CLEAN = \
+ $(VBoxPci-src_0_OUTDIR)/Makefile \
+ $(PATH_TARGET)/VBoxPciSrc-src-1.dep
+
+ # Generate the scripts needed for building the kernel module.
+
+ includedep $(PATH_TARGET)/VBoxPci-src-1.dep
+ $$(VBoxPci-src_0_OUTDIR)/Makefile: \
+ $(PATH_SUB_CURRENT)/linux/Makefile \
+ $$(if $$(eq $$(VBoxPci/linux/Makefile_VBOX_HARDENED),$$(VBOX_WITH_HARDENING)),,FORCE) \
+ | $$(dir $$@)
+ $(QUIET)$(RM) -f -- $@
+ ifndef VBOX_WITH_HARDENING
+ $(QUIET)$(SED) -e "s;VBOX_WITH_HARDENING;;g" --output $@ $<
+ else
+ $(QUIET)$(CP) -f $< $@
+ endif
+ %$(QUIET2)$(APPEND) -t '$(PATH_TARGET)/VBoxPci-src-1.dep' 'VBoxPci/linux/Makefile_VBOX_HARDENED=$(VBOX_WITH_HARDENING)'
+
+ #
+ # Build test for the linux host kernel modules.
+ #
+ $(evalcall2 VBOX_LINUX_KMOD_TEST_BUILD_RULE_FN,VBoxPci-src,vboxdrv-src,)
+
+endif # Supported platform.
+include $(FILE_KBUILD_SUB_FOOTER)
+
diff --git a/src/VBox/HostDrivers/VBoxPci/VBoxPci.c b/src/VBox/HostDrivers/VBoxPci/VBoxPci.c
new file mode 100644
index 00000000..d4e756a4
--- /dev/null
+++ b/src/VBox/HostDrivers/VBoxPci/VBoxPci.c
@@ -0,0 +1,800 @@
+/* $Id: VBoxPci.c $ */
+/** @file
+ * VBoxPci - PCI card passthrough support (Host), Common Code.
+ */
+
+/*
+ * Copyright (C) 2011-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+/** @page pg_rawpci VBoxPci - host PCI support
+ *
+ * This is a kernel module that works as host proxy between guest and
+ * PCI hardware.
+ *
+ */
+
+#define LOG_GROUP LOG_GROUP_DEV_PCI_RAW
+#include <VBox/log.h>
+#include <VBox/err.h>
+#include <VBox/sup.h>
+#include <VBox/version.h>
+
+#include <iprt/string.h>
+#include <iprt/assert.h>
+#include <iprt/spinlock.h>
+#include <iprt/uuid.h>
+#include <iprt/asm.h>
+#include <iprt/mem.h>
+
+#include "VBoxPciInternal.h"
+
+
+#define DEVPORT_2_VBOXRAWPCIINS(pPort) \
+ ( (PVBOXRAWPCIINS)((uint8_t *)pPort - RT_OFFSETOF(VBOXRAWPCIINS, DevPort)) )
+
+
+/**
+ * Implements the SUPDRV component factor interface query method.
+ *
+ * @returns Pointer to an interface. NULL if not supported.
+ *
+ * @param pSupDrvFactory Pointer to the component factory registration structure.
+ * @param pSession The session - unused.
+ * @param pszInterfaceUuid The factory interface id.
+ */
+static DECLCALLBACK(void *) vboxPciQueryFactoryInterface(PCSUPDRVFACTORY pSupDrvFactory, PSUPDRVSESSION pSession, const char *pszInterfaceUuid)
+{
+ PVBOXRAWPCIGLOBALS pGlobals = (PVBOXRAWPCIGLOBALS)((uint8_t *)pSupDrvFactory - RT_OFFSETOF(VBOXRAWPCIGLOBALS, SupDrvFactory));
+
+ /*
+ * Convert the UUID strings and compare them.
+ */
+ RTUUID UuidReq;
+ int rc = RTUuidFromStr(&UuidReq, pszInterfaceUuid);
+ if (RT_SUCCESS(rc))
+ {
+ if (!RTUuidCompareStr(&UuidReq, RAWPCIFACTORY_UUID_STR))
+ {
+ ASMAtomicIncS32(&pGlobals->cFactoryRefs);
+ return &pGlobals->RawPciFactory;
+ }
+ }
+ else
+ Log(("VBoxRawPci: rc=%Rrc, uuid=%s\n", rc, pszInterfaceUuid));
+
+ return NULL;
+}
+DECLINLINE(int) vboxPciDevLock(PVBOXRAWPCIINS pThis)
+{
+#ifdef VBOX_WITH_SHARED_PCI_INTERRUPTS
+ RTSpinlockAcquire(pThis->hSpinlock);
+ return VINF_SUCCESS;
+#else
+ int rc = RTSemFastMutexRequest(pThis->hFastMtx);
+
+ AssertRC(rc);
+ return rc;
+#endif
+}
+
+DECLINLINE(void) vboxPciDevUnlock(PVBOXRAWPCIINS pThis)
+{
+#ifdef VBOX_WITH_SHARED_PCI_INTERRUPTS
+ RTSpinlockRelease(pThis->hSpinlock);
+#else
+ RTSemFastMutexRelease(pThis->hFastMtx);
+#endif
+}
+
+DECLINLINE(int) vboxPciVmLock(PVBOXRAWPCIDRVVM pThis)
+{
+ int rc = RTSemFastMutexRequest(pThis->hFastMtx);
+ AssertRC(rc);
+ return rc;
+}
+
+DECLINLINE(void) vboxPciVmUnlock(PVBOXRAWPCIDRVVM pThis)
+{
+ RTSemFastMutexRelease(pThis->hFastMtx);
+}
+
+DECLINLINE(int) vboxPciGlobalsLock(PVBOXRAWPCIGLOBALS pGlobals)
+{
+ int rc = RTSemFastMutexRequest(pGlobals->hFastMtx);
+ AssertRC(rc);
+ return rc;
+}
+
+DECLINLINE(void) vboxPciGlobalsUnlock(PVBOXRAWPCIGLOBALS pGlobals)
+{
+ RTSemFastMutexRelease(pGlobals->hFastMtx);
+}
+
+static PVBOXRAWPCIINS vboxPciFindInstanceLocked(PVBOXRAWPCIGLOBALS pGlobals, uint32_t iHostAddress)
+{
+ PVBOXRAWPCIINS pCur;
+ for (pCur = pGlobals->pInstanceHead; pCur != NULL; pCur = pCur->pNext)
+ {
+ if (iHostAddress == pCur->HostPciAddress)
+ return pCur;
+ }
+ return NULL;
+}
+
+static void vboxPciUnlinkInstanceLocked(PVBOXRAWPCIGLOBALS pGlobals, PVBOXRAWPCIINS pToUnlink)
+{
+ if (pGlobals->pInstanceHead == pToUnlink)
+ pGlobals->pInstanceHead = pToUnlink->pNext;
+ else
+ {
+ PVBOXRAWPCIINS pCur;
+ for (pCur = pGlobals->pInstanceHead; pCur != NULL; pCur = pCur->pNext)
+ {
+ if (pCur->pNext == pToUnlink)
+ {
+ pCur->pNext = pToUnlink->pNext;
+ break;
+ }
+ }
+ }
+ pToUnlink->pNext = NULL;
+}
+
+
+#if 0 /** @todo r=bird: Who the heck is supposed to call this?!? */
+DECLHIDDEN(void) vboxPciDevCleanup(PVBOXRAWPCIINS pThis)
+{
+ pThis->DevPort.pfnDeinit(&pThis->DevPort, 0);
+
+ if (pThis->hFastMtx)
+ {
+ RTSemFastMutexDestroy(pThis->hFastMtx);
+ pThis->hFastMtx = NIL_RTSEMFASTMUTEX;
+ }
+
+ if (pThis->hSpinlock)
+ {
+ RTSpinlockDestroy(pThis->hSpinlock);
+ pThis->hSpinlock = NIL_RTSPINLOCK;
+ }
+
+ vboxPciGlobalsLock(pThis->pGlobals);
+ vboxPciUnlinkInstanceLocked(pThis->pGlobals, pThis);
+ vboxPciGlobalsUnlock(pThis->pGlobals);
+}
+#endif
+
+
+/**
+ * @interface_method_impl{RAWPCIDEVPORT,pfnInit}
+ */
+static DECLCALLBACK(int) vboxPciDevInit(PRAWPCIDEVPORT pPort, uint32_t fFlags)
+{
+ PVBOXRAWPCIINS pThis = DEVPORT_2_VBOXRAWPCIINS(pPort);
+ int rc;
+
+ vboxPciDevLock(pThis);
+
+ rc = vboxPciOsDevInit(pThis, fFlags);
+
+ vboxPciDevUnlock(pThis);
+
+ return rc;
+}
+
+/**
+ * @interface_method_impl{RAWPCIDEVPORT,pfnDeinit}
+ */
+static DECLCALLBACK(int) vboxPciDevDeinit(PRAWPCIDEVPORT pPort, uint32_t fFlags)
+{
+ PVBOXRAWPCIINS pThis = DEVPORT_2_VBOXRAWPCIINS(pPort);
+ int rc;
+
+ vboxPciDevLock(pThis);
+
+ if (pThis->IrqHandler.pfnIrqHandler)
+ {
+ vboxPciOsDevUnregisterIrqHandler(pThis, pThis->IrqHandler.iHostIrq);
+ pThis->IrqHandler.iHostIrq = 0;
+ pThis->IrqHandler.pfnIrqHandler = NULL;
+ }
+
+ rc = vboxPciOsDevDeinit(pThis, fFlags);
+
+ vboxPciDevUnlock(pThis);
+
+ return rc;
+}
+
+
+/**
+ * @interface_method_impl{RAWPCIDEVPORT,pfnDestroy}
+ */
+static DECLCALLBACK(int) vboxPciDevDestroy(PRAWPCIDEVPORT pPort)
+{
+ PVBOXRAWPCIINS pThis = DEVPORT_2_VBOXRAWPCIINS(pPort);
+ int rc;
+
+ rc = vboxPciOsDevDestroy(pThis);
+ if (rc == VINF_SUCCESS)
+ {
+ if (pThis->hFastMtx)
+ {
+ RTSemFastMutexDestroy(pThis->hFastMtx);
+ pThis->hFastMtx = NIL_RTSEMFASTMUTEX;
+ }
+
+ if (pThis->hSpinlock)
+ {
+ RTSpinlockDestroy(pThis->hSpinlock);
+ pThis->hSpinlock = NIL_RTSPINLOCK;
+ }
+
+ vboxPciGlobalsLock(pThis->pGlobals);
+ vboxPciUnlinkInstanceLocked(pThis->pGlobals, pThis);
+ vboxPciGlobalsUnlock(pThis->pGlobals);
+
+ RTMemFree(pThis);
+ }
+
+ return rc;
+}
+/**
+ * @interface_method_impl{RAWPCIDEVPORT,pfnGetRegionInfo}
+ */
+static DECLCALLBACK(int) vboxPciDevGetRegionInfo(PRAWPCIDEVPORT pPort,
+ int32_t iRegion,
+ RTHCPHYS *pRegionStart,
+ uint64_t *pu64RegionSize,
+ bool *pfPresent,
+ uint32_t *pfFlags)
+{
+ PVBOXRAWPCIINS pThis = DEVPORT_2_VBOXRAWPCIINS(pPort);
+ int rc;
+
+ vboxPciDevLock(pThis);
+
+ rc = vboxPciOsDevGetRegionInfo(pThis, iRegion,
+ pRegionStart, pu64RegionSize,
+ pfPresent, pfFlags);
+ vboxPciDevUnlock(pThis);
+
+ return rc;
+}
+
+/**
+ * @interface_method_impl{RAWPCIDEVPORT,pfnMapRegion}
+ */
+static DECLCALLBACK(int) vboxPciDevMapRegion(PRAWPCIDEVPORT pPort,
+ int32_t iRegion,
+ RTHCPHYS RegionStart,
+ uint64_t u64RegionSize,
+ int32_t fFlags,
+ RTR0PTR *pRegionBaseR0)
+{
+ PVBOXRAWPCIINS pThis = DEVPORT_2_VBOXRAWPCIINS(pPort);
+ int rc;
+
+ vboxPciDevLock(pThis);
+
+ rc = vboxPciOsDevMapRegion(pThis, iRegion, RegionStart, u64RegionSize, fFlags, pRegionBaseR0);
+
+ vboxPciDevUnlock(pThis);
+
+ return rc;
+}
+
+/**
+ * @interface_method_impl{RAWPCIDEVPORT,pfnUnmapRegion}
+ */
+static DECLCALLBACK(int) vboxPciDevUnmapRegion(PRAWPCIDEVPORT pPort,
+ int32_t iRegion,
+ RTHCPHYS RegionStart,
+ uint64_t u64RegionSize,
+ RTR0PTR RegionBase)
+{
+ PVBOXRAWPCIINS pThis = DEVPORT_2_VBOXRAWPCIINS(pPort);
+ int rc;
+
+ vboxPciDevLock(pThis);
+
+ rc = vboxPciOsDevUnmapRegion(pThis, iRegion, RegionStart, u64RegionSize, RegionBase);
+
+ vboxPciDevUnlock(pThis);
+
+ return rc;
+}
+
+/**
+ * @interface_method_impl{RAWPCIDEVPORT,pfnPciCfgRead}
+ */
+static DECLCALLBACK(int) vboxPciDevPciCfgRead(PRAWPCIDEVPORT pPort,
+ uint32_t Register,
+ PCIRAWMEMLOC *pValue)
+{
+ PVBOXRAWPCIINS pThis = DEVPORT_2_VBOXRAWPCIINS(pPort);
+ int rc;
+
+ vboxPciDevLock(pThis);
+
+ rc = vboxPciOsDevPciCfgRead(pThis, Register, pValue);
+
+ vboxPciDevUnlock(pThis);
+
+ return rc;
+}
+
+/**
+ * @interface_method_impl{RAWPCIDEVPORT,pfnPciCfgWrite}
+ */
+static DECLCALLBACK(int) vboxPciDevPciCfgWrite(PRAWPCIDEVPORT pPort,
+ uint32_t Register,
+ PCIRAWMEMLOC *pValue)
+{
+ PVBOXRAWPCIINS pThis = DEVPORT_2_VBOXRAWPCIINS(pPort);
+ int rc;
+
+ vboxPciDevLock(pThis);
+
+ rc = vboxPciOsDevPciCfgWrite(pThis, Register, pValue);
+
+ vboxPciDevUnlock(pThis);
+
+ return rc;
+}
+
+static DECLCALLBACK(int) vboxPciDevRegisterIrqHandler(PRAWPCIDEVPORT pPort,
+ PFNRAWPCIISR pfnHandler,
+ void* pIrqContext,
+ PCIRAWISRHANDLE *phIsr)
+{
+ PVBOXRAWPCIINS pThis = DEVPORT_2_VBOXRAWPCIINS(pPort);
+ int rc;
+ int32_t iHostIrq = 0;
+
+ if (pfnHandler == NULL)
+ return VERR_INVALID_PARAMETER;
+
+ vboxPciDevLock(pThis);
+
+ if (pThis->IrqHandler.pfnIrqHandler)
+ {
+ rc = VERR_ALREADY_EXISTS;
+ }
+ else
+ {
+ rc = vboxPciOsDevRegisterIrqHandler(pThis, pfnHandler, pIrqContext, &iHostIrq);
+ if (RT_SUCCESS(rc))
+ {
+ *phIsr = 0xcafe0000;
+ pThis->IrqHandler.iHostIrq = iHostIrq;
+ pThis->IrqHandler.pfnIrqHandler = pfnHandler;
+ pThis->IrqHandler.pIrqContext = pIrqContext;
+ }
+ }
+
+ vboxPciDevUnlock(pThis);
+
+ return rc;
+}
+
+static DECLCALLBACK(int) vboxPciDevUnregisterIrqHandler(PRAWPCIDEVPORT pPort,
+ PCIRAWISRHANDLE hIsr)
+{
+ PVBOXRAWPCIINS pThis = DEVPORT_2_VBOXRAWPCIINS(pPort);
+ int rc;
+
+ if (hIsr != 0xcafe0000)
+ return VERR_INVALID_PARAMETER;
+
+ vboxPciDevLock(pThis);
+
+ rc = vboxPciOsDevUnregisterIrqHandler(pThis, pThis->IrqHandler.iHostIrq);
+ if (RT_SUCCESS(rc))
+ {
+ pThis->IrqHandler.pfnIrqHandler = NULL;
+ pThis->IrqHandler.pIrqContext = NULL;
+ pThis->IrqHandler.iHostIrq = 0;
+ }
+ vboxPciDevUnlock(pThis);
+
+ return rc;
+}
+
+static DECLCALLBACK(int) vboxPciDevPowerStateChange(PRAWPCIDEVPORT pPort,
+ PCIRAWPOWERSTATE aState,
+ uint64_t *pu64Param)
+{
+ PVBOXRAWPCIINS pThis = DEVPORT_2_VBOXRAWPCIINS(pPort);
+ int rc;
+
+ vboxPciDevLock(pThis);
+
+ rc = vboxPciOsDevPowerStateChange(pThis, aState);
+
+ switch (aState)
+ {
+ case PCIRAW_POWER_ON:
+ /*
+ * Let virtual device know about VM caps.
+ */
+ *pu64Param = VBOX_DRV_VMDATA(pThis)->pPerVmData->fVmCaps;
+ break;
+ default:
+ pu64Param = 0;
+ break;
+ }
+
+
+ vboxPciDevUnlock(pThis);
+
+ return rc;
+}
+
+/**
+ * Creates a new instance.
+ *
+ * @returns VBox status code.
+ * @param pGlobals The globals.
+ * @param u32HostAddress Host address.
+ * @param fFlags Flags.
+ * @param pVmCtx VM context.
+ * @param ppDevPort Where to store the pointer to our port interface.
+ * @param pfDevFlags The device flags.
+ */
+static int vboxPciNewInstance(PVBOXRAWPCIGLOBALS pGlobals,
+ uint32_t u32HostAddress,
+ uint32_t fFlags,
+ PRAWPCIPERVM pVmCtx,
+ PRAWPCIDEVPORT *ppDevPort,
+ uint32_t *pfDevFlags)
+{
+ int rc;
+ PVBOXRAWPCIINS pNew = (PVBOXRAWPCIINS)RTMemAllocZ(sizeof(*pNew));
+ if (!pNew)
+ return VERR_NO_MEMORY;
+
+ pNew->pGlobals = pGlobals;
+ pNew->hSpinlock = NIL_RTSPINLOCK;
+ pNew->cRefs = 1;
+ pNew->pNext = NULL;
+ pNew->HostPciAddress = u32HostAddress;
+ pNew->pVmCtx = pVmCtx;
+
+ pNew->DevPort.u32Version = RAWPCIDEVPORT_VERSION;
+
+ pNew->DevPort.pfnInit = vboxPciDevInit;
+ pNew->DevPort.pfnDeinit = vboxPciDevDeinit;
+ pNew->DevPort.pfnDestroy = vboxPciDevDestroy;
+ pNew->DevPort.pfnGetRegionInfo = vboxPciDevGetRegionInfo;
+ pNew->DevPort.pfnMapRegion = vboxPciDevMapRegion;
+ pNew->DevPort.pfnUnmapRegion = vboxPciDevUnmapRegion;
+ pNew->DevPort.pfnPciCfgRead = vboxPciDevPciCfgRead;
+ pNew->DevPort.pfnPciCfgWrite = vboxPciDevPciCfgWrite;
+ pNew->DevPort.pfnPciCfgRead = vboxPciDevPciCfgRead;
+ pNew->DevPort.pfnPciCfgWrite = vboxPciDevPciCfgWrite;
+ pNew->DevPort.pfnRegisterIrqHandler = vboxPciDevRegisterIrqHandler;
+ pNew->DevPort.pfnUnregisterIrqHandler = vboxPciDevUnregisterIrqHandler;
+ pNew->DevPort.pfnPowerStateChange = vboxPciDevPowerStateChange;
+ pNew->DevPort.u32VersionEnd = RAWPCIDEVPORT_VERSION;
+
+ rc = RTSpinlockCreate(&pNew->hSpinlock, RTSPINLOCK_FLAGS_INTERRUPT_SAFE, "VBoxPCI");
+ if (RT_SUCCESS(rc))
+ {
+ rc = RTSemFastMutexCreate(&pNew->hFastMtx);
+ if (RT_SUCCESS(rc))
+ {
+ rc = pNew->DevPort.pfnInit(&pNew->DevPort, fFlags);
+ if (RT_SUCCESS(rc))
+ {
+ *ppDevPort = &pNew->DevPort;
+
+ pNew->pNext = pGlobals->pInstanceHead;
+ pGlobals->pInstanceHead = pNew;
+ }
+ else
+ {
+ RTSemFastMutexDestroy(pNew->hFastMtx);
+ RTSpinlockDestroy(pNew->hSpinlock);
+ RTMemFree(pNew);
+ }
+ }
+ }
+
+ return rc;
+}
+
+/**
+ * @interface_method_impl{RAWPCIFACTORY,pfnCreateAndConnect}
+ */
+static DECLCALLBACK(int) vboxPciFactoryCreateAndConnect(PRAWPCIFACTORY pFactory,
+ uint32_t u32HostAddress,
+ uint32_t fFlags,
+ PRAWPCIPERVM pVmCtx,
+ PRAWPCIDEVPORT *ppDevPort,
+ uint32_t *pfDevFlags)
+{
+ PVBOXRAWPCIGLOBALS pGlobals = (PVBOXRAWPCIGLOBALS)((uint8_t *)pFactory - RT_OFFSETOF(VBOXRAWPCIGLOBALS, RawPciFactory));
+ int rc;
+
+ LogFlow(("vboxPciFactoryCreateAndConnect: PCI=%x fFlags=%#x\n", u32HostAddress, fFlags));
+ Assert(pGlobals->cFactoryRefs > 0);
+ rc = vboxPciGlobalsLock(pGlobals);
+ AssertRCReturn(rc, rc);
+
+ /* First search if there's no existing instance with same host device
+ * address - if so - we cannot continue.
+ */
+ if (vboxPciFindInstanceLocked(pGlobals, u32HostAddress) != NULL)
+ {
+ rc = VERR_RESOURCE_BUSY;
+ goto unlock;
+ }
+
+ rc = vboxPciNewInstance(pGlobals, u32HostAddress, fFlags, pVmCtx, ppDevPort, pfDevFlags);
+
+unlock:
+ vboxPciGlobalsUnlock(pGlobals);
+
+ return rc;
+}
+
+/**
+ * @interface_method_impl{RAWPCIFACTORY,pfnRelease}
+ */
+static DECLCALLBACK(void) vboxPciFactoryRelease(PRAWPCIFACTORY pFactory)
+{
+ PVBOXRAWPCIGLOBALS pGlobals = (PVBOXRAWPCIGLOBALS)((uint8_t *)pFactory - RT_OFFSETOF(VBOXRAWPCIGLOBALS, RawPciFactory));
+
+ int32_t cRefs = ASMAtomicDecS32(&pGlobals->cFactoryRefs);
+ Assert(cRefs >= 0); NOREF(cRefs);
+ LogFlow(("vboxPciFactoryRelease: cRefs=%d (new)\n", cRefs));
+}
+
+/**
+ * @interface_method_impl{RAWPCIFACTORY,pfnInitVm}
+ */
+static DECLCALLBACK(int) vboxPciFactoryInitVm(PRAWPCIFACTORY pFactory,
+ PVM pVM,
+ PRAWPCIPERVM pVmData)
+{
+ PVBOXRAWPCIDRVVM pThis = (PVBOXRAWPCIDRVVM)RTMemAllocZ(sizeof(VBOXRAWPCIDRVVM));
+ int rc;
+
+ if (!pThis)
+ return VERR_NO_MEMORY;
+
+ rc = RTSemFastMutexCreate(&pThis->hFastMtx);
+ if (RT_SUCCESS(rc))
+ {
+ rc = vboxPciOsInitVm(pThis, pVM, pVmData);
+
+ if (RT_SUCCESS(rc))
+ {
+#ifdef VBOX_WITH_IOMMU
+ /* If IOMMU notification routine in pVmData->pfnContigMemInfo
+ is set - we have functional IOMMU hardware. */
+ if (pVmData->pfnContigMemInfo)
+ pVmData->fVmCaps |= PCIRAW_VMFLAGS_HAS_IOMMU;
+#endif
+ pThis->pPerVmData = pVmData;
+ pVmData->pDriverData = pThis;
+ return VINF_SUCCESS;
+ }
+
+ RTSemFastMutexDestroy(pThis->hFastMtx);
+ pThis->hFastMtx = NIL_RTSEMFASTMUTEX;
+ RTMemFree(pThis);
+ }
+
+ return rc;
+}
+
+/**
+ * @interface_method_impl{RAWPCIFACTORY,pfnDeinitVm}
+ */
+static DECLCALLBACK(void) vboxPciFactoryDeinitVm(PRAWPCIFACTORY pFactory,
+ PVM pVM,
+ PRAWPCIPERVM pVmData)
+{
+ if (pVmData->pDriverData)
+ {
+ PVBOXRAWPCIDRVVM pThis = (PVBOXRAWPCIDRVVM)pVmData->pDriverData;
+
+#ifdef VBOX_WITH_IOMMU
+ /* If we have IOMMU, need to unmap all guest's physical pages from IOMMU on VM termination. */
+#endif
+
+ vboxPciOsDeinitVm(pThis, pVM);
+
+ if (pThis->hFastMtx)
+ {
+ RTSemFastMutexDestroy(pThis->hFastMtx);
+ pThis->hFastMtx = NIL_RTSEMFASTMUTEX;
+ }
+
+ RTMemFree(pThis);
+ pVmData->pDriverData = NULL;
+ }
+}
+
+
+static bool vboxPciCanUnload(PVBOXRAWPCIGLOBALS pGlobals)
+{
+ int rc = vboxPciGlobalsLock(pGlobals);
+ bool fRc = !pGlobals->pInstanceHead
+ && pGlobals->cFactoryRefs <= 0;
+ vboxPciGlobalsUnlock(pGlobals);
+ AssertRC(rc);
+ return fRc;
+}
+
+
+static int vboxPciInitIdc(PVBOXRAWPCIGLOBALS pGlobals)
+{
+ int rc;
+ Assert(!pGlobals->fIDCOpen);
+
+ /*
+ * Establish a connection to SUPDRV and register our component factory.
+ */
+ rc = SUPR0IdcOpen(&pGlobals->SupDrvIDC, 0 /* iReqVersion = default */, 0 /* iMinVersion = default */, NULL, NULL, NULL);
+ if (RT_SUCCESS(rc))
+ {
+ rc = SUPR0IdcComponentRegisterFactory(&pGlobals->SupDrvIDC, &pGlobals->SupDrvFactory);
+ if (RT_SUCCESS(rc))
+ {
+ pGlobals->fIDCOpen = true;
+ Log(("VBoxRawPci: pSession=%p\n", SUPR0IdcGetSession(&pGlobals->SupDrvIDC)));
+ return rc;
+ }
+
+ /* bail out. */
+ LogRel(("VBoxRawPci: Failed to register component factory, rc=%Rrc\n", rc));
+ SUPR0IdcClose(&pGlobals->SupDrvIDC);
+ }
+
+ return rc;
+}
+
+
+/**
+ * Try to close the IDC connection to SUPDRV if established.
+ *
+ * @returns VBox status code.
+ * @retval VINF_SUCCESS on success.
+ * @retval VERR_WRONG_ORDER if we're busy.
+ *
+ * @param pGlobals Pointer to the globals.
+ */
+static int vboxPciDeleteIdc(PVBOXRAWPCIGLOBALS pGlobals)
+{
+ int rc;
+
+ Assert(pGlobals->hFastMtx != NIL_RTSEMFASTMUTEX);
+
+ /*
+ * Check before trying to deregister the factory.
+ */
+ if (!vboxPciCanUnload(pGlobals))
+ return VERR_WRONG_ORDER;
+
+ if (!pGlobals->fIDCOpen)
+ rc = VINF_SUCCESS;
+ else
+ {
+ /*
+ * Disconnect from SUPDRV.
+ */
+ rc = SUPR0IdcComponentDeregisterFactory(&pGlobals->SupDrvIDC, &pGlobals->SupDrvFactory);
+ AssertRC(rc);
+ SUPR0IdcClose(&pGlobals->SupDrvIDC);
+ pGlobals->fIDCOpen = false;
+ }
+
+ return rc;
+}
+
+
+/**
+ * Initializes the globals.
+ *
+ * @returns VBox status code.
+ * @param pGlobals Pointer to the globals.
+ */
+static int vboxPciInitGlobals(PVBOXRAWPCIGLOBALS pGlobals)
+{
+ /*
+ * Initialize the common portions of the structure.
+ */
+ int rc = RTSemFastMutexCreate(&pGlobals->hFastMtx);
+ if (RT_SUCCESS(rc))
+ {
+ pGlobals->pInstanceHead = NULL;
+ pGlobals->RawPciFactory.pfnRelease = vboxPciFactoryRelease;
+ pGlobals->RawPciFactory.pfnCreateAndConnect = vboxPciFactoryCreateAndConnect;
+ pGlobals->RawPciFactory.pfnInitVm = vboxPciFactoryInitVm;
+ pGlobals->RawPciFactory.pfnDeinitVm = vboxPciFactoryDeinitVm;
+ memcpy(pGlobals->SupDrvFactory.szName, "VBoxRawPci", sizeof("VBoxRawPci"));
+ pGlobals->SupDrvFactory.pfnQueryFactoryInterface = vboxPciQueryFactoryInterface;
+ pGlobals->fIDCOpen = false;
+ }
+ return rc;
+}
+
+
+/**
+ * Deletes the globals.
+ *
+ * @param pGlobals Pointer to the globals.
+ */
+static void vboxPciDeleteGlobals(PVBOXRAWPCIGLOBALS pGlobals)
+{
+ Assert(!pGlobals->fIDCOpen);
+
+ /*
+ * Release resources.
+ */
+ if (pGlobals->hFastMtx)
+ {
+ RTSemFastMutexDestroy(pGlobals->hFastMtx);
+ pGlobals->hFastMtx = NIL_RTSEMFASTMUTEX;
+ }
+}
+
+
+int vboxPciInit(PVBOXRAWPCIGLOBALS pGlobals)
+{
+
+ /*
+ * Initialize the common portions of the structure.
+ */
+ int rc = vboxPciInitGlobals(pGlobals);
+ if (RT_SUCCESS(rc))
+ {
+ rc = vboxPciInitIdc(pGlobals);
+ if (RT_SUCCESS(rc))
+ return rc;
+
+ /* bail out. */
+ vboxPciDeleteGlobals(pGlobals);
+ }
+
+ return rc;
+}
+
+void vboxPciShutdown(PVBOXRAWPCIGLOBALS pGlobals)
+{
+ int rc = vboxPciDeleteIdc(pGlobals);
+ if (RT_SUCCESS(rc))
+ vboxPciDeleteGlobals(pGlobals);
+}
+
diff --git a/src/VBox/HostDrivers/VBoxPci/VBoxPciInternal.h b/src/VBox/HostDrivers/VBoxPci/VBoxPciInternal.h
new file mode 100644
index 00000000..c82cd18d
--- /dev/null
+++ b/src/VBox/HostDrivers/VBoxPci/VBoxPciInternal.h
@@ -0,0 +1,213 @@
+/* $Id: VBoxPciInternal.h $ */
+/** @file
+ * VBoxPci - PCI driver (Host), Internal Header.
+ */
+
+/*
+ * Copyright (C) 2011-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+#ifndef VBOX_INCLUDED_SRC_VBoxPci_VBoxPciInternal_h
+#define VBOX_INCLUDED_SRC_VBoxPci_VBoxPciInternal_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+#include <VBox/sup.h>
+#include <VBox/rawpci.h>
+#include <iprt/semaphore.h>
+#include <iprt/assert.h>
+
+#ifdef RT_OS_LINUX
+
+#if RTLNX_VER_MIN(2,6,35) && defined(CONFIG_IOMMU_API)
+# define VBOX_WITH_IOMMU
+#endif
+
+#ifdef VBOX_WITH_IOMMU
+#include <linux/errno.h>
+#include <linux/iommu.h>
+#endif
+
+#endif
+
+RT_C_DECLS_BEGIN
+
+/* Forward declaration. */
+typedef struct VBOXRAWPCIGLOBALS *PVBOXRAWPCIGLOBALS;
+typedef struct VBOXRAWPCIDRVVM *PVBOXRAWPCIDRVVM;
+typedef struct VBOXRAWPCIINS *PVBOXRAWPCIINS;
+
+typedef struct VBOXRAWPCIISRDESC
+{
+ /** Handler function. */
+ PFNRAWPCIISR pfnIrqHandler;
+ /** Handler context. */
+ void *pIrqContext;
+ /** Host IRQ. */
+ int32_t iHostIrq;
+} VBOXRAWPCIISRDESC;
+typedef struct VBOXRAWPCIISRDESC *PVBOXRAWPCIISRDESC;
+
+/**
+ * The per-instance data of the VBox raw PCI interface.
+ *
+ * This is data associated with a host PCI card attached to the VM.
+ *
+ */
+typedef struct VBOXRAWPCIINS
+{
+ /** Pointer to the globals. */
+ PVBOXRAWPCIGLOBALS pGlobals;
+
+ /** Mutex protecting device access. */
+ RTSEMFASTMUTEX hFastMtx;
+ /** The spinlock protecting the state variables and device access. */
+ RTSPINLOCK hSpinlock;
+ /** Pointer to the next device in the list. */
+ PVBOXRAWPCIINS pNext;
+ /** Reference count. */
+ uint32_t volatile cRefs;
+
+ /* Host PCI address of this device. */
+ uint32_t HostPciAddress;
+
+#ifdef RT_OS_LINUX
+ struct pci_dev * pPciDev;
+ char szPrevDriver[64];
+#endif
+ bool fMsiUsed;
+ bool fMsixUsed;
+ bool fIommuUsed;
+ bool fPad0;
+
+ /** Port, given to the outside world. */
+ RAWPCIDEVPORT DevPort;
+
+ /** IRQ handler. */
+ VBOXRAWPCIISRDESC IrqHandler;
+
+ /** Pointer to per-VM context in hypervisor data. */
+ PRAWPCIPERVM pVmCtx;
+
+ RTR0PTR aRegionR0Mapping[/* XXX: magic */ 7];
+} VBOXRAWPCIINS;
+
+/**
+ * Per-VM data of the VBox PCI driver. Pointed to by pGVM->rawpci.s.pDriverData.
+ *
+ */
+typedef struct VBOXRAWPCIDRVVM
+{
+ /** Mutex protecting state changes. */
+ RTSEMFASTMUTEX hFastMtx;
+
+#ifdef RT_OS_LINUX
+# ifdef VBOX_WITH_IOMMU
+ /* IOMMU domain. */
+ struct iommu_domain* pIommuDomain;
+# endif
+#endif
+ /* Back pointer to pGVM->rawpci.s. */
+ PRAWPCIPERVM pPerVmData;
+} VBOXRAWPCIDRVVM;
+
+/**
+ * The global data of the VBox PCI driver.
+ *
+ * This contains the bit required for communicating with support driver, VBoxDrv
+ * (start out as SupDrv).
+ */
+typedef struct VBOXRAWPCIGLOBALS
+{
+ /** Mutex protecting the list of instances and state changes. */
+ RTSEMFASTMUTEX hFastMtx;
+
+ /** Pointer to a list of instance data. */
+ PVBOXRAWPCIINS pInstanceHead;
+
+ /** The raw PCI interface factory. */
+ RAWPCIFACTORY RawPciFactory;
+ /** The SUPDRV component factory registration. */
+ SUPDRVFACTORY SupDrvFactory;
+ /** The number of current factory references. */
+ int32_t volatile cFactoryRefs;
+ /** Whether the IDC connection is open or not.
+ * This is only for cleaning up correctly after the separate IDC init on Windows. */
+ bool fIDCOpen;
+ /** The SUPDRV IDC handle (opaque struct). */
+ SUPDRVIDCHANDLE SupDrvIDC;
+#ifdef RT_OS_LINUX
+ bool fPciStubModuleAvail;
+ struct module * pciStubModule;
+#endif
+} VBOXRAWPCIGLOBALS;
+
+DECLHIDDEN(int) vboxPciInit(PVBOXRAWPCIGLOBALS pGlobals);
+DECLHIDDEN(void) vboxPciShutdown(PVBOXRAWPCIGLOBALS pGlobals);
+
+DECLHIDDEN(int) vboxPciOsInitVm(PVBOXRAWPCIDRVVM pThis, PVM pVM, PRAWPCIPERVM pVmData);
+DECLHIDDEN(void) vboxPciOsDeinitVm(PVBOXRAWPCIDRVVM pThis, PVM pVM);
+
+DECLHIDDEN(int) vboxPciOsDevInit (PVBOXRAWPCIINS pIns, uint32_t fFlags);
+DECLHIDDEN(int) vboxPciOsDevDeinit(PVBOXRAWPCIINS pIns, uint32_t fFlags);
+DECLHIDDEN(int) vboxPciOsDevDestroy(PVBOXRAWPCIINS pIns);
+
+DECLHIDDEN(int) vboxPciOsDevGetRegionInfo(PVBOXRAWPCIINS pIns,
+ int32_t iRegion,
+ RTHCPHYS *pRegionStart,
+ uint64_t *pu64RegionSize,
+ bool *pfPresent,
+ uint32_t *pfFlags);
+DECLHIDDEN(int) vboxPciOsDevMapRegion(PVBOXRAWPCIINS pIns,
+ int32_t iRegion,
+ RTHCPHYS pRegionStart,
+ uint64_t u64RegionSize,
+ uint32_t fFlags,
+ RTR0PTR *pRegionBase);
+DECLHIDDEN(int) vboxPciOsDevUnmapRegion(PVBOXRAWPCIINS pIns,
+ int32_t iRegion,
+ RTHCPHYS RegionStart,
+ uint64_t u64RegionSize,
+ RTR0PTR RegionBase);
+
+DECLHIDDEN(int) vboxPciOsDevPciCfgWrite(PVBOXRAWPCIINS pIns, uint32_t Register, PCIRAWMEMLOC *pValue);
+DECLHIDDEN(int) vboxPciOsDevPciCfgRead (PVBOXRAWPCIINS pIns, uint32_t Register, PCIRAWMEMLOC *pValue);
+
+DECLHIDDEN(int) vboxPciOsDevRegisterIrqHandler (PVBOXRAWPCIINS pIns, PFNRAWPCIISR pfnHandler, void* pIrqContext, int32_t *piHostIrq);
+DECLHIDDEN(int) vboxPciOsDevUnregisterIrqHandler(PVBOXRAWPCIINS pIns, int32_t iHostIrq);
+
+DECLHIDDEN(int) vboxPciOsDevPowerStateChange(PVBOXRAWPCIINS pIns, PCIRAWPOWERSTATE aState);
+
+#define VBOX_DRV_VMDATA(pIns) ((PVBOXRAWPCIDRVVM)(pIns->pVmCtx ? pIns->pVmCtx->pDriverData : NULL))
+
+RT_C_DECLS_END
+
+#endif /* !VBOX_INCLUDED_SRC_VBoxPci_VBoxPciInternal_h */
diff --git a/src/VBox/HostDrivers/VBoxPci/linux/Makefile b/src/VBox/HostDrivers/VBoxPci/linux/Makefile
new file mode 100644
index 00000000..bbcefe76
--- /dev/null
+++ b/src/VBox/HostDrivers/VBoxPci/linux/Makefile
@@ -0,0 +1,81 @@
+# $Id: Makefile $
+## @file
+# Makefile for the VirtualBox Linux Host PCI Driver.
+#
+
+#
+# Copyright (C) 2011-2023 Oracle and/or its affiliates.
+#
+# This file is part of VirtualBox base platform packages, as
+# available from https://www.virtualbox.org.
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation, in version 3 of the
+# License.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, see <https://www.gnu.org/licenses>.
+#
+# The contents of this file may alternatively be used under the terms
+# of the Common Development and Distribution License Version 1.0
+# (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+# in the VirtualBox distribution, in which case the provisions of the
+# CDDL are applicable instead of those of the GPL.
+#
+# You may elect to license modified versions of this file under the
+# terms and conditions of either the GPL or the CDDL or both.
+#
+# SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+#
+
+
+# Linux kbuild sets this to our source directory if we are called from there
+obj ?= $(CURDIR)
+include $(obj)/Makefile-header.gmk
+VBOXPCI_DIR := $(VBOX_MODULE_SRC_DIR)
+
+# Allow building directly from the subdirectory without assuming the toplevel
+# makefile has done the copying. Not the default use case, but can be handy.
+ifndef KBUILD_EXTRA_SYMBOLS
+KBUILD_EXTRA_SYMBOLS=$(abspath $(VBOXPCI_DIR)/../vboxdrv/Module.symvers)
+endif
+
+# override is required by the Debian guys
+VBOXMOD_NAME = vboxpci
+VBOXMOD_OBJS = \
+ linux/VBoxPci-linux.o \
+ VBoxPci.o \
+ SUPR0IdcClient.o \
+ SUPR0IdcClientComponent.o \
+ linux/SUPR0IdcClient-linux.o
+ifeq ($(VBOX_KBUILD_TARGET_ARCH),x86)
+VBOXMOD_OBJS += \
+ math/gcc/divdi3.o \
+ math/gcc/moddi3.o \
+ math/gcc/qdivrem.o \
+ math/gcc/udivdi3.o \
+ math/gcc/udivmoddi4.o \
+ math/gcc/divdi3.o \
+ math/gcc/umoddi3.o
+endif
+VBOXMOD_INCL = \
+ $(VBOXPCI_DIR) \
+ $(VBOXPCI_DIR)include \
+ $(VBOXPCI_DIR)r0drv/linux
+VBOXMOD_DEFS = \
+ RT_OS_LINUX \
+ IN_RING0 \
+ IN_RT_R0 \
+ IN_SUP_R0 \
+ VBOX \
+ RT_WITH_VBOX \
+ VBOX_WITH_HARDENING
+VBOXMOD_CFLAGS = -include $(VBOXPCI_DIR)include/VBox/SUPDrvMangling.h -fno-pie
+
+include $(obj)/Makefile-footer.gmk
diff --git a/src/VBox/HostDrivers/VBoxPci/linux/Makefile.kup b/src/VBox/HostDrivers/VBoxPci/linux/Makefile.kup
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/src/VBox/HostDrivers/VBoxPci/linux/Makefile.kup
diff --git a/src/VBox/HostDrivers/VBoxPci/linux/VBoxPci-linux.c b/src/VBox/HostDrivers/VBoxPci/linux/VBoxPci-linux.c
new file mode 100644
index 00000000..36685dd9
--- /dev/null
+++ b/src/VBox/HostDrivers/VBoxPci/linux/VBoxPci-linux.c
@@ -0,0 +1,1186 @@
+/* $Id: VBoxPci-linux.c $ */
+/** @file
+ * VBoxPci - PCI Driver (Host), Linux Specific Code.
+ */
+
+/*
+ * Copyright (C) 2011-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include "the-linux-kernel.h"
+#include "version-generated.h"
+#include "revision-generated.h"
+#include "product-generated.h"
+
+#define LOG_GROUP LOG_GROUP_DEV_PCI_RAW
+#include <VBox/log.h>
+#include <VBox/err.h>
+#include <iprt/process.h>
+#include <iprt/initterm.h>
+#include <iprt/string.h>
+#include <iprt/mem.h>
+
+#include "../VBoxPciInternal.h"
+
+#ifdef VBOX_WITH_IOMMU
+# include <linux/dmar.h>
+# include <linux/intel-iommu.h>
+# include <linux/pci.h>
+# if RTLNX_VER_MAX(3,1,0) && \
+ (RTLNX_VER_MAX(2,6,41) || RTLNX_VER_MIN(3,0,0))
+# include <asm/amd_iommu.h>
+# else
+# include <linux/amd-iommu.h>
+# endif
+# if RTLNX_VER_MAX(3,2,0)
+# define IOMMU_PRESENT() iommu_found()
+# define IOMMU_DOMAIN_ALLOC() iommu_domain_alloc()
+# else
+# define IOMMU_PRESENT() iommu_present(&pci_bus_type)
+# define IOMMU_DOMAIN_ALLOC() iommu_domain_alloc(&pci_bus_type)
+# endif
+#endif /* VBOX_WITH_IOMMU */
+
+
+/*********************************************************************************************************************************
+* Internal Functions *
+*********************************************************************************************************************************/
+static int __init VBoxPciLinuxInit(void);
+static void __exit VBoxPciLinuxUnload(void);
+
+
+/*********************************************************************************************************************************
+* Global Variables *
+*********************************************************************************************************************************/
+static VBOXRAWPCIGLOBALS g_VBoxPciGlobals;
+
+module_init(VBoxPciLinuxInit);
+module_exit(VBoxPciLinuxUnload);
+
+MODULE_AUTHOR(VBOX_VENDOR);
+MODULE_DESCRIPTION(VBOX_PRODUCT " PCI access Driver");
+MODULE_LICENSE("GPL");
+#ifdef MODULE_VERSION
+MODULE_VERSION(VBOX_VERSION_STRING " r" RT_XSTR(VBOX_SVN_REV));
+#endif
+
+
+#if RTLNX_VER_MIN(2,6,20)
+# define PCI_DEV_GET(v,d,p) pci_get_device(v,d,p)
+# define PCI_DEV_PUT(x) pci_dev_put(x)
+#if RTLNX_VER_MIN(4,17,0)
+/* assume the domain number to be zero - exactly the same assumption of
+ * pci_get_bus_and_slot()
+ */
+# define PCI_DEV_GET_SLOT(bus, devfn) pci_get_domain_bus_and_slot(0, bus, devfn)
+#else
+# define PCI_DEV_GET_SLOT(bus, devfn) pci_get_bus_and_slot(bus, devfn)
+#endif
+#else
+# define PCI_DEV_GET(v,d,p) pci_find_device(v,d,p)
+# define PCI_DEV_PUT(x) do { } while (0)
+# define PCI_DEV_GET_SLOT(bus, devfn) pci_find_slot(bus, devfn)
+#endif
+
+/**
+ * Name of module used to attach to the host PCI device, when
+ * PCI device passthrough is used.
+ */
+#define PCI_STUB_MODULE "pci-stub"
+/* For some reasons my kernel names module for find_module() this way,
+ * while device name seems to be above one.
+ */
+#define PCI_STUB_MODULE_NAME "pci_stub"
+
+/**
+ * Our driver name.
+ */
+#define DRIVER_NAME "vboxpci"
+
+/*
+ * Currently we keep the device bound to pci stub driver, so
+ * dev_printk() &co would report that instead of our name. They also
+ * expect non-NULL dev pointer in older kernels.
+ */
+#define vbpci_printk(level, pdev, format, arg...) \
+ printk(level DRIVER_NAME "%s%s: " format, \
+ pdev ? " " : "", pdev ? pci_name(pdev) : "", \
+ ## arg)
+
+
+/**
+ * Initialize module.
+ *
+ * @returns appropriate status code.
+ */
+static int __init VBoxPciLinuxInit(void)
+{
+ int rc;
+ /*
+ * Initialize IPRT.
+ */
+ rc = RTR0Init(0);
+
+ if (RT_FAILURE(rc))
+ goto error;
+
+
+ LogRel(("VBoxPciLinuxInit\n"));
+
+ RT_ZERO(g_VBoxPciGlobals);
+
+ rc = vboxPciInit(&g_VBoxPciGlobals);
+ if (RT_FAILURE(rc))
+ {
+ LogRel(("cannot do VBoxPciInit: %Rc\n", rc));
+ goto error;
+ }
+
+#if defined(CONFIG_PCI_STUB)
+ /* nothing to do, pci_stub module part of the kernel */
+ g_VBoxPciGlobals.fPciStubModuleAvail = true;
+
+#elif defined(CONFIG_PCI_STUB_MODULE)
+ if (request_module(PCI_STUB_MODULE) == 0)
+ {
+# if RTLNX_VER_MIN(2,6,30)
+ /* find_module() is static before Linux 2.6.30 */
+ mutex_lock(&module_mutex);
+ g_VBoxPciGlobals.pciStubModule = find_module(PCI_STUB_MODULE_NAME);
+ mutex_unlock(&module_mutex);
+ if (g_VBoxPciGlobals.pciStubModule)
+ {
+ if (try_module_get(g_VBoxPciGlobals.pciStubModule))
+ g_VBoxPciGlobals.fPciStubModuleAvail = true;
+ }
+ else
+ printk(KERN_INFO "vboxpci: find_module %s failed\n", PCI_STUB_MODULE);
+# endif
+ }
+ else
+ printk(KERN_INFO "vboxpci: cannot load %s\n", PCI_STUB_MODULE);
+
+#else
+ printk(KERN_INFO "vboxpci: %s module not available, cannot detach PCI devices\n",
+ PCI_STUB_MODULE);
+#endif
+
+#ifdef VBOX_WITH_IOMMU
+ if (IOMMU_PRESENT())
+ printk(KERN_INFO "vboxpci: IOMMU found\n");
+ else
+ printk(KERN_INFO "vboxpci: IOMMU not found (not registered)\n");
+#else
+ printk(KERN_INFO "vboxpci: IOMMU not found (not compiled)\n");
+#endif
+
+ return 0;
+
+ error:
+ return -RTErrConvertToErrno(rc);
+}
+
+/**
+ * Unload the module.
+ */
+static void __exit VBoxPciLinuxUnload(void)
+{
+ LogRel(("VBoxPciLinuxLinuxUnload\n"));
+
+ /*
+ * Undo the work done during start (in reverse order).
+ */
+ vboxPciShutdown(&g_VBoxPciGlobals);
+
+ RTR0Term();
+
+ if (g_VBoxPciGlobals.pciStubModule)
+ {
+ module_put(g_VBoxPciGlobals.pciStubModule);
+ g_VBoxPciGlobals.pciStubModule = NULL;
+ }
+
+ Log(("VBoxPciLinuxUnload - done\n"));
+}
+
+static int vboxPciLinuxDevRegisterWithIommu(PVBOXRAWPCIINS pIns)
+{
+#ifdef VBOX_WITH_IOMMU
+ int rc = VINF_SUCCESS;
+ struct pci_dev *pPciDev = pIns->pPciDev;
+ PVBOXRAWPCIDRVVM pData = VBOX_DRV_VMDATA(pIns);
+ IPRT_LINUX_SAVE_EFL_AC();
+
+ if (RT_LIKELY(pData))
+ {
+ if (RT_LIKELY(pData->pIommuDomain))
+ {
+ /** @todo KVM checks IOMMU_CAP_CACHE_COHERENCY and sets
+ * flag IOMMU_CACHE later used when mapping physical
+ * addresses, which could improve performance.
+ */
+ int rcLnx = iommu_attach_device(pData->pIommuDomain, &pPciDev->dev);
+ if (!rcLnx)
+ {
+ vbpci_printk(KERN_DEBUG, pPciDev, "attached to IOMMU\n");
+ pIns->fIommuUsed = true;
+ rc = VINF_SUCCESS;
+ }
+ else
+ {
+ vbpci_printk(KERN_DEBUG, pPciDev, "failed to attach to IOMMU, error %d\n", rcLnx);
+ rc = VERR_INTERNAL_ERROR;
+ }
+ }
+ else
+ {
+ vbpci_printk(KERN_DEBUG, pIns->pPciDev, "cannot attach to IOMMU, no domain\n");
+ rc = VERR_NOT_FOUND;
+ }
+ }
+ else
+ {
+ vbpci_printk(KERN_DEBUG, pPciDev, "cannot attach to IOMMU, no VM data\n");
+ rc = VERR_INVALID_PARAMETER;
+ }
+
+ IPRT_LINUX_RESTORE_EFL_AC();
+ return rc;
+#else
+ return VERR_NOT_SUPPORTED;
+#endif
+}
+
+static int vboxPciLinuxDevUnregisterWithIommu(PVBOXRAWPCIINS pIns)
+{
+#ifdef VBOX_WITH_IOMMU
+ int rc = VINF_SUCCESS;
+ struct pci_dev *pPciDev = pIns->pPciDev;
+ PVBOXRAWPCIDRVVM pData = VBOX_DRV_VMDATA(pIns);
+ IPRT_LINUX_SAVE_EFL_AC();
+
+ if (RT_LIKELY(pData))
+ {
+ if (RT_LIKELY(pData->pIommuDomain))
+ {
+ if (pIns->fIommuUsed)
+ {
+ iommu_detach_device(pData->pIommuDomain, &pIns->pPciDev->dev);
+ vbpci_printk(KERN_DEBUG, pPciDev, "detached from IOMMU\n");
+ pIns->fIommuUsed = false;
+ }
+ }
+ else
+ {
+ vbpci_printk(KERN_DEBUG, pPciDev,
+ "cannot detach from IOMMU, no domain\n");
+ rc = VERR_NOT_FOUND;
+ }
+ }
+ else
+ {
+ vbpci_printk(KERN_DEBUG, pPciDev,
+ "cannot detach from IOMMU, no VM data\n");
+ rc = VERR_INVALID_PARAMETER;
+ }
+
+ IPRT_LINUX_RESTORE_EFL_AC();
+ return rc;
+#else
+ return VERR_NOT_SUPPORTED;
+#endif
+}
+
+static int vboxPciLinuxDevReset(PVBOXRAWPCIINS pIns)
+{
+ int rc = VINF_SUCCESS;
+ IPRT_LINUX_SAVE_EFL_AC();
+
+ if (RT_LIKELY(pIns->pPciDev))
+ {
+#if RTLNX_VER_MIN(2,6,28)
+ if (pci_reset_function(pIns->pPciDev))
+ {
+ vbpci_printk(KERN_DEBUG, pIns->pPciDev,
+ "pci_reset_function() failed\n");
+ rc = VERR_INTERNAL_ERROR;
+ }
+#else
+ rc = VERR_NOT_SUPPORTED;
+#endif
+ }
+ else
+ rc = VERR_INVALID_PARAMETER;
+
+ IPRT_LINUX_RESTORE_EFL_AC();
+ return rc;
+}
+
+static struct file* vboxPciFileOpen(const char* path, int flags)
+{
+ struct file* filp = NULL;
+ int err = 0;
+
+ filp = filp_open(path, flags, 0);
+
+ if (IS_ERR(filp))
+ {
+ err = PTR_ERR(filp);
+ printk(KERN_DEBUG "vboxPciFileOpen: error %d\n", err);
+ return NULL;
+ }
+
+ if (!filp->f_op || !filp->f_op->write)
+ {
+ printk(KERN_DEBUG "Not writable FS\n");
+ filp_close(filp, NULL);
+ return NULL;
+ }
+
+ return filp;
+}
+
+static void vboxPciFileClose(struct file* file)
+{
+ filp_close(file, NULL);
+}
+
+static int vboxPciFileWrite(struct file* file, unsigned long long offset, unsigned char* data, unsigned int size)
+{
+ int ret;
+ mm_segment_t fs_save;
+
+ fs_save = get_fs();
+ set_fs(KERNEL_DS);
+#if RTLNX_VER_MIN(4,14,0)
+ ret = kernel_write(file, data, size, &offset);
+#else
+ ret = vfs_write(file, data, size, &offset);
+#endif
+ set_fs(fs_save);
+ if (ret < 0)
+ printk(KERN_DEBUG "vboxPciFileWrite: error %d\n", ret);
+
+ return ret;
+}
+
+static int vboxPciLinuxDevDetachHostDriver(PVBOXRAWPCIINS pIns)
+{
+ struct pci_dev *pPciDev = NULL;
+ uint8_t uBus = (pIns->HostPciAddress) >> 8;
+ uint8_t uDevFn = (pIns->HostPciAddress) & 0xff;
+ const char* currentDriver;
+ uint16_t uVendor, uDevice;
+ bool fDetach = 0;
+
+ if (!g_VBoxPciGlobals.fPciStubModuleAvail)
+ {
+ printk(KERN_INFO "vboxpci: stub module %s not detected: cannot detach\n",
+ PCI_STUB_MODULE);
+ return VERR_ACCESS_DENIED;
+ }
+
+ pPciDev = PCI_DEV_GET_SLOT(uBus, uDevFn);
+
+ if (!pPciDev)
+ {
+ printk(KERN_INFO "vboxpci: device at %02x:%02x.%d not found\n",
+ uBus, uDevFn>>3, uDevFn&7);
+ return VERR_NOT_FOUND;
+ }
+
+ uVendor = pPciDev->vendor;
+ uDevice = pPciDev->device;
+
+ currentDriver = pPciDev->driver ? pPciDev->driver->name : NULL;
+
+ printk(KERN_DEBUG "vboxpci: detected device: %04x:%04x at %02x:%02x.%d, driver %s\n",
+ uVendor, uDevice, uBus, uDevFn>>3, uDevFn&7,
+ currentDriver ? currentDriver : "<none>");
+
+ fDetach = (currentDriver == NULL || (strcmp(currentDriver, PCI_STUB_MODULE) != 0));
+
+ /* Init previous driver data. */
+ pIns->szPrevDriver[0] = '\0';
+
+ if (fDetach && currentDriver)
+ {
+ /* Dangerous: if device name for some reasons contains slashes - arbitrary file could be written to. */
+ if (strchr(currentDriver, '/') != 0)
+ {
+ printk(KERN_DEBUG "vboxpci: ERROR: %s contains invalid symbols\n", currentDriver);
+ return VERR_ACCESS_DENIED;
+ }
+ /** @todo RTStrCopy not exported. */
+ strncpy(pIns->szPrevDriver, currentDriver, sizeof(pIns->szPrevDriver) - 1);
+ pIns->szPrevDriver[sizeof(pIns->szPrevDriver) - 1] = '\0';
+ }
+
+ PCI_DEV_PUT(pPciDev);
+ pPciDev = NULL;
+
+ if (fDetach)
+ {
+ char* szCmdBuf;
+ char* szFileBuf;
+ struct file* pFile;
+ int iCmdLen;
+ const int cMaxBuf = 128;
+#if RTLNX_VER_MIN(2,6,29)
+ const struct cred *pOldCreds;
+ struct cred *pNewCreds;
+#endif
+
+ /*
+ * Now perform kernel analog of:
+ *
+ * echo -n "10de 040a" > /sys/bus/pci/drivers/pci-stub/new_id
+ * echo -n 0000:03:00.0 > /sys/bus/pci/drivers/nvidia/unbind
+ * echo -n 0000:03:00.0 > /sys/bus/pci/drivers/pci-stub/bind
+ *
+ * We do this way, as this interface is presumingly more stable than
+ * in-kernel ones.
+ */
+ szCmdBuf = kmalloc(cMaxBuf, GFP_KERNEL);
+ szFileBuf = kmalloc(cMaxBuf, GFP_KERNEL);
+ if (!szCmdBuf || !szFileBuf)
+ goto done;
+
+ /* Somewhat ugly hack - override current credentials */
+#if RTLNX_VER_MIN(2,6,29)
+ pNewCreds = prepare_creds();
+ if (!pNewCreds)
+ goto done;
+
+# if RTLNX_VER_MIN(3,5,0)
+ pNewCreds->fsuid = GLOBAL_ROOT_UID;
+# else
+ pNewCreds->fsuid = 0;
+# endif
+ pOldCreds = override_creds(pNewCreds);
+#endif
+
+ RTStrPrintf(szFileBuf, cMaxBuf,
+ "/sys/bus/pci/drivers/%s/new_id",
+ PCI_STUB_MODULE);
+ pFile = vboxPciFileOpen(szFileBuf, O_WRONLY);
+ if (pFile)
+ {
+ iCmdLen = RTStrPrintf(szCmdBuf, cMaxBuf,
+ "%04x %04x",
+ uVendor, uDevice);
+ /* Don't write trailing \0 */
+ vboxPciFileWrite(pFile, 0, szCmdBuf, iCmdLen);
+ vboxPciFileClose(pFile);
+ }
+ else
+ printk(KERN_DEBUG "vboxpci: cannot open %s\n", szFileBuf);
+
+ iCmdLen = RTStrPrintf(szCmdBuf, cMaxBuf,
+ "0000:%02x:%02x.%d",
+ uBus, uDevFn>>3, uDevFn&7);
+
+ /* Unbind if bound to smth */
+ if (pIns->szPrevDriver[0])
+ {
+ RTStrPrintf(szFileBuf, cMaxBuf,
+ "/sys/bus/pci/drivers/%s/unbind",
+ pIns->szPrevDriver);
+ pFile = vboxPciFileOpen(szFileBuf, O_WRONLY);
+ if (pFile)
+ {
+
+ /* Don't write trailing \0 */
+ vboxPciFileWrite(pFile, 0, szCmdBuf, iCmdLen);
+ vboxPciFileClose(pFile);
+ }
+ else
+ printk(KERN_DEBUG "vboxpci: cannot open %s\n", szFileBuf);
+ }
+
+ RTStrPrintf(szFileBuf, cMaxBuf,
+ "/sys/bus/pci/drivers/%s/bind",
+ PCI_STUB_MODULE);
+ pFile = vboxPciFileOpen(szFileBuf, O_WRONLY);
+ if (pFile)
+ {
+ /* Don't write trailing \0 */
+ vboxPciFileWrite(pFile, 0, szCmdBuf, iCmdLen);
+ vboxPciFileClose(pFile);
+ }
+ else
+ printk(KERN_DEBUG "vboxpci: cannot open %s\n", szFileBuf);
+
+#if RTLNX_VER_MIN(2,6,29)
+ revert_creds(pOldCreds);
+ put_cred(pNewCreds);
+#endif
+
+ done:
+ kfree(szCmdBuf);
+ kfree(szFileBuf);
+ }
+
+ return 0;
+}
+
+static int vboxPciLinuxDevReattachHostDriver(PVBOXRAWPCIINS pIns)
+{
+ struct pci_dev *pPciDev = pIns->pPciDev;
+
+ if (!pPciDev)
+ return VINF_SUCCESS;
+
+ if (pIns->szPrevDriver[0])
+ {
+ char* szCmdBuf;
+ char* szFileBuf;
+ struct file* pFile;
+ int iCmdLen;
+ const int cMaxBuf = 128;
+#if RTLNX_VER_MIN(2,6,29)
+ const struct cred *pOldCreds;
+ struct cred *pNewCreds;
+#endif
+ uint8_t uBus = (pIns->HostPciAddress) >> 8;
+ uint8_t uDevFn = (pIns->HostPciAddress) & 0xff;
+
+ vbpci_printk(KERN_DEBUG, pPciDev,
+ "reattaching old host driver %s\n", pIns->szPrevDriver);
+ /*
+ * Now perform kernel analog of:
+ *
+ * echo -n 0000:03:00.0 > /sys/bus/pci/drivers/pci-stub/unbind
+ * echo -n 0000:03:00.0 > /sys/bus/pci/drivers/nvidia/bind
+ */
+ szCmdBuf = kmalloc(cMaxBuf, GFP_KERNEL);
+ szFileBuf = kmalloc(cMaxBuf, GFP_KERNEL);
+
+ if (!szCmdBuf || !szFileBuf)
+ goto done;
+
+ iCmdLen = RTStrPrintf(szCmdBuf, cMaxBuf,
+ "0000:%02x:%02x.%d",
+ uBus, uDevFn>>3, uDevFn&7);
+
+ /* Somewhat ugly hack - override current credentials */
+#if RTLNX_VER_MIN(2,6,29)
+ pNewCreds = prepare_creds();
+ if (!pNewCreds)
+ goto done;
+
+# if RTLNX_VER_MIN(3,5,0)
+ pNewCreds->fsuid = GLOBAL_ROOT_UID;
+# else
+ pNewCreds->fsuid = 0;
+# endif
+ pOldCreds = override_creds(pNewCreds);
+#endif
+ RTStrPrintf(szFileBuf, cMaxBuf,
+ "/sys/bus/pci/drivers/%s/unbind",
+ PCI_STUB_MODULE);
+ pFile = vboxPciFileOpen(szFileBuf, O_WRONLY);
+ if (pFile)
+ {
+
+ /* Don't write trailing \0 */
+ vboxPciFileWrite(pFile, 0, szCmdBuf, iCmdLen);
+ vboxPciFileClose(pFile);
+ }
+ else
+ printk(KERN_DEBUG "vboxpci: cannot open %s\n", szFileBuf);
+
+ RTStrPrintf(szFileBuf, cMaxBuf,
+ "/sys/bus/pci/drivers/%s/bind",
+ pIns->szPrevDriver);
+ pFile = vboxPciFileOpen(szFileBuf, O_WRONLY);
+ if (pFile)
+ {
+
+ /* Don't write trailing \0 */
+ vboxPciFileWrite(pFile, 0, szCmdBuf, iCmdLen);
+ vboxPciFileClose(pFile);
+ pIns->szPrevDriver[0] = '\0';
+ }
+ else
+ printk(KERN_DEBUG "vboxpci: cannot open %s\n", szFileBuf);
+
+#if RTLNX_VER_MIN(2,6,29)
+ revert_creds(pOldCreds);
+ put_cred(pNewCreds);
+#endif
+
+ done:
+ kfree(szCmdBuf);
+ kfree(szFileBuf);
+ }
+
+ return VINF_SUCCESS;
+}
+
+DECLHIDDEN(int) vboxPciOsDevInit(PVBOXRAWPCIINS pIns, uint32_t fFlags)
+{
+ struct pci_dev *pPciDev = NULL;
+ int rc = VINF_SUCCESS;
+ IPRT_LINUX_SAVE_EFL_AC();
+
+ if (fFlags & PCIRAWDRIVERRFLAG_DETACH_HOST_DRIVER)
+ {
+ rc = vboxPciLinuxDevDetachHostDriver(pIns);
+ if (RT_FAILURE(rc))
+ {
+ printk(KERN_DEBUG "Cannot detach host driver for device %x: %d\n",
+ pIns->HostPciAddress, rc);
+ }
+ }
+
+ if (RT_SUCCESS(rc))
+ {
+ pPciDev = PCI_DEV_GET_SLOT((pIns->HostPciAddress) >> 8,
+ (pIns->HostPciAddress) & 0xff);
+
+ if (RT_LIKELY(pPciDev))
+ {
+ int rcLnx = pci_enable_device(pPciDev);
+
+ if (!rcLnx)
+ {
+ pIns->pPciDev = pPciDev;
+ vbpci_printk(KERN_DEBUG, pPciDev, "%s\n", __func__);
+
+#if RTLNX_VER_MIN(2,6,1)
+ if (pci_enable_msi(pPciDev) == 0)
+ pIns->fMsiUsed = true;
+#endif
+
+ /** @todo
+ * pci_enable_msix(pPciDev, entries, nvec)
+ *
+ * In fact, if device uses interrupts, and cannot be forced to use MSI or MSI-X
+ * we have to refuse using it, as we cannot work with shared PCI interrupts (unless we're lucky
+ * to grab unshared PCI interrupt).
+ */
+ }
+ else
+ rc = RTErrConvertFromErrno(RT_ABS(rcLnx));
+ }
+ else
+ rc = VERR_NOT_FOUND;
+ }
+
+ IPRT_LINUX_RESTORE_EFL_AC();
+ return rc;
+}
+
+DECLHIDDEN(int) vboxPciOsDevDeinit(PVBOXRAWPCIINS pIns, uint32_t fFlags)
+{
+ int rc = VINF_SUCCESS;
+ struct pci_dev *pPciDev = pIns->pPciDev;
+ IPRT_LINUX_SAVE_EFL_AC();
+
+ vbpci_printk(KERN_DEBUG, pPciDev, "%s\n", __func__);
+
+ if (RT_LIKELY(pPciDev))
+ {
+ int iRegion;
+ for (iRegion = 0; iRegion < 7; ++iRegion)
+ {
+ if (pIns->aRegionR0Mapping[iRegion])
+ {
+ iounmap(pIns->aRegionR0Mapping[iRegion]);
+ pIns->aRegionR0Mapping[iRegion] = 0;
+ pci_release_region(pPciDev, iRegion);
+ }
+ }
+
+ vboxPciLinuxDevUnregisterWithIommu(pIns);
+
+#if RTLNX_VER_MIN(2,6,1)
+ if (pIns->fMsiUsed)
+ pci_disable_msi(pPciDev);
+#endif
+ // pci_disable_msix(pPciDev);
+ pci_disable_device(pPciDev);
+ vboxPciLinuxDevReattachHostDriver(pIns);
+
+ PCI_DEV_PUT(pPciDev);
+ pIns->pPciDev = NULL;
+ }
+ else
+ rc = VERR_INVALID_PARAMETER;
+
+ IPRT_LINUX_RESTORE_EFL_AC();
+ return rc;
+}
+
+DECLHIDDEN(int) vboxPciOsDevDestroy(PVBOXRAWPCIINS pIns)
+{
+ return VINF_SUCCESS;
+}
+
+DECLHIDDEN(int) vboxPciOsDevGetRegionInfo(PVBOXRAWPCIINS pIns,
+ int32_t iRegion,
+ RTHCPHYS *pRegionStart,
+ uint64_t *pu64RegionSize,
+ bool *pfPresent,
+ uint32_t *pfFlags)
+{
+ int rc = VINF_SUCCESS;
+ struct pci_dev *pPciDev = pIns->pPciDev;
+ IPRT_LINUX_SAVE_EFL_AC();
+
+ if (RT_LIKELY(pPciDev))
+ {
+ int fFlags = pci_resource_flags(pPciDev, iRegion);
+
+ if ( ((fFlags & (IORESOURCE_MEM | IORESOURCE_IO)) == 0)
+ || ((fFlags & IORESOURCE_DISABLED) != 0))
+ {
+ *pfPresent = false;
+ rc = VERR_INVALID_PARAMETER;
+ }
+ else
+ {
+ uint32_t fResFlags = 0;
+ *pfPresent = true;
+
+ if (fFlags & IORESOURCE_MEM)
+ fResFlags |= PCIRAW_ADDRESS_SPACE_MEM;
+
+ if (fFlags & IORESOURCE_IO)
+ fResFlags |= PCIRAW_ADDRESS_SPACE_IO;
+
+#ifdef IORESOURCE_MEM_64
+ if (fFlags & IORESOURCE_MEM_64)
+ fResFlags |= PCIRAW_ADDRESS_SPACE_BAR64;
+#endif
+
+ if (fFlags & IORESOURCE_PREFETCH)
+ fResFlags |= PCIRAW_ADDRESS_SPACE_MEM_PREFETCH;
+
+ *pfFlags = fResFlags;
+ *pRegionStart = pci_resource_start(pPciDev, iRegion);
+ *pu64RegionSize = pci_resource_len (pPciDev, iRegion);
+
+ vbpci_printk(KERN_DEBUG, pPciDev,
+ "region %d: %s %llx+%lld\n",
+ iRegion, (fFlags & IORESOURCE_MEM) ? "mmio" : "pio",
+ *pRegionStart, *pu64RegionSize);
+ }
+ }
+ else
+ {
+ *pfPresent = false;
+ rc = VERR_INVALID_PARAMETER;
+ }
+
+ IPRT_LINUX_RESTORE_EFL_AC();
+ return rc;
+}
+
+DECLHIDDEN(int) vboxPciOsDevMapRegion(PVBOXRAWPCIINS pIns,
+ int32_t iRegion,
+ RTHCPHYS RegionStart,
+ uint64_t u64RegionSize,
+ uint32_t fFlags,
+ RTR0PTR *pRegionBase)
+{
+ int rc = VINF_SUCCESS;
+ struct pci_dev *pPciDev = pIns->pPciDev;
+ IPRT_LINUX_SAVE_EFL_AC();
+
+ if (!pPciDev || iRegion < 0 || iRegion > 0)
+ {
+ if (pPciDev)
+ vbpci_printk(KERN_DEBUG, pPciDev, "invalid region %d\n", iRegion);
+
+ IPRT_LINUX_RESTORE_EFL_AC();
+ return VERR_INVALID_PARAMETER;
+ }
+
+ vbpci_printk(KERN_DEBUG, pPciDev, "reg=%d start=%llx size=%lld\n",
+ iRegion, RegionStart, u64RegionSize);
+
+ if ( (pci_resource_flags(pPciDev, iRegion) & IORESOURCE_IO)
+ || RegionStart != pci_resource_start(pPciDev, iRegion)
+ || u64RegionSize != pci_resource_len(pPciDev, iRegion))
+ {
+ IPRT_LINUX_RESTORE_EFL_AC();
+ return VERR_INVALID_PARAMETER;
+ }
+
+ /*
+ * XXX: Current code never calls unmap. To avoid leaking mappings
+ * only request and map resources once.
+ */
+ if (!pIns->aRegionR0Mapping[iRegion])
+ {
+ int rcLnx;
+ *pRegionBase = pIns->aRegionR0Mapping[iRegion];
+
+ rcLnx = pci_request_region(pPciDev, iRegion, "vboxpci");
+ if (!rcLnx)
+ {
+#if RTLNX_VER_MIN(2,6,25)
+ /*
+ * ioremap() defaults to no caching since the 2.6 kernels.
+ * ioremap_nocache() has been removed finally in 5.6-rc1.
+ */
+ RTR0PTR R0PtrMapping = ioremap(pci_resource_start(pPciDev, iRegion),
+ pci_resource_len(pPciDev, iRegion));
+#else /* KERNEL_VERSION < 2.6.25 */
+ /* For now no caching, try to optimize later. */
+ RTR0PTR R0PtrMapping = ioremap_nocache(pci_resource_start(pPciDev, iRegion),
+ pci_resource_len(pPciDev, iRegion));
+#endif /* KERNEL_VERSION < 2.6.25 */
+ if (R0PtrMapping != NIL_RTR0PTR)
+ pIns->aRegionR0Mapping[iRegion] = R0PtrMapping;
+ else
+ {
+#if RTLNX_VER_MIN(2,6,25)
+ vbpci_printk(KERN_DEBUG, pPciDev, "ioremap() failed\n");
+#else
+ vbpci_printk(KERN_DEBUG, pPciDev, "ioremap_nocache() failed\n");
+#endif
+ pci_release_region(pPciDev, iRegion);
+ rc = VERR_MAP_FAILED;
+ }
+ }
+ else
+ rc = VERR_RESOURCE_BUSY;
+ }
+
+ if (RT_SUCCESS(rc))
+ *pRegionBase = pIns->aRegionR0Mapping[iRegion];
+
+ IPRT_LINUX_RESTORE_EFL_AC();
+ return rc;
+}
+
+DECLHIDDEN(int) vboxPciOsDevUnmapRegion(PVBOXRAWPCIINS pIns,
+ int32_t iRegion,
+ RTHCPHYS RegionStart,
+ uint64_t u64RegionSize,
+ RTR0PTR RegionBase)
+{
+ /* XXX: Current code never calls unmap. */
+ return VERR_NOT_IMPLEMENTED;
+}
+
+DECLHIDDEN(int) vboxPciOsDevPciCfgWrite(PVBOXRAWPCIINS pIns, uint32_t Register, PCIRAWMEMLOC *pValue)
+{
+ struct pci_dev *pPciDev = pIns->pPciDev;
+ int rc = VINF_SUCCESS;
+ IPRT_LINUX_SAVE_EFL_AC();
+
+ if (RT_LIKELY(pPciDev))
+ {
+ switch (pValue->cb)
+ {
+ case 1:
+ pci_write_config_byte(pPciDev, Register, pValue->u.u8);
+ break;
+ case 2:
+ pci_write_config_word(pPciDev, Register, pValue->u.u16);
+ break;
+ case 4:
+ pci_write_config_dword(pPciDev, Register, pValue->u.u32);
+ break;
+ }
+ }
+ else
+ rc = VERR_INVALID_PARAMETER;
+
+ IPRT_LINUX_RESTORE_EFL_AC();
+ return rc;
+}
+
+DECLHIDDEN(int) vboxPciOsDevPciCfgRead(PVBOXRAWPCIINS pIns, uint32_t Register, PCIRAWMEMLOC *pValue)
+{
+ struct pci_dev *pPciDev = pIns->pPciDev;
+ int rc = VINF_SUCCESS;
+
+ if (RT_LIKELY(pPciDev))
+ {
+ IPRT_LINUX_SAVE_EFL_AC();
+
+ switch (pValue->cb)
+ {
+ case 1:
+ pci_read_config_byte(pPciDev, Register, &pValue->u.u8);
+ break;
+ case 2:
+ pci_read_config_word(pPciDev, Register, &pValue->u.u16);
+ break;
+ case 4:
+ pci_read_config_dword(pPciDev, Register, &pValue->u.u32);
+ break;
+ }
+
+ IPRT_LINUX_RESTORE_EFL_AC();
+ }
+ else
+ rc = VERR_INVALID_PARAMETER;
+
+ return rc;
+}
+
+/**
+ * Interrupt service routine.
+ *
+ * @returns 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 PVBOXRAWPCIINS.
+ * @param pRegs Register set. Removed in 2.6.19.
+ */
+#if RTLNX_VER_MIN(2,6,19) && !defined(DOXYGEN_RUNNING)
+static irqreturn_t vboxPciOsIrqHandler(int iIrq, void *pvDevId)
+#else
+static irqreturn_t vboxPciOsIrqHandler(int iIrq, void *pvDevId, struct pt_regs *pRegs)
+#endif
+{
+ PVBOXRAWPCIINS pIns = (PVBOXRAWPCIINS)pvDevId;
+ bool fTaken = true;
+
+ if (pIns && pIns->IrqHandler.pfnIrqHandler)
+ fTaken = pIns->IrqHandler.pfnIrqHandler(pIns->IrqHandler.pIrqContext, iIrq);
+#ifndef VBOX_WITH_SHARED_PCI_INTERRUPTS
+ /* If we don't allow interrupts sharing, we consider all interrupts as non-shared, thus targetted to us. */
+ fTaken = true;
+#endif
+
+ return fTaken;
+}
+
+DECLHIDDEN(int) vboxPciOsDevRegisterIrqHandler(PVBOXRAWPCIINS pIns, PFNRAWPCIISR pfnHandler, void* pIrqContext, int32_t *piHostIrq)
+{
+ int rc;
+ int32_t iIrq = pIns->pPciDev->irq;
+ IPRT_LINUX_SAVE_EFL_AC();
+
+ if (iIrq == 0)
+ {
+ vbpci_printk(KERN_NOTICE, pIns->pPciDev, "no irq assigned\n");
+ IPRT_LINUX_RESTORE_EFL_AC();
+ return VERR_INVALID_PARAMETER;
+ }
+
+ rc = request_irq(iIrq,
+ vboxPciOsIrqHandler,
+#ifdef VBOX_WITH_SHARED_PCI_INTERRUPTS
+ /* Allow interrupts sharing. */
+# if RTLNX_VER_MIN(2,6,20)
+ IRQF_SHARED,
+# else
+ SA_SHIRQ,
+# endif
+
+#else
+
+ /* We don't allow interrupts sharing */
+ /* XXX overhaul */
+# if RTLNX_VER_MIN(2,6,20) && RTLNX_VER_MAX(4,1,0)
+ IRQF_DISABLED, /* keep irqs disabled when calling the action handler */
+# else
+ 0,
+# endif
+#endif
+ DRIVER_NAME,
+ pIns);
+ if (rc)
+ {
+ vbpci_printk(KERN_DEBUG, pIns->pPciDev,
+ "could not request irq %d, error %d\n", iIrq, rc);
+ IPRT_LINUX_RESTORE_EFL_AC();
+ return VERR_RESOURCE_BUSY;
+ }
+
+ vbpci_printk(KERN_DEBUG, pIns->pPciDev, "got irq %d\n", iIrq);
+ *piHostIrq = iIrq;
+
+ IPRT_LINUX_RESTORE_EFL_AC();
+ return VINF_SUCCESS;
+}
+
+DECLHIDDEN(int) vboxPciOsDevUnregisterIrqHandler(PVBOXRAWPCIINS pIns, int32_t iHostIrq)
+{
+ IPRT_LINUX_SAVE_EFL_AC();
+
+ vbpci_printk(KERN_DEBUG, pIns->pPciDev, "freeing irq %d\n", iHostIrq);
+ free_irq(iHostIrq, pIns);
+
+ IPRT_LINUX_RESTORE_EFL_AC();
+ return VINF_SUCCESS;
+}
+
+DECLHIDDEN(int) vboxPciOsDevPowerStateChange(PVBOXRAWPCIINS pIns, PCIRAWPOWERSTATE aState)
+{
+ int rc;
+
+ switch (aState)
+ {
+ case PCIRAW_POWER_ON:
+ vbpci_printk(KERN_DEBUG, pIns->pPciDev, "PCIRAW_POWER_ON\n");
+ /* Reset device, just in case. */
+ vboxPciLinuxDevReset(pIns);
+ /* register us with IOMMU */
+ rc = vboxPciLinuxDevRegisterWithIommu(pIns);
+ break;
+ case PCIRAW_POWER_RESET:
+ vbpci_printk(KERN_DEBUG, pIns->pPciDev, "PCIRAW_POWER_RESET\n");
+ rc = vboxPciLinuxDevReset(pIns);
+ break;
+ case PCIRAW_POWER_OFF:
+ vbpci_printk(KERN_DEBUG, pIns->pPciDev, "PCIRAW_POWER_OFF\n");
+ /* unregister us from IOMMU */
+ rc = vboxPciLinuxDevUnregisterWithIommu(pIns);
+ break;
+ case PCIRAW_POWER_SUSPEND:
+ vbpci_printk(KERN_DEBUG, pIns->pPciDev, "PCIRAW_POWER_SUSPEND\n");
+ rc = VINF_SUCCESS;
+ /// @todo what do we do here?
+ break;
+ case PCIRAW_POWER_RESUME:
+ vbpci_printk(KERN_DEBUG, pIns->pPciDev, "PCIRAW_POWER_RESUME\n");
+ rc = VINF_SUCCESS;
+ /// @todo what do we do here?
+ break;
+ default:
+ vbpci_printk(KERN_DEBUG, pIns->pPciDev, "unknown power state %u\n", aState);
+ /* to make compiler happy */
+ rc = VERR_NOT_SUPPORTED;
+ break;
+ }
+
+ return rc;
+}
+
+
+#ifdef VBOX_WITH_IOMMU
+/** Callback for FNRAWPCICONTIGPHYSMEMINFO. */
+static DECLCALLBACK(int) vboxPciOsContigMemInfo(PRAWPCIPERVM pVmCtx, RTHCPHYS HostStart, RTGCPHYS GuestStart,
+ uint64_t cMemSize, PCIRAWMEMINFOACTION Action)
+{
+ struct iommu_domain* domain = ((PVBOXRAWPCIDRVVM)(pVmCtx->pDriverData))->pIommuDomain;
+ int rc = VINF_SUCCESS;
+ IPRT_LINUX_SAVE_EFL_AC();
+
+ switch (Action)
+ {
+ case PCIRAW_MEMINFO_MAP:
+ {
+ int flags, r;
+
+ if (iommu_iova_to_phys(domain, GuestStart))
+ break;
+
+ flags = IOMMU_READ | IOMMU_WRITE;
+ /** @todo flags |= IOMMU_CACHE; */
+
+ r = iommu_map(domain, GuestStart, HostStart, get_order(cMemSize), flags);
+ if (r)
+ {
+ printk(KERN_ERR "vboxPciOsContigMemInfo:"
+ "iommu failed to map pfn=%llx\n", HostStart);
+ rc = VERR_GENERAL_FAILURE;
+ break;
+ }
+ rc = VINF_SUCCESS;
+ break;
+ }
+ case PCIRAW_MEMINFO_UNMAP:
+ {
+ int order;
+ order = iommu_unmap(domain, GuestStart, get_order(cMemSize));
+ NOREF(order);
+ break;
+ }
+
+ default:
+ printk(KERN_DEBUG "Unsupported action: %d\n", (int)Action);
+ rc = VERR_NOT_SUPPORTED;
+ break;
+ }
+
+ IPRT_LINUX_RESTORE_EFL_AC();
+ return rc;
+}
+#endif
+
+DECLHIDDEN(int) vboxPciOsInitVm(PVBOXRAWPCIDRVVM pThis, PVM pVM, PRAWPCIPERVM pVmData)
+{
+ int rc = VINF_SUCCESS;
+
+#ifdef VBOX_WITH_IOMMU
+ IPRT_LINUX_SAVE_EFL_AC();
+
+ if (IOMMU_PRESENT())
+ {
+ pThis->pIommuDomain = IOMMU_DOMAIN_ALLOC();
+ if (!pThis->pIommuDomain)
+ {
+ vbpci_printk(KERN_DEBUG, NULL, "cannot allocate IOMMU domain\n");
+ rc = VERR_NO_MEMORY;
+ }
+ else
+ {
+ pVmData->pfnContigMemInfo = vboxPciOsContigMemInfo;
+
+ vbpci_printk(KERN_DEBUG, NULL, "created IOMMU domain %p\n",
+ pThis->pIommuDomain);
+ }
+ }
+
+ IPRT_LINUX_RESTORE_EFL_AC();
+#endif
+ return rc;
+}
+
+DECLHIDDEN(void) vboxPciOsDeinitVm(PVBOXRAWPCIDRVVM pThis, PVM pVM)
+{
+#ifdef VBOX_WITH_IOMMU
+ IPRT_LINUX_SAVE_EFL_AC();
+
+ if (pThis->pIommuDomain)
+ {
+ vbpci_printk(KERN_DEBUG, NULL, "freeing IOMMU domain %p\n",
+ pThis->pIommuDomain);
+ iommu_domain_free(pThis->pIommuDomain);
+ pThis->pIommuDomain = NULL;
+ }
+
+ IPRT_LINUX_RESTORE_EFL_AC();
+#endif
+}
diff --git a/src/VBox/HostDrivers/VBoxPci/linux/files_vboxpci b/src/VBox/HostDrivers/VBoxPci/linux/files_vboxpci
new file mode 100755
index 00000000..70e15ed2
--- /dev/null
+++ b/src/VBox/HostDrivers/VBoxPci/linux/files_vboxpci
@@ -0,0 +1,111 @@
+#!/bin/sh
+# $Id: files_vboxpci $
+## @file
+# Shared file between Makefile.kmk and export_modules.sh.
+#
+
+#
+# Copyright (C) 2011-2023 Oracle and/or its affiliates.
+#
+# This file is part of VirtualBox base platform packages, as
+# available from https://www.virtualbox.org.
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation, in version 3 of the
+# License.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, see <https://www.gnu.org/licenses>.
+#
+# The contents of this file may alternatively be used under the terms
+# of the Common Development and Distribution License Version 1.0
+# (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+# in the VirtualBox distribution, in which case the provisions of the
+# CDDL are applicable instead of those of the GPL.
+#
+# You may elect to license modified versions of this file under the
+# terms and conditions of either the GPL or the CDDL or both.
+#
+# SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+#
+
+VBOX_VBOXPCI_SOURCES=" \
+ ${PATH_ROOT}/include/iprt/alloc.h=>include/iprt/alloc.h \
+ ${PATH_ROOT}/include/iprt/alloca.h=>include/iprt/alloca.h \
+ ${PATH_ROOT}/include/iprt/asm.h=>include/iprt/asm.h \
+ ${PATH_ROOT}/include/iprt/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/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/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/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/uuid.h=>include/iprt/uuid.h \
+ ${PATH_ROOT}/include/iprt/x86-helpers.h=>include/iprt/x86-helpers.h \
+ ${PATH_ROOT}/include/iprt/linux/version.h=>include/iprt/linux/version.h \
+ ${PATH_ROOT}/include/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/rawpci.h=>include/VBox/rawpci.h \
+ ${PATH_ROOT}/include/VBox/vmm/stam.h=>include/VBox/vmm/stam.h \
+ ${PATH_ROOT}/include/VBox/param.h=>include/VBox/param.h \
+ ${PATH_ROOT}/include/VBox/sup.h=>include/VBox/sup.h \
+ ${PATH_ROOT}/include/VBox/types.h=>include/VBox/types.h \
+ ${PATH_ROOT}/include/VBox/version.h=>include/VBox/version.h \
+ ${PATH_ROOT}/include/VBox/SUPDrvMangling.h=>include/VBox/SUPDrvMangling.h \
+ ${PATH_ROOT}/src/VBox/HostDrivers/VBoxPci/linux/VBoxPci-linux.c=>linux/VBoxPci-linux.c \
+ ${PATH_ROOT}/src/VBox/HostDrivers/VBoxPci/VBoxPci.c=>VBoxPci.c \
+ ${PATH_ROOT}/src/VBox/HostDrivers/VBoxPci/VBoxPciInternal.h=>VBoxPciInternal.h \
+ ${PATH_ROOT}/src/VBox/HostDrivers/Support/SUPDrvIDC.h=>SUPDrvIDC.h \
+ ${PATH_ROOT}/src/VBox/HostDrivers/Support/SUPR0IdcClient.c=>SUPR0IdcClient.c \
+ ${PATH_ROOT}/src/VBox/HostDrivers/Support/SUPR0IdcClientComponent.c=>SUPR0IdcClientComponent.c \
+ ${PATH_ROOT}/src/VBox/HostDrivers/Support/SUPR0IdcClientInternal.h=>SUPR0IdcClientInternal.h \
+ ${PATH_ROOT}/src/VBox/HostDrivers/Support/linux/SUPR0IdcClient-linux.c=>linux/SUPR0IdcClient-linux.c \
+ ${PATH_ROOT}/src/VBox/Installer/linux/Makefile-footer.gmk=>Makefile-footer.gmk \
+ ${PATH_ROOT}/src/VBox/Installer/linux/Makefile-header.gmk=>Makefile-header.gmk \
+ ${PATH_ROOT}/src/VBox/Runtime/common/math/gcc/divdi3.c=>math/gcc/divdi3.c \
+ ${PATH_ROOT}/src/VBox/Runtime/common/math/gcc/moddi3.c=>math/gcc/moddi3.c \
+ ${PATH_ROOT}/src/VBox/Runtime/common/math/gcc/qdivrem.c=>math/gcc/qdivrem.c \
+ ${PATH_ROOT}/src/VBox/Runtime/common/math/gcc/quad.h=>math/gcc/quad.h \
+ ${PATH_ROOT}/src/VBox/Runtime/common/math/gcc/udivdi3.c=>math/gcc/udivdi3.c \
+ ${PATH_ROOT}/src/VBox/Runtime/common/math/gcc/udivmoddi4.c=>math/gcc/udivmoddi4.c \
+ ${PATH_ROOT}/src/VBox/Runtime/common/math/gcc/umoddi3.c=>math/gcc/umoddi3.c \
+ ${PATH_ROOT}/src/VBox/Runtime/r0drv/linux/the-linux-kernel.h=>r0drv/linux/the-linux-kernel.h \
+ ${PATH_OUT}/version-generated.h=>version-generated.h \
+ ${PATH_OUT}/revision-generated.h=>revision-generated.h \
+ ${PATH_OUT}/product-generated.h=>product-generated.h \
+"
+
diff --git a/src/VBox/HostDrivers/VBoxUSB/Makefile.kmk b/src/VBox/HostDrivers/VBoxUSB/Makefile.kmk
new file mode 100644
index 00000000..8936ba55
--- /dev/null
+++ b/src/VBox/HostDrivers/VBoxUSB/Makefile.kmk
@@ -0,0 +1,85 @@
+# $Id: Makefile.kmk $
+## @file
+# Sub-Makefile for the Windows USB drivers.
+#
+
+#
+# Copyright (C) 2006-2023 Oracle and/or its affiliates.
+#
+# This file is part of VirtualBox base platform packages, as
+# available from https://www.virtualbox.org.
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation, in version 3 of the
+# License.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, see <https://www.gnu.org/licenses>.
+#
+# The contents of this file may alternatively be used under the terms
+# of the Common Development and Distribution License Version 1.0
+# (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+# in the VirtualBox distribution, in which case the provisions of the
+# CDDL are applicable instead of those of the GPL.
+#
+# You may elect to license modified versions of this file under the
+# terms and conditions of either the GPL or the CDDL or both.
+#
+# SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+#
+
+SUB_DEPTH = ../../../..
+include $(KBUILD_PATH)/subheader.kmk
+
+# Include sub-makefiles.
+if1of ($(KBUILD_TARGET), solaris win)
+ include $(PATH_SUB_CURRENT)/$(KBUILD_TARGET)/Makefile.kmk
+endif
+
+#
+# USBLib - The static USB Library for use in VBoxSVC and VBoxDD.
+#
+# Note! Drivers using USBFilter.cpp should just add ../USBFilter.cpp
+# to their source list.
+#
+LIBRARIES += USBLib
+USBLib_TEMPLATE = VBoxR3Dll
+USBLib_SDKS.win = ReorderCompilerIncs $(VBOX_WINPSDK) $(VBOX_WINDDK)
+USBLib_DEFS = IN_USBLIB
+USBLib_DEFS.os2 = STATIC_USBCALLS
+USBLib_DEFS.win = _WIN32_WINNT=0x0501
+USBLib_SOURCES = \
+ USBLib.cpp \
+ USBFilter.cpp
+
+# OS specific bits if applicable.
+USBLib_SOURCES.os2 = \
+ os2/usbcalls.c
+USBLib_SOURCES.solaris = \
+ solaris/USBLib-solaris.cpp
+USBLib_SOURCES.win = \
+ win/lib/VBoxUsbLib-win.cpp
+
+ifdef VBOX_WITH_TESTCASES
+ #
+ # USBFilter testcase.
+ #
+ PROGRAMS += tstUSBFilter
+ tstUSBFilter_TEMPLATE = VBoxR3TstExe
+ tstUSBFilter_DEFS = IN_USBLIB
+ tstUSBFilter_SOURCES = \
+ testcase/tstUSBFilter.cpp
+ tstUSBFilter_LIBS = \
+ $(USBLib_1_TARGET)
+endif # VBOX_WITH_TESTCASES
+
+
+# generate rules
+include $(FILE_KBUILD_SUB_FOOTER)
+
diff --git a/src/VBox/HostDrivers/VBoxUSB/USBFilter.cpp b/src/VBox/HostDrivers/VBoxUSB/USBFilter.cpp
new file mode 100644
index 00000000..e2e6e199
--- /dev/null
+++ b/src/VBox/HostDrivers/VBoxUSB/USBFilter.cpp
@@ -0,0 +1,1860 @@
+/* $Id: USBFilter.cpp $ */
+/** @file
+ * VirtualBox USB filter abstraction.
+ */
+
+/*
+ * Copyright (C) 2007-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include <VBox/usbfilter.h>
+#include <VBox/usblib.h>
+#include <VBox/err.h>
+#include <VBox/log.h>
+#include <iprt/string.h>
+#include <iprt/assert.h>
+#include <iprt/ctype.h>
+
+
+/** @todo split this up for the sake of device drivers and such. */
+
+
+/**
+ * Initializes an USBFILTER structure.
+ *
+ * @param pFilter The filter to initialize.
+ * @param enmType The filter type. If not valid, the filter will not
+ * be properly initialized and all other calls will fail.
+ */
+USBLIB_DECL(void) USBFilterInit(PUSBFILTER pFilter, USBFILTERTYPE enmType)
+{
+ memset(pFilter, 0, sizeof(*pFilter));
+ AssertReturnVoid(enmType > USBFILTERTYPE_INVALID && enmType < USBFILTERTYPE_END);
+ pFilter->u32Magic = USBFILTER_MAGIC;
+ pFilter->enmType = enmType;
+ for (unsigned i = 0; i < RT_ELEMENTS(pFilter->aFields); i++)
+ pFilter->aFields[i].enmMatch = USBFILTERMATCH_IGNORE;
+}
+
+
+/**
+ * Make a clone of the specified filter.
+ *
+ * @param pFilter The target filter.
+ * @param pToClone The source filter.
+ */
+USBLIB_DECL(void) USBFilterClone(PUSBFILTER pFilter, PCUSBFILTER pToClone)
+{
+ memcpy(pFilter, pToClone, sizeof(*pToClone));
+}
+
+
+/**
+ * Deletes (invalidates) an USBFILTER structure.
+ *
+ * @param pFilter The filter to delete.
+ */
+USBLIB_DECL(void) USBFilterDelete(PUSBFILTER pFilter)
+{
+ pFilter->u32Magic = ~USBFILTER_MAGIC;
+ pFilter->enmType = USBFILTERTYPE_INVALID;
+ pFilter->offCurEnd = 0xfffff;
+}
+
+
+/**
+ * Skips blanks.
+ *
+ * @returns Next non-blank char in the string.
+ * @param psz The string.
+ */
+DECLINLINE(const char *) usbfilterSkipBlanks(const char *psz)
+{
+ while (RT_C_IS_BLANK(*psz))
+ psz++;
+ return psz;
+}
+
+
+/**
+ * Worker for usbfilterReadNumber that parses a hexadecimal number.
+ *
+ * @returns Same as usbfilterReadNumber, except for VERR_NO_DIGITS.
+ * @param pszExpr Where to start converting, first char is a valid digit.
+ * @param ppszExpr See usbfilterReadNumber.
+ * @param pu16Val See usbfilterReadNumber.
+ */
+static int usbfilterReadNumberHex(const char *pszExpr, const char **ppszExpr, uint16_t *pu16Val)
+{
+ int rc = VINF_SUCCESS;
+ uint32_t u32 = 0;
+ do
+ {
+ unsigned uDigit = *pszExpr >= 'a' && *pszExpr <= 'f'
+ ? *pszExpr - 'a' + 10
+ : *pszExpr >= 'A' && *pszExpr <= 'F'
+ ? *pszExpr - 'A' + 10
+ : *pszExpr - '0';
+ if (uDigit >= 16)
+ break;
+ u32 *= 16;
+ u32 += uDigit;
+ if (u32 > UINT16_MAX)
+ rc = VWRN_NUMBER_TOO_BIG;
+ } while (*++pszExpr);
+
+ *ppszExpr = usbfilterSkipBlanks(pszExpr);
+ *pu16Val = rc == VINF_SUCCESS ? u32 : UINT16_MAX;
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * Worker for usbfilterReadNumber that parses a decimal number.
+ *
+ * @returns Same as usbfilterReadNumber, except for VERR_NO_DIGITS.
+ * @param pszExpr Where to start converting, first char is a valid digit.
+ * @param uBase The base - 8 or 16.
+ * @param ppszExpr See usbfilterReadNumber.
+ * @param pu16Val See usbfilterReadNumber.
+ */
+static int usbfilterReadNumberDecimal(const char *pszExpr, unsigned uBase, const char **ppszExpr, uint16_t *pu16Val)
+{
+ int rc = VINF_SUCCESS;
+ uint32_t u32 = 0;
+ do
+ {
+ unsigned uDigit = *pszExpr - '0';
+ if (uDigit >= uBase)
+ break;
+ u32 *= uBase;
+ u32 += uDigit;
+ if (u32 > UINT16_MAX)
+ rc = VWRN_NUMBER_TOO_BIG;
+ } while (*++pszExpr);
+
+ *ppszExpr = usbfilterSkipBlanks(pszExpr);
+ *pu16Val = rc == VINF_SUCCESS ? u32 : UINT16_MAX;
+ return rc;
+}
+
+
+/**
+ * Reads a number from a numeric expression.
+ *
+ * @returns IPRT status code.
+ * @retval VINF_SUCCESS if all is fine. *ppszExpr and *pu16Val are updated.
+ * @retval VWRN_NUMBER_TOO_BIG if the number exceeds unsigned 16-bit, both *ppszExpr and *pu16Val are updated.
+ * @retval VERR_NO_DIGITS if there aren't any digits.
+ *
+ * @param ppszExpr Pointer to the current expression pointer.
+ * This is advanced past the expression and trailing blanks on success.
+ * @param pu16Val Where to store the value on success.
+ */
+static int usbfilterReadNumber(const char **ppszExpr, uint16_t *pu16Val)
+{
+ const char *pszExpr = usbfilterSkipBlanks(*ppszExpr);
+ if (!RT_C_IS_DIGIT(*pszExpr))
+ return VERR_NO_DIGITS;
+
+ if (*pszExpr == '0')
+ {
+ if (pszExpr[1] == 'x' || pszExpr[1] == 'X')
+ {
+ if (!RT_C_IS_XDIGIT(pszExpr[2]))
+ return VERR_NO_DIGITS;
+ return usbfilterReadNumberHex(pszExpr + 2, ppszExpr, pu16Val);
+ }
+ if (RT_C_IS_ODIGIT(pszExpr[1]))
+ return usbfilterReadNumberDecimal(pszExpr + 1, 8, ppszExpr, pu16Val);
+ /* Solitary 0! */
+ if (RT_C_IS_DIGIT(pszExpr[1]))
+ return VERR_NO_DIGITS;
+ }
+ return usbfilterReadNumberDecimal(pszExpr, 10, ppszExpr, pu16Val);
+}
+
+
+/**
+ * Validates a numeric expression.
+ *
+ * @returns VBox status code.
+ * @retval VINF_SUCCESS if valid.
+ * @retval VERR_INVALID_PARAMETER if invalid.
+ * @retval VERR_NO_DIGITS if some expression is short of digits.
+ *
+ * @param pszExpr The numeric expression.
+ */
+static int usbfilterValidateNumExpression(const char *pszExpr)
+{
+ /*
+ * An empty expression is fine.
+ */
+ if (!*pszExpr)
+ return VINF_SUCCESS;
+
+ /*
+ * The string format is: "int:((<m>)|([<m>]-[<n>]))(,(<m>)|([<m>]-[<n>]))*"
+ * where <m> and <n> are numbers in decimal, hex (0xNNN) or octal (0NNN).
+ * Spaces are allowed around <m> and <n>.
+ */
+ unsigned cSubExpressions = 0;
+ while (*pszExpr)
+ {
+ if (!strncmp(pszExpr, RT_STR_TUPLE("int:")))
+ pszExpr += strlen("int:");
+
+ /*
+ * Skip remnants of the previous expression and any empty expressions.
+ * ('|' is the expression separator.)
+ */
+ while (*pszExpr == '|' || RT_C_IS_BLANK(*pszExpr) || *pszExpr == '(' || *pszExpr == ')')
+ pszExpr++;
+ if (!*pszExpr)
+ break;
+
+ /*
+ * Parse the expression.
+ */
+ int rc;
+ uint16_t u16First = 0;
+ uint16_t u16Last = 0;
+ if (*pszExpr == '-')
+ {
+ /* -N */
+ pszExpr++;
+ rc = usbfilterReadNumber(&pszExpr, &u16Last);
+ }
+ else
+ {
+ /* M or M,N or M-N or M- */
+ rc = usbfilterReadNumber(&pszExpr, &u16First);
+ if (RT_SUCCESS(rc))
+ {
+ pszExpr = usbfilterSkipBlanks(pszExpr);
+ if (*pszExpr == '-')
+ {
+ pszExpr++;
+ if (*pszExpr) /* M-N */
+ rc = usbfilterReadNumber(&pszExpr, &u16Last);
+ else /* M- */
+ u16Last = UINT16_MAX;
+ }
+ else if (*pszExpr == ',')
+ {
+ /* M,N */
+ pszExpr++;
+ rc = usbfilterReadNumber(&pszExpr, &u16Last);
+ }
+ else
+ {
+ /* M */
+ u16Last = u16First;
+ }
+ }
+ }
+ if (RT_FAILURE(rc))
+ return rc;
+
+ /*
+ * We should either be at the end of the string, at an expression separator (|),
+ * or at the end of an interval filter (')').
+ */
+ if (*pszExpr && *pszExpr != '|' && *pszExpr != ')')
+ return VERR_INVALID_PARAMETER;
+
+ cSubExpressions++;
+ }
+
+ return cSubExpressions ? VINF_SUCCESS : VERR_INVALID_PARAMETER;
+}
+
+
+/**
+ * Validates a string pattern.
+ *
+ * @returns VBox status code.
+ * @retval VINF_SUCCESS if valid.
+ * @retval VERR_INVALID_PARAMETER if invalid.
+ *
+ * @param psz The string pattern.
+ */
+static int usbfilterValidateStringPattern(const char *psz)
+{
+ /*
+ * This is only becomes important if we start doing
+ * sets ([0-9]) and such like.
+ */
+ RT_NOREF1(psz);
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * Thoroughly validates the USB Filter.
+ *
+ * @returns Appropriate VBox status code.
+ * @param pFilter The filter to validate.
+ */
+USBLIB_DECL(int) USBFilterValidate(PCUSBFILTER pFilter)
+{
+ if (!RT_VALID_PTR(pFilter))
+ return VERR_INVALID_POINTER;
+
+ if (pFilter->u32Magic != USBFILTER_MAGIC)
+ return VERR_INVALID_MAGIC;
+
+ if ( pFilter->enmType <= USBFILTERTYPE_INVALID
+ || pFilter->enmType >= USBFILTERTYPE_END)
+ {
+ Log(("USBFilter: %p - enmType=%d!\n", pFilter, pFilter->enmType));
+ return VERR_INVALID_PARAMETER;
+ }
+
+ if (pFilter->offCurEnd >= sizeof(pFilter->achStrTab))
+ {
+ Log(("USBFilter: %p - offCurEnd=%#x!\n", pFilter, pFilter->offCurEnd));
+ return VERR_INVALID_PARAMETER;
+ }
+
+ /* Validate that string value offsets are inside the string table. */
+ for (uint32_t i = 0; i < RT_ELEMENTS(pFilter->aFields); i++)
+ {
+ if ( USBFilterIsMethodUsingStringValue((USBFILTERMATCH)pFilter->aFields[i].enmMatch)
+ && pFilter->aFields[i].u16Value > pFilter->offCurEnd)
+ {
+ Log(("USBFilter: %p - bad offset=%#x\n", pFilter, pFilter->aFields[i].u16Value));
+ return VERR_INVALID_PARAMETER;
+ }
+ }
+
+ /*
+ * Validate the string table.
+ */
+ if (pFilter->achStrTab[0])
+ {
+ Log(("USBFilter: %p - bad null string\n", pFilter));
+ return VERR_INVALID_PARAMETER;
+ }
+
+ const char *psz = &pFilter->achStrTab[1];
+ while (psz < &pFilter->achStrTab[pFilter->offCurEnd])
+ {
+ const char *pszEnd = RTStrEnd(psz, &pFilter->achStrTab[sizeof(pFilter->achStrTab)] - psz);
+ if (!pszEnd)
+ {
+ Log(("USBFilter: %p - string at %#x isn't terminated!\n",
+ pFilter, psz - &pFilter->achStrTab[0]));
+ return VERR_INVALID_PARAMETER;
+ }
+
+ uint16_t off = (uint16_t)(uintptr_t)(psz - &pFilter->achStrTab[0]);
+ unsigned i;
+ for (i = 0; i < RT_ELEMENTS(pFilter->aFields); i++)
+ if ( USBFilterIsMethodUsingStringValue((USBFILTERMATCH)pFilter->aFields[i].enmMatch)
+ && pFilter->aFields[i].u16Value == off)
+ break;
+ if (i >= RT_ELEMENTS(pFilter->aFields))
+ {
+ Log(("USBFilter: %p - string at %#x isn't used by anyone! (%s)\n",
+ pFilter, psz - &pFilter->achStrTab[0], psz));
+ return VERR_INVALID_PARAMETER;
+ }
+
+ psz = pszEnd + 1;
+ }
+
+ if ((uintptr_t)(psz - &pFilter->achStrTab[0] - 1) != pFilter->offCurEnd)
+ {
+ Log(("USBFilter: %p - offCurEnd=%#x currently at %#x\n",
+ pFilter, pFilter->offCurEnd, psz - &pFilter->achStrTab[0] - 1));
+ return VERR_INVALID_PARAMETER;
+ }
+
+ while (psz < &pFilter->achStrTab[sizeof(pFilter->achStrTab)])
+ {
+ if (*psz)
+ {
+ Log(("USBFilter: %p - str tab isn't zero padded! %#x: %c\n",
+ pFilter, psz - &pFilter->achStrTab[0], *psz));
+ return VERR_INVALID_PARAMETER;
+ }
+ psz++;
+ }
+
+
+ /*
+ * Validate the fields.
+ */
+ int rc;
+ for (unsigned i = 0; i < RT_ELEMENTS(pFilter->aFields); i++)
+ {
+ switch (pFilter->aFields[i].enmMatch)
+ {
+ case USBFILTERMATCH_IGNORE:
+ case USBFILTERMATCH_PRESENT:
+ if (pFilter->aFields[i].u16Value)
+ {
+ Log(("USBFilter: %p - #%d/%d u16Value=%d expected 0!\n",
+ pFilter, i, pFilter->aFields[i].enmMatch, pFilter->aFields[i].u16Value));
+ return VERR_INVALID_PARAMETER;
+ }
+ break;
+
+ case USBFILTERMATCH_NUM_EXACT:
+ case USBFILTERMATCH_NUM_EXACT_NP:
+ if (!USBFilterIsNumericField((USBFILTERIDX)i))
+ {
+ Log(("USBFilter: %p - #%d / %d - not numeric field\n",
+ pFilter, i, pFilter->aFields[i].enmMatch));
+ return VERR_INVALID_PARAMETER;
+ }
+ break;
+
+ case USBFILTERMATCH_NUM_EXPRESSION:
+ case USBFILTERMATCH_NUM_EXPRESSION_NP:
+ if (!USBFilterIsNumericField((USBFILTERIDX)i))
+ {
+ Log(("USBFilter: %p - #%d / %d - not numeric field\n",
+ pFilter, i, pFilter->aFields[i].enmMatch));
+ return VERR_INVALID_PARAMETER;
+ }
+ if ( pFilter->aFields[i].u16Value >= pFilter->offCurEnd
+ && pFilter->offCurEnd)
+ {
+ Log(("USBFilter: %p - #%d / %d - off=%#x max=%#x\n",
+ pFilter, i, pFilter->aFields[i].enmMatch, pFilter->aFields[i].u16Value, pFilter->offCurEnd));
+ return VERR_INVALID_PARAMETER;
+ }
+ psz = &pFilter->achStrTab[pFilter->aFields[i].u16Value];
+ rc = usbfilterValidateNumExpression(psz);
+ if (RT_FAILURE(rc))
+ {
+ Log(("USBFilter: %p - #%d / %d - bad num expr: %s (rc=%Rrc)\n",
+ pFilter, i, pFilter->aFields[i].enmMatch, psz, rc));
+ return rc;
+ }
+ break;
+
+ case USBFILTERMATCH_STR_EXACT:
+ case USBFILTERMATCH_STR_EXACT_NP:
+ if (!USBFilterIsStringField((USBFILTERIDX)i))
+ {
+ Log(("USBFilter: %p - #%d / %d - not string field\n",
+ pFilter, i, pFilter->aFields[i].enmMatch));
+ return VERR_INVALID_PARAMETER;
+ }
+ if ( pFilter->aFields[i].u16Value >= pFilter->offCurEnd
+ && pFilter->offCurEnd)
+ {
+ Log(("USBFilter: %p - #%d / %d - off=%#x max=%#x\n",
+ pFilter, i, pFilter->aFields[i].enmMatch, pFilter->aFields[i].u16Value, pFilter->offCurEnd));
+ return VERR_INVALID_PARAMETER;
+ }
+ break;
+
+ case USBFILTERMATCH_STR_PATTERN:
+ case USBFILTERMATCH_STR_PATTERN_NP:
+ if (!USBFilterIsStringField((USBFILTERIDX)i))
+ {
+ Log(("USBFilter: %p - #%d / %d - not string field\n",
+ pFilter, i, pFilter->aFields[i].enmMatch));
+ return VERR_INVALID_PARAMETER;
+ }
+ if ( pFilter->aFields[i].u16Value >= pFilter->offCurEnd
+ && pFilter->offCurEnd)
+ {
+ Log(("USBFilter: %p - #%d / %d - off=%#x max=%#x\n",
+ pFilter, i, pFilter->aFields[i].enmMatch, pFilter->aFields[i].u16Value, pFilter->offCurEnd));
+ return VERR_INVALID_PARAMETER;
+ }
+ psz = &pFilter->achStrTab[pFilter->aFields[i].u16Value];
+ rc = usbfilterValidateStringPattern(psz);
+ if (RT_FAILURE(rc))
+ {
+ Log(("USBFilter: %p - #%d / %d - bad string pattern: %s (rc=%Rrc)\n",
+ pFilter, i, pFilter->aFields[i].enmMatch, psz, rc));
+ return rc;
+ }
+ break;
+
+ default:
+ Log(("USBFilter: %p - #%d enmMatch=%d!\n", pFilter, i, pFilter->aFields[i].enmMatch));
+ return VERR_INVALID_PARAMETER;
+ }
+ }
+
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * Find the specified field in the string table.
+ *
+ * @returns Pointer to the string in the string table on success.
+ * NULL if the field is invalid or it doesn't have a string value.
+ * @param pFilter The filter.
+ * @param enmFieldIdx The field index.
+ */
+DECLINLINE(const char *) usbfilterGetString(PCUSBFILTER pFilter, USBFILTERIDX enmFieldIdx)
+{
+ if ((unsigned)enmFieldIdx < (unsigned)USBFILTERIDX_END)
+ {
+ switch (pFilter->aFields[enmFieldIdx].enmMatch)
+ {
+ case USBFILTERMATCH_NUM_EXPRESSION:
+ case USBFILTERMATCH_NUM_EXPRESSION_NP:
+ case USBFILTERMATCH_STR_EXACT:
+ case USBFILTERMATCH_STR_EXACT_NP:
+ case USBFILTERMATCH_STR_PATTERN:
+ case USBFILTERMATCH_STR_PATTERN_NP:
+ Assert(pFilter->aFields[enmFieldIdx].u16Value < sizeof(pFilter->achStrTab));
+ return &pFilter->achStrTab[pFilter->aFields[enmFieldIdx].u16Value];
+
+ default:
+ AssertMsgFailed(("%d\n", pFilter->aFields[enmFieldIdx].enmMatch));
+ case USBFILTERMATCH_IGNORE:
+ case USBFILTERMATCH_PRESENT:
+ case USBFILTERMATCH_NUM_EXACT:
+ case USBFILTERMATCH_NUM_EXACT_NP:
+ break;
+ }
+ }
+ return NULL;
+}
+
+
+/**
+ * Gets a number value of a field.
+ *
+ * The field must contain a numeric value.
+ *
+ * @returns The field value on success, -1 on failure (invalid input / not numeric).
+ * @param pFilter The filter.
+ * @param enmFieldIdx The field index.
+ */
+DECLINLINE(int) usbfilterGetNum(PCUSBFILTER pFilter, USBFILTERIDX enmFieldIdx)
+{
+ if ((unsigned)enmFieldIdx < (unsigned)USBFILTERIDX_END)
+ {
+ switch (pFilter->aFields[enmFieldIdx].enmMatch)
+ {
+ case USBFILTERMATCH_NUM_EXACT:
+ case USBFILTERMATCH_NUM_EXACT_NP:
+ return pFilter->aFields[enmFieldIdx].u16Value;
+
+ default:
+ AssertMsgFailed(("%d\n", pFilter->aFields[enmFieldIdx].enmMatch));
+ case USBFILTERMATCH_IGNORE:
+ case USBFILTERMATCH_PRESENT:
+ case USBFILTERMATCH_NUM_EXPRESSION:
+ case USBFILTERMATCH_NUM_EXPRESSION_NP:
+ case USBFILTERMATCH_STR_EXACT:
+ case USBFILTERMATCH_STR_EXACT_NP:
+ case USBFILTERMATCH_STR_PATTERN:
+ case USBFILTERMATCH_STR_PATTERN_NP:
+ break;
+ }
+ }
+ return -1;
+}
+
+
+/**
+ * Performs simple pattern matching.
+ *
+ * @returns true on match and false on mismatch.
+ * @param pszExpr The numeric expression.
+ * @param u16Value The value to match.
+ */
+static bool usbfilterMatchNumExpression(const char *pszExpr, uint16_t u16Value)
+{
+ /*
+ * The string format is: "int:((<m>)|([<m>]-[<n>]))(,(<m>)|([<m>]-[<n>]))*"
+ * where <m> and <n> are numbers in decimal, hex (0xNNN) or octal (0NNN).
+ * Spaces are allowed around <m> and <n>.
+ */
+ while (*pszExpr)
+ {
+ if (!strncmp(pszExpr, RT_STR_TUPLE("int:")))
+ pszExpr += strlen("int:");
+
+ /*
+ * Skip remnants of the previous expression and any empty expressions.
+ * ('|' is the expression separator.)
+ */
+ while (*pszExpr == '|' || RT_C_IS_BLANK(*pszExpr) || *pszExpr == '(' || *pszExpr == ')')
+ pszExpr++;
+ if (!*pszExpr)
+ break;
+
+ /*
+ * Parse the expression.
+ */
+ int rc;
+ uint16_t u16First = 0;
+ uint16_t u16Last = 0;
+ if (*pszExpr == '-')
+ {
+ /* -N */
+ pszExpr++;
+ rc = usbfilterReadNumber(&pszExpr, &u16Last);
+ }
+ else
+ {
+ /* M or M,N or M-N or M- */
+ rc = usbfilterReadNumber(&pszExpr, &u16First);
+ if (RT_SUCCESS(rc))
+ {
+ pszExpr = usbfilterSkipBlanks(pszExpr);
+ if (*pszExpr == '-')
+ {
+ pszExpr++;
+ if (*pszExpr) /* M-N */
+ rc = usbfilterReadNumber(&pszExpr, &u16Last);
+ else /* M- */
+ u16Last = UINT16_MAX;
+ }
+ else if (*pszExpr == ',')
+ {
+ /* M,N */
+ pszExpr++;
+ rc = usbfilterReadNumber(&pszExpr, &u16Last);
+ }
+ else
+ {
+ /* M */
+ u16Last = u16First;
+ }
+ }
+ }
+
+ /* On success, we should either be at the end of the string, at an expression
+ * separator (|), or at the end of an interval filter (')').
+ */
+ if (RT_SUCCESS(rc) && *pszExpr && *pszExpr != '|' && *pszExpr != ')')
+ rc = VERR_INVALID_PARAMETER;
+ if (RT_SUCCESS(rc))
+ {
+ /*
+ * Swap the values if the order is mixed up.
+ */
+ if (u16First > u16Last)
+ {
+ uint16_t u16Tmp = u16First;
+ u16First = u16Last;
+ u16Last = u16Tmp;
+ }
+
+ /*
+ * Perform the compare.
+ */
+ if ( u16Value >= u16First
+ && u16Value <= u16Last)
+ return true;
+ }
+ else
+ {
+ /*
+ * Skip the bad expression.
+ * ('|' is the expression separator.)
+ */
+ while (*pszExpr && *pszExpr != '|')
+ pszExpr++;
+ }
+ }
+
+ return false;
+}
+
+
+/**
+ * Performs simple pattern matching.
+ *
+ * @returns true on match and false on mismatch.
+ * @param pszPattern The pattern to match against.
+ * @param psz The string to match.
+ */
+static bool usbfilterMatchStringPattern(const char *pszPattern, const char *psz)
+{
+ char ch;
+ while ((ch = *pszPattern++))
+ {
+ if (ch == '?')
+ {
+ /*
+ * Matches one char or end of string.
+ */
+ if (*psz)
+ psz++;
+ }
+ else if (ch == '*')
+ {
+ /*
+ * Matches zero or more characters.
+ */
+ /* skip subsequent wildcards */
+ while ( (ch = *pszPattern) == '*'
+ || ch == '?')
+ pszPattern++;
+ if (!ch)
+ /* Pattern ends with a '*' and thus matches the rest of psz. */
+ return true;
+
+ /* Find the length of the following exact pattern sequence. */
+ ssize_t cchMatch = 1;
+ while ( (ch = pszPattern[cchMatch]) != '\0'
+ && ch != '*'
+ && ch != '?')
+ cchMatch++;
+
+ /* Check if the exact pattern sequence is too long. */
+ ssize_t cch = strlen(psz);
+ cch -= cchMatch;
+ if (cch < 0)
+ return false;
+
+ /* Is the rest an exact match? */
+ if (!ch)
+ return memcmp(psz + cch, pszPattern, cchMatch) == 0;
+
+ /*
+ * This is where things normally starts to get recursive or ugly.
+ *
+ * Just to make life simple, we'll skip the nasty stuff and say
+ * that we will do a maximal wildcard match and forget about any
+ * alternative matches.
+ *
+ * If somebody is bored out of their mind one day, feel free to
+ * implement correct matching without using recursion.
+ */
+ ch = *pszPattern;
+ const char *pszMatch = NULL;
+ while ( cch-- >= 0
+ && *psz)
+ {
+ if ( *psz == ch
+ && !strncmp(psz, pszPattern, cchMatch))
+ pszMatch = psz;
+ psz++;
+ }
+ if (!pszMatch)
+ return false;
+
+ /* advance */
+ psz = pszMatch + cchMatch;
+ pszPattern += cchMatch;
+ }
+ else
+ {
+ /* exact match */
+ if (ch != *psz)
+ return false;
+ psz++;
+ }
+ }
+
+ return *psz == '\0';
+}
+
+
+/**
+ * Match a filter against a device.
+ *
+ * @returns true if they match, false if not.
+ *
+ * @param pFilter The filter to match with.
+ * @param pDevice The device data. This is a filter (type ignored) that
+ * contains 'exact' values for all present fields and 'ignore'
+ * values for the non-present fields.
+ *
+ * @remark Both the filter and the device are ASSUMED to be valid because
+ * we don't wish to waste any time in this function.
+ */
+USBLIB_DECL(bool) USBFilterMatch(PCUSBFILTER pFilter, PCUSBFILTER pDevice)
+{
+ return USBFilterMatchRated(pFilter, pDevice) > 0;
+}
+
+
+#if 0 /*def IN_RING0*/ /** @todo convert to proper logging. */
+extern "C" int printf(const char *format, ...);
+# define dprintf(a) printf a
+#else
+# define dprintf(a) do {} while (0)
+#endif
+
+/**
+ * Match a filter against a device and rate the result.
+ *
+ * @returns -1 if no match, matching rate between 1 and 100 (inclusive) if matched.
+ *
+ * @param pFilter The filter to match with.
+ * @param pDevice The device data. This is a filter (type ignored) that
+ * contains 'exact' values for all present fields and 'ignore'
+ * values for the non-present fields.
+ *
+ * @remark Both the filter and the device are ASSUMED to be valid because
+ * we don't wish to waste any time in this function.
+ */
+USBLIB_DECL(int) USBFilterMatchRated(PCUSBFILTER pFilter, PCUSBFILTER pDevice)
+{
+ unsigned iRate = 0;
+dprintf(("USBFilterMatchRated: %p %p\n", pFilter, pDevice));
+
+ for (unsigned i = 0; i < RT_ELEMENTS(pFilter->aFields); i++)
+ {
+ switch (pFilter->aFields[i].enmMatch)
+ {
+ case USBFILTERMATCH_IGNORE:
+ iRate += 2;
+ break;
+
+ case USBFILTERMATCH_PRESENT:
+ if (pDevice->aFields[i].enmMatch == USBFILTERMATCH_IGNORE)
+ {
+dprintf(("filter match[%d]: !present\n", i));
+ return -1;
+ }
+ iRate += 2;
+ break;
+
+ case USBFILTERMATCH_NUM_EXACT:
+ if ( pDevice->aFields[i].enmMatch == USBFILTERMATCH_IGNORE
+ || pFilter->aFields[i].u16Value != pDevice->aFields[i].u16Value)
+ {
+if (pDevice->aFields[i].enmMatch == USBFILTERMATCH_IGNORE)
+ dprintf(("filter match[%d]: !num_exact device=ignore\n", i));
+else
+ dprintf(("filter match[%d]: !num_exact %#x (filter) != %#x (device)\n", i, pFilter->aFields[i].u16Value, pDevice->aFields[i].u16Value));
+ return -1;
+ }
+ iRate += 2;
+ break;
+
+ case USBFILTERMATCH_NUM_EXACT_NP:
+ if ( pDevice->aFields[i].enmMatch != USBFILTERMATCH_IGNORE
+ && pFilter->aFields[i].u16Value != pDevice->aFields[i].u16Value)
+ {
+dprintf(("filter match[%d]: !num_exact_np %#x (filter) != %#x (device)\n", i, pFilter->aFields[i].u16Value, pDevice->aFields[i].u16Value));
+ return -1;
+ }
+ iRate += 2;
+ break;
+
+ case USBFILTERMATCH_NUM_EXPRESSION:
+ if ( pDevice->aFields[i].enmMatch == USBFILTERMATCH_IGNORE
+ || !usbfilterMatchNumExpression(usbfilterGetString(pFilter, (USBFILTERIDX)i),
+ pDevice->aFields[i].u16Value))
+ {
+dprintf(("filter match[%d]: !num_expression\n", i));
+ return -1;
+ }
+ iRate += 1;
+ break;
+
+ case USBFILTERMATCH_NUM_EXPRESSION_NP:
+ if ( pDevice->aFields[i].enmMatch != USBFILTERMATCH_IGNORE
+ && !usbfilterMatchNumExpression(usbfilterGetString(pFilter, (USBFILTERIDX)i),
+ pDevice->aFields[i].u16Value))
+ {
+dprintf(("filter match[%d]: !num_expression_no\n", i));
+ return -1;
+ }
+ iRate += 1;
+ break;
+
+ case USBFILTERMATCH_STR_EXACT:
+ if ( pDevice->aFields[i].enmMatch == USBFILTERMATCH_IGNORE
+ || strcmp(usbfilterGetString(pFilter, (USBFILTERIDX)i),
+ usbfilterGetString(pDevice, (USBFILTERIDX)i)))
+ {
+dprintf(("filter match[%d]: !str_exact\n", i));
+ return -1;
+ }
+ iRate += 2;
+ break;
+
+ case USBFILTERMATCH_STR_EXACT_NP:
+ if ( pDevice->aFields[i].enmMatch != USBFILTERMATCH_IGNORE
+ && strcmp(usbfilterGetString(pFilter, (USBFILTERIDX)i),
+ usbfilterGetString(pDevice, (USBFILTERIDX)i)))
+ {
+dprintf(("filter match[%d]: !str_exact_np\n", i));
+ return -1;
+ }
+ iRate += 2;
+ break;
+
+ case USBFILTERMATCH_STR_PATTERN:
+ if ( pDevice->aFields[i].enmMatch == USBFILTERMATCH_IGNORE
+ || !usbfilterMatchStringPattern(usbfilterGetString(pFilter, (USBFILTERIDX)i),
+ usbfilterGetString(pDevice, (USBFILTERIDX)i)))
+ {
+dprintf(("filter match[%d]: !str_pattern\n", i));
+ return -1;
+ }
+ iRate += 1;
+ break;
+
+ case USBFILTERMATCH_STR_PATTERN_NP:
+ if ( pDevice->aFields[i].enmMatch != USBFILTERMATCH_IGNORE
+ && !usbfilterMatchStringPattern(usbfilterGetString(pFilter, (USBFILTERIDX)i),
+ usbfilterGetString(pDevice, (USBFILTERIDX)i)))
+ {
+dprintf(("filter match[%d]: !str_pattern_np\n", i));
+ return -1;
+ }
+ iRate += 1;
+ break;
+
+ default:
+ AssertMsgFailed(("#%d: %d\n", i, pFilter->aFields[i].enmMatch));
+ return -1;
+ }
+ }
+
+ /* iRate is the range 0..2*cFields - recalc to percent. */
+dprintf(("filter match: iRate=%d", iRate));
+ return iRate == 2 * RT_ELEMENTS(pFilter->aFields)
+ ? 100
+ : (iRate * 100) / (2 * RT_ELEMENTS(pFilter->aFields));
+}
+
+
+/**
+ * Match a filter against a USBDEVICE.
+ *
+ * @returns true if they match, false if not.
+ *
+ * @param pFilter The filter to match with.
+ * @param pDevice The device to match.
+ *
+ * @remark Both the filter and the device are ASSUMED to be valid because
+ * we don't wish to waste any time in this function.
+ */
+USBLIB_DECL(bool) USBFilterMatchDevice(PCUSBFILTER pFilter, PUSBDEVICE pDevice)
+{
+ for (unsigned i = 0; i < RT_ELEMENTS(pFilter->aFields); i++)
+ {
+ switch (pFilter->aFields[i].enmMatch)
+ {
+ case USBFILTERMATCH_IGNORE:
+ break;
+
+ case USBFILTERMATCH_PRESENT:
+ {
+ const char *psz;
+ switch (i)
+ {
+ case USBFILTERIDX_MANUFACTURER_STR: psz = pDevice->pszManufacturer; break;
+ case USBFILTERIDX_PRODUCT_STR: psz = pDevice->pszProduct; break;
+ case USBFILTERIDX_SERIAL_NUMBER_STR: psz = pDevice->pszSerialNumber; break;
+ default: psz = ""; break;
+ }
+ if (!psz)
+ return false;
+ break;
+ }
+
+ case USBFILTERMATCH_NUM_EXACT:
+ case USBFILTERMATCH_NUM_EXACT_NP:
+ case USBFILTERMATCH_NUM_EXPRESSION:
+ case USBFILTERMATCH_NUM_EXPRESSION_NP:
+ {
+ uint16_t u16Value;
+ switch (i)
+ {
+ case USBFILTERIDX_VENDOR_ID: u16Value = pDevice->idVendor; break;
+ case USBFILTERIDX_PRODUCT_ID: u16Value = pDevice->idProduct; break;
+ case USBFILTERIDX_DEVICE: u16Value = pDevice->bcdDevice; break;
+ case USBFILTERIDX_DEVICE_CLASS: u16Value = pDevice->bDeviceClass; break;
+ case USBFILTERIDX_DEVICE_SUB_CLASS: u16Value = pDevice->bDeviceSubClass; break;
+ case USBFILTERIDX_DEVICE_PROTOCOL: u16Value = pDevice->bDeviceProtocol; break;
+ case USBFILTERIDX_BUS: u16Value = pDevice->bBus; break;
+ case USBFILTERIDX_PORT: u16Value = pDevice->bPort; break;
+ default: u16Value = UINT16_MAX; break;
+
+ }
+ switch (pFilter->aFields[i].enmMatch)
+ {
+ case USBFILTERMATCH_NUM_EXACT:
+ case USBFILTERMATCH_NUM_EXACT_NP:
+ if (pFilter->aFields[i].u16Value != u16Value)
+ return false;
+ break;
+ case USBFILTERMATCH_NUM_EXPRESSION:
+ case USBFILTERMATCH_NUM_EXPRESSION_NP:
+ if (!usbfilterMatchNumExpression(usbfilterGetString(pFilter, (USBFILTERIDX)i), u16Value))
+ return false;
+ break;
+ }
+ break;
+ }
+
+ case USBFILTERMATCH_STR_EXACT:
+ case USBFILTERMATCH_STR_EXACT_NP:
+ case USBFILTERMATCH_STR_PATTERN:
+ case USBFILTERMATCH_STR_PATTERN_NP:
+ {
+ const char *psz;
+ switch (i)
+ {
+ case USBFILTERIDX_MANUFACTURER_STR: psz = pDevice->pszManufacturer; break;
+ case USBFILTERIDX_PRODUCT_STR: psz = pDevice->pszProduct; break;
+ case USBFILTERIDX_SERIAL_NUMBER_STR: psz = pDevice->pszSerialNumber; break;
+ default: psz = NULL; break;
+ }
+ switch (pFilter->aFields[i].enmMatch)
+ {
+ case USBFILTERMATCH_STR_EXACT:
+ if ( !psz
+ || strcmp(usbfilterGetString(pFilter, (USBFILTERIDX)i), psz))
+ return false;
+ break;
+
+ case USBFILTERMATCH_STR_EXACT_NP:
+ if ( psz
+ && strcmp(usbfilterGetString(pFilter, (USBFILTERIDX)i), psz))
+ return false;
+ break;
+
+ case USBFILTERMATCH_STR_PATTERN:
+ if ( !psz
+ || !usbfilterMatchStringPattern(usbfilterGetString(pFilter, (USBFILTERIDX)i), psz))
+ return false;
+ break;
+
+ case USBFILTERMATCH_STR_PATTERN_NP:
+ if ( psz
+ && !usbfilterMatchStringPattern(usbfilterGetString(pFilter, (USBFILTERIDX)i), psz))
+ return false;
+ break;
+ }
+ break;
+ }
+
+ default:
+ AssertMsgFailed(("#%d: %d\n", i, pFilter->aFields[i].enmMatch));
+ return false;
+ }
+ }
+
+ return true;
+}
+
+
+/**
+ * Checks if the two filters are identical.
+ *
+ * @returns true if the are identical, false if they aren't.
+ * @param pFilter The first filter.
+ * @param pFilter2 The second filter.
+ */
+USBLIB_DECL(bool) USBFilterIsIdentical(PCUSBFILTER pFilter, PCUSBFILTER pFilter2)
+{
+ /* Lazy works here because we're darn strict with zero padding and such elsewhere. */
+ return memcmp(pFilter, pFilter2, sizeof(*pFilter)) == 0;
+}
+
+
+
+/**
+ * Sets the filter type.
+ *
+ * @returns VBox status code.
+ * @retval VERR_INVALID_PARAMETER if the filter type is invalid.
+ * @retval VERR_INVALID_MAGIC if pFilter is invalid.
+ *
+ * @param pFilter The filter.
+ * @param enmType The new filter type.
+ */
+USBLIB_DECL(int) USBFilterSetFilterType(PUSBFILTER pFilter, USBFILTERTYPE enmType)
+{
+ AssertReturn(pFilter->u32Magic == USBFILTER_MAGIC, VERR_INVALID_MAGIC);
+ AssertReturn(enmType > USBFILTERTYPE_INVALID && enmType < USBFILTERTYPE_END, VERR_INVALID_PARAMETER);
+
+ pFilter->enmType = enmType;
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * Replaces the string value of a field.
+ *
+ * This will remove any existing string value current held by the field from the
+ * string table and then attempt to add the new value. This function can be used
+ * to delete any assigned string before changing the type to numeric by passing
+ * in an empty string. This works because the first byte in the string table is
+ * reserved for the empty (NULL) string.
+ *
+ * @returns VBox status code.
+ * @retval VINF_SUCCESS on success.
+ * @retval VERR_BUFFER_OVERFLOW if the string table is full.
+ * @retval VERR_INVALID_PARAMETER if the enmFieldIdx isn't valid.
+ * @retval VERR_INVALID_POINTER if pszString isn't valid.
+ * @retval VERR_INVALID_MAGIC if pFilter is invalid.
+ *
+ * @param pFilter The filter.
+ * @param enmFieldIdx The field index.
+ * @param pszString The string to add.
+ * @param fPurge Purge invalid UTF-8 encoding and control characters
+ * before setting it.
+ */
+static int usbfilterSetString(PUSBFILTER pFilter, USBFILTERIDX enmFieldIdx, const char *pszString, bool fPurge)
+{
+ /*
+ * Validate input.
+ */
+ AssertReturn(pFilter->u32Magic == USBFILTER_MAGIC, VERR_INVALID_MAGIC);
+ AssertReturn((unsigned)enmFieldIdx < (unsigned)USBFILTERIDX_END, VERR_INVALID_PARAMETER);
+ AssertPtrReturn(pszString, VERR_INVALID_POINTER);
+
+ Assert(pFilter->offCurEnd < sizeof(pFilter->achStrTab));
+ Assert(pFilter->achStrTab[pFilter->offCurEnd] == '\0');
+
+ /*
+ * Remove old string value if any.
+ */
+ if ( USBFilterIsMethodUsingStringValue((USBFILTERMATCH)pFilter->aFields[enmFieldIdx].enmMatch)
+ && pFilter->aFields[enmFieldIdx].u16Value != 0)
+ {
+ uint32_t off = pFilter->aFields[enmFieldIdx].u16Value;
+ pFilter->aFields[enmFieldIdx].u16Value = 0; /* Assign it to the NULL string. */
+
+ unsigned cchShift = (unsigned)strlen(&pFilter->achStrTab[off]) + 1;
+ ssize_t cchToMove = (pFilter->offCurEnd + 1) - (off + cchShift);
+ Assert(cchToMove >= 0);
+ if (cchToMove > 0)
+ {
+ /* We're not last - must shift the strings. */
+ memmove(&pFilter->achStrTab[off], &pFilter->achStrTab[off + cchShift], cchToMove);
+ for (unsigned i = 0; i < RT_ELEMENTS(pFilter->aFields); i++)
+ if ( pFilter->aFields[i].u16Value >= off
+ && USBFilterIsMethodUsingStringValue((USBFILTERMATCH)pFilter->aFields[i].enmMatch))
+ pFilter->aFields[i].u16Value -= cchShift;
+ }
+ pFilter->offCurEnd -= cchShift;
+ Assert(pFilter->offCurEnd < sizeof(pFilter->achStrTab));
+ Assert(pFilter->offCurEnd + cchShift <= sizeof(pFilter->achStrTab));
+
+ /* zero the unused string table (to allow lazyness/strictness elsewhere). */
+ memset(&pFilter->achStrTab[pFilter->offCurEnd], '\0', cchShift);
+ }
+
+ /*
+ * Make a special case for the empty string.
+ * (This also makes the delete logical above work correctly for the last string.)
+ */
+ if (!*pszString)
+ pFilter->aFields[enmFieldIdx].u16Value = 0;
+ else
+ {
+ size_t cch = strlen(pszString);
+ if (pFilter->offCurEnd + cch + 2 > sizeof(pFilter->achStrTab))
+ return VERR_BUFFER_OVERFLOW;
+
+ pFilter->aFields[enmFieldIdx].u16Value = pFilter->offCurEnd + 1;
+ memcpy(&pFilter->achStrTab[pFilter->offCurEnd + 1], pszString, cch + 1);
+ if (fPurge)
+ cch = USBLibPurgeEncoding(&pFilter->achStrTab[pFilter->offCurEnd + 1]);
+ pFilter->offCurEnd += (uint32_t)cch + 1;
+ }
+
+ return VINF_SUCCESS;
+}
+
+/**
+ * Wrapper around usbfilterSetString() that deletes any string value
+ * currently assigned to a field.
+ *
+ * Upon successful return the field contains a null string, nothing or a number.
+ *
+ * This function will validate the field index if there isn't any string
+ * value to delete, thus preventing any extra validating of the index.
+ *
+ * @returns VBox status code. See usbfilterSetString.
+ * @param pFilter The filter.
+ * @param enmFieldIdx The index of the field which string value should be deleted.
+ */
+static int usbfilterDeleteAnyStringValue(PUSBFILTER pFilter, USBFILTERIDX enmFieldIdx)
+{
+ int rc = VINF_SUCCESS;
+ if ( USBFilterIsMethodUsingStringValue((USBFILTERMATCH)pFilter->aFields[enmFieldIdx].enmMatch)
+ && pFilter->aFields[enmFieldIdx].u16Value != 0)
+ rc = usbfilterSetString(pFilter, enmFieldIdx, "", false /*fPurge*/);
+ else if ((unsigned)enmFieldIdx >= (unsigned)USBFILTERIDX_END)
+ rc = VERR_INVALID_PARAMETER;
+ return rc;
+}
+
+
+/**
+ * Sets a field to always match (ignore whatever is thrown at it).
+ *
+ * @returns VBox status code.
+ * @retval VINF_SUCCESS on success.
+ * @retval VERR_INVALID_PARAMETER if the enmFieldIdx isn't valid.
+ * @retval VERR_INVALID_MAGIC if pFilter is invalid.
+ *
+ * @param pFilter The filter.
+ * @param enmFieldIdx The field index. This must be a string field.
+ */
+USBLIB_DECL(int) USBFilterSetIgnore(PUSBFILTER pFilter, USBFILTERIDX enmFieldIdx)
+{
+ int rc = usbfilterDeleteAnyStringValue(pFilter, enmFieldIdx);
+ if (RT_SUCCESS(rc))
+ {
+ pFilter->aFields[enmFieldIdx].enmMatch = USBFILTERMATCH_IGNORE;
+ pFilter->aFields[enmFieldIdx].u16Value = 0;
+ }
+ return rc;
+}
+
+
+/**
+ * Sets a field to match on device field present only.
+ *
+ * @returns VBox status code.
+ * @retval VINF_SUCCESS on success.
+ * @retval VERR_INVALID_PARAMETER if the enmFieldIdx isn't valid.
+ * @retval VERR_INVALID_MAGIC if pFilter is invalid.
+ *
+ * @param pFilter The filter.
+ * @param enmFieldIdx The field index. This must be a string field.
+ */
+USBLIB_DECL(int) USBFilterSetPresentOnly(PUSBFILTER pFilter, USBFILTERIDX enmFieldIdx)
+{
+ int rc = usbfilterDeleteAnyStringValue(pFilter, enmFieldIdx);
+ if (RT_SUCCESS(rc))
+ {
+ pFilter->aFields[enmFieldIdx].enmMatch = USBFILTERMATCH_PRESENT;
+ pFilter->aFields[enmFieldIdx].u16Value = 0;
+ }
+ return rc;
+}
+
+
+/**
+ * Sets a field to exactly match a number.
+ *
+ * @returns VBox status code.
+ * @retval VINF_SUCCESS on success.
+ * @retval VERR_INVALID_PARAMETER if the enmFieldIdx isn't valid.
+ * @retval VERR_INVALID_MAGIC if pFilter is invalid.
+ *
+ * @param pFilter The filter.
+ * @param enmFieldIdx The field index. This must be a string field.
+ * @param u16Value The string pattern.
+ * @param fMustBePresent If set, a non-present field on the device will result in a mismatch.
+ * If clear, a non-present field on the device will match.
+ */
+USBLIB_DECL(int) USBFilterSetNumExact(PUSBFILTER pFilter, USBFILTERIDX enmFieldIdx, uint16_t u16Value, bool fMustBePresent)
+{
+ int rc = USBFilterIsNumericField(enmFieldIdx) ? VINF_SUCCESS : VERR_INVALID_PARAMETER;
+ if (RT_SUCCESS(rc))
+ {
+ rc = usbfilterDeleteAnyStringValue(pFilter, enmFieldIdx);
+ if (RT_SUCCESS(rc))
+ {
+ pFilter->aFields[enmFieldIdx].u16Value = u16Value;
+ pFilter->aFields[enmFieldIdx].enmMatch = fMustBePresent ? USBFILTERMATCH_NUM_EXACT : USBFILTERMATCH_NUM_EXACT_NP;
+ }
+ }
+
+ return rc;
+}
+
+
+/**
+ * Sets a field to match a numeric expression.
+ *
+ * @returns VBox status code.
+ * @retval VINF_SUCCESS on success.
+ * @retval VERR_BUFFER_OVERFLOW if the string table is full.
+ * @retval VERR_INVALID_PARAMETER if the enmFieldIdx or the numeric expression aren't valid.
+ * @retval VERR_INVALID_POINTER if pszExpression isn't a valid pointer.
+ * @retval VERR_INVALID_MAGIC if pFilter is invalid.
+ *
+ * @param pFilter The filter.
+ * @param enmFieldIdx The field index. This must be a string field.
+ * @param pszExpression The numeric expression.
+ * @param fMustBePresent If set, a non-present field on the device will result in a mismatch.
+ * If clear, a non-present field on the device will match.
+ */
+USBLIB_DECL(int) USBFilterSetNumExpression(PUSBFILTER pFilter, USBFILTERIDX enmFieldIdx, const char *pszExpression, bool fMustBePresent)
+{
+ int rc = USBFilterIsNumericField(enmFieldIdx) ? VINF_SUCCESS : VERR_INVALID_PARAMETER;
+ if (RT_SUCCESS(rc))
+ {
+ /* Strip leading spaces and empty sub expressions (||). */
+ while (*pszExpression && (RT_C_IS_BLANK(*pszExpression) || *pszExpression == '|'))
+ pszExpression++;
+
+ rc = usbfilterValidateNumExpression(pszExpression);
+ if (RT_SUCCESS(rc))
+ {
+ /* We could optimize the expression further (stripping spaces, convert numbers),
+ but it's more work than what it's worth and it could upset some users. */
+ rc = usbfilterSetString(pFilter, enmFieldIdx, pszExpression, false /*fPurge*/);
+ if (RT_SUCCESS(rc))
+ pFilter->aFields[enmFieldIdx].enmMatch = fMustBePresent ? USBFILTERMATCH_NUM_EXPRESSION : USBFILTERMATCH_NUM_EXPRESSION_NP;
+ else if (rc == VERR_NO_DIGITS)
+ rc = VERR_INVALID_PARAMETER;
+ }
+ }
+ return rc;
+}
+
+
+/**
+ * Sets a field to exactly match a string.
+ *
+ * @returns VBox status code.
+ * @retval VINF_SUCCESS on success.
+ * @retval VERR_BUFFER_OVERFLOW if the string table is full.
+ * @retval VERR_INVALID_PARAMETER if the enmFieldIdx isn't valid.
+ * @retval VERR_INVALID_POINTER if pszPattern isn't a valid pointer.
+ * @retval VERR_INVALID_MAGIC if pFilter is invalid.
+ *
+ * @param pFilter The filter.
+ * @param enmFieldIdx The field index. This must be a string field.
+ * @param pszValue The string value.
+ * @param fMustBePresent If set, a non-present field on the device will result in a mismatch.
+ * If clear, a non-present field on the device will match.
+ * @param fPurge Purge invalid UTF-8 encoding and control
+ * characters before setting it.
+ */
+USBLIB_DECL(int) USBFilterSetStringExact(PUSBFILTER pFilter, USBFILTERIDX enmFieldIdx, const char *pszValue,
+ bool fMustBePresent, bool fPurge)
+{
+ int rc = USBFilterIsStringField(enmFieldIdx) ? VINF_SUCCESS : VERR_INVALID_PARAMETER;
+ if (RT_SUCCESS(rc))
+ {
+ rc = usbfilterSetString(pFilter, enmFieldIdx, pszValue, fPurge);
+ if (RT_SUCCESS(rc))
+ pFilter->aFields[enmFieldIdx].enmMatch = fMustBePresent ? USBFILTERMATCH_STR_EXACT : USBFILTERMATCH_STR_EXACT_NP;
+ }
+ return rc;
+}
+
+
+/**
+ * Sets a field to match a string pattern.
+ *
+ * @returns VBox status code.
+ * @retval VINF_SUCCESS on success.
+ * @retval VERR_BUFFER_OVERFLOW if the string table is full.
+ * @retval VERR_INVALID_PARAMETER if the enmFieldIdx or pattern aren't valid.
+ * @retval VERR_INVALID_POINTER if pszPattern isn't a valid pointer.
+ * @retval VERR_INVALID_MAGIC if pFilter is invalid.
+ *
+ * @param pFilter The filter.
+ * @param enmFieldIdx The field index. This must be a string field.
+ * @param pszPattern The string pattern.
+ * @param fMustBePresent If set, a non-present field on the device will result in a mismatch.
+ * If clear, a non-present field on the device will match.
+ */
+USBLIB_DECL(int) USBFilterSetStringPattern(PUSBFILTER pFilter, USBFILTERIDX enmFieldIdx, const char *pszPattern, bool fMustBePresent)
+{
+ int rc = USBFilterIsStringField(enmFieldIdx) ? VINF_SUCCESS : VERR_INVALID_PARAMETER;
+ if (RT_SUCCESS(rc))
+ {
+ rc = usbfilterValidateStringPattern(pszPattern);
+ if (RT_SUCCESS(rc))
+ {
+ rc = usbfilterSetString(pFilter, enmFieldIdx, pszPattern, false /*fPurge*/);
+ if (RT_SUCCESS(rc))
+ pFilter->aFields[enmFieldIdx].enmMatch = fMustBePresent ? USBFILTERMATCH_STR_PATTERN : USBFILTERMATCH_STR_PATTERN_NP;
+ }
+ }
+ return rc;
+}
+
+
+/**
+ * Sets the must-be-present part of a field.
+ *
+ * This only works on field which already has matching criteria. This means
+ * that field marked 'ignore' will not be processed and will result in a
+ * warning status code.
+ *
+ * @returns VBox status code.
+ * @retval VINF_SUCCESS on success.
+ * @retval VWRN_INVALID_PARAMETER if the field is marked 'ignore'. No assertions.
+ * @retval VERR_INVALID_PARAMETER if the enmFieldIdx or pattern aren't valid.
+ * @retval VERR_INVALID_POINTER if pszPattern isn't a valid pointer.
+ * @retval VERR_INVALID_MAGIC if pFilter is invalid.
+ *
+ * @param pFilter The filter.
+ * @param enmFieldIdx The field index.
+ * @param fMustBePresent If set, a non-present field on the device will result in a mismatch.
+ * If clear, a non-present field on the device will match.
+ */
+USBLIB_DECL(int) USBFilterSetMustBePresent(PUSBFILTER pFilter, USBFILTERIDX enmFieldIdx, bool fMustBePresent)
+{
+ AssertPtrReturn(pFilter, VERR_INVALID_POINTER);
+ AssertReturn(pFilter->u32Magic == USBFILTER_MAGIC, VERR_INVALID_MAGIC);
+ AssertReturn((unsigned)enmFieldIdx < (unsigned)USBFILTERIDX_END, VERR_INVALID_PARAMETER);
+
+ USBFILTERMATCH enmMatch = (USBFILTERMATCH)pFilter->aFields[enmFieldIdx].enmMatch;
+ if (fMustBePresent)
+ {
+ switch (enmMatch)
+ {
+ case USBFILTERMATCH_IGNORE:
+ return VWRN_INVALID_PARAMETER;
+
+ case USBFILTERMATCH_PRESENT:
+ case USBFILTERMATCH_NUM_EXACT:
+ case USBFILTERMATCH_NUM_EXPRESSION:
+ case USBFILTERMATCH_STR_EXACT:
+ case USBFILTERMATCH_STR_PATTERN:
+ break;
+
+ case USBFILTERMATCH_NUM_EXACT_NP:
+ enmMatch = USBFILTERMATCH_NUM_EXACT;
+ break;
+ case USBFILTERMATCH_NUM_EXPRESSION_NP:
+ enmMatch = USBFILTERMATCH_NUM_EXPRESSION;
+ break;
+ case USBFILTERMATCH_STR_EXACT_NP:
+ enmMatch = USBFILTERMATCH_STR_EXACT;
+ break;
+ case USBFILTERMATCH_STR_PATTERN_NP:
+ enmMatch = USBFILTERMATCH_STR_PATTERN;
+ break;
+ default:
+ AssertMsgFailedReturn(("%p: enmFieldIdx=%d enmMatch=%d\n", pFilter, enmFieldIdx, enmMatch), VERR_INVALID_MAGIC);
+ }
+ }
+ else
+ {
+ switch (enmMatch)
+ {
+ case USBFILTERMATCH_IGNORE:
+ return VWRN_INVALID_PARAMETER;
+
+ case USBFILTERMATCH_NUM_EXACT_NP:
+ case USBFILTERMATCH_STR_PATTERN_NP:
+ case USBFILTERMATCH_STR_EXACT_NP:
+ case USBFILTERMATCH_NUM_EXPRESSION_NP:
+ break;
+
+ case USBFILTERMATCH_PRESENT:
+ enmMatch = USBFILTERMATCH_IGNORE;
+ break;
+ case USBFILTERMATCH_NUM_EXACT:
+ enmMatch = USBFILTERMATCH_NUM_EXACT_NP;
+ break;
+ case USBFILTERMATCH_NUM_EXPRESSION:
+ enmMatch = USBFILTERMATCH_NUM_EXPRESSION_NP;
+ break;
+ case USBFILTERMATCH_STR_EXACT:
+ enmMatch = USBFILTERMATCH_STR_EXACT_NP;
+ break;
+ case USBFILTERMATCH_STR_PATTERN:
+ enmMatch = USBFILTERMATCH_STR_PATTERN_NP;
+ break;
+
+ default:
+ AssertMsgFailedReturn(("%p: enmFieldIdx=%d enmMatch=%d\n", pFilter, enmFieldIdx, enmMatch), VERR_INVALID_MAGIC);
+ }
+ }
+
+ pFilter->aFields[enmFieldIdx].enmMatch = enmMatch;
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * Gets the filter type.
+ *
+ * @returns The filter type.
+ * USBFILTERTYPE_INVALID if the filter is invalid.
+ * @param pFilter The filter.
+ */
+USBLIB_DECL(USBFILTERTYPE) USBFilterGetFilterType(PCUSBFILTER pFilter)
+{
+ AssertReturn(pFilter->u32Magic == USBFILTER_MAGIC, USBFILTERTYPE_INVALID);
+ return pFilter->enmType;
+}
+
+
+/**
+ * Gets the matching method for a field.
+ *
+ * @returns The matching method on success, UBFILTERMATCH_INVALID on invalid field index.
+ * @param pFilter The filter.
+ * @param enmFieldIdx The field index.
+ */
+USBLIB_DECL(USBFILTERMATCH) USBFilterGetMatchingMethod(PCUSBFILTER pFilter, USBFILTERIDX enmFieldIdx)
+{
+ if ( pFilter->u32Magic == USBFILTER_MAGIC
+ && (unsigned)enmFieldIdx < (unsigned)USBFILTERIDX_END)
+ return (USBFILTERMATCH)pFilter->aFields[enmFieldIdx].enmMatch;
+ return USBFILTERMATCH_INVALID;
+}
+
+
+/**
+ * Gets the numeric value of a field.
+ *
+ * The field must contain a number, we're not doing any conversions for you.
+ *
+ * @returns VBox status code.
+ * @retval VINF_SUCCESS on success.
+ * @retval VERR_INVALID_PARAMETER if the enmFieldIdx isn't valid or if the field doesn't contain a number.
+ * @retval VERR_INVALID_MAGIC if pFilter is invalid.
+ *
+ * @param pFilter The filter.
+ * @param enmFieldIdx The field index.
+ * @param pu16Value Where to store the value.
+ */
+USBLIB_DECL(int) USBFilterQueryNum(PUSBFILTER pFilter, USBFILTERIDX enmFieldIdx, uint16_t *pu16Value)
+{
+ AssertReturn(pFilter->u32Magic == USBFILTER_MAGIC, VERR_INVALID_MAGIC);
+ int iValue = usbfilterGetNum(pFilter, enmFieldIdx);
+ if (iValue == -1)
+ return VERR_INVALID_PARAMETER;
+ *pu16Value = (uint16_t)iValue;
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * Gets the numeric value of a field.
+ *
+ * The field must contain a number, we're not doing any conversions for you.
+ *
+ * @returns The field value on success, -1 on failure (invalid input / not numeric).
+ *
+ * @param pFilter The filter.
+ * @param enmFieldIdx The field index.
+ */
+USBLIB_DECL(int) USBFilterGetNum(PCUSBFILTER pFilter, USBFILTERIDX enmFieldIdx)
+{
+ AssertReturn(pFilter->u32Magic == USBFILTER_MAGIC, -1);
+ return usbfilterGetNum(pFilter, enmFieldIdx);
+}
+
+
+/**
+ * Gets the string value of a field.
+ *
+ * The field must contain a string, we're not doing any conversions for you.
+ *
+ * @returns VBox status code.
+ * @retval VINF_SUCCESS on success.
+ * @retval VERR_BUFFER_OVERFLOW if the buffer isn't sufficient to hold the string. The buffer
+ * will be filled with as much of the string that'll fit.
+ * @retval VERR_INVALID_PARAMETER if the enmFieldIdx isn't valid or if the field doesn't contain a string.
+ * @retval VERR_INVALID_MAGIC if pFilter is invalid.
+ *
+ * @param pFilter The filter.
+ * @param enmFieldIdx The field index.
+ * @param pszBuf Where to store the string.
+ * @param cchBuf The size of the buffer.
+ */
+USBLIB_DECL(int) USBFilterQueryString(PUSBFILTER pFilter, USBFILTERIDX enmFieldIdx, char *pszBuf, size_t cchBuf)
+{
+ AssertReturn(pFilter->u32Magic == USBFILTER_MAGIC, VERR_INVALID_MAGIC);
+
+ const char *psz = usbfilterGetString(pFilter, enmFieldIdx);
+ if (RT_UNLIKELY(!psz))
+ return VERR_INVALID_PARAMETER;
+
+ int rc = VINF_SUCCESS;
+ size_t cch = strlen(psz);
+ if (cch < cchBuf)
+ memcpy(pszBuf, psz, cch + 1);
+ else
+ {
+ rc = VERR_BUFFER_OVERFLOW;
+ if (cchBuf)
+ {
+ memcpy(pszBuf, psz, cchBuf - 1);
+ pszBuf[cchBuf - 1] = '\0';
+ }
+ }
+
+ return rc;
+}
+
+
+/**
+ * Gets the string table entry for a field.
+ *
+ * @returns Pointer to the string. (readonly!)
+ *
+ * @param pFilter The filter.
+ * @param enmFieldIdx The field index.
+ */
+USBLIB_DECL(const char *) USBFilterGetString(PCUSBFILTER pFilter, USBFILTERIDX enmFieldIdx)
+{
+ AssertReturn(pFilter->u32Magic == USBFILTER_MAGIC, NULL);
+
+ const char *psz = usbfilterGetString(pFilter, enmFieldIdx);
+ if (RT_UNLIKELY(!psz))
+ return NULL;
+ return psz;
+}
+
+
+/**
+ * Gets the string length of a field containing a string.
+ *
+ * @returns String length on success, -1 on failure (not a string, bad filter).
+ * @param pFilter The filter.
+ * @param enmFieldIdx The field index.
+ */
+USBLIB_DECL(ssize_t) USBFilterGetStringLen(PCUSBFILTER pFilter, USBFILTERIDX enmFieldIdx)
+{
+ if (RT_LIKELY(pFilter->u32Magic == USBFILTER_MAGIC))
+ {
+ const char *psz = usbfilterGetString(pFilter, enmFieldIdx);
+ if (RT_LIKELY(psz))
+ return strlen(psz);
+ }
+ return -1;
+}
+
+
+/**
+ * Check if any of the fields are set to something substatial.
+ *
+ * Consider the fileter a wildcard if this returns false.
+ *
+ * @returns true / false.
+ * @param pFilter The filter.
+ */
+USBLIB_DECL(bool) USBFilterHasAnySubstatialCriteria(PCUSBFILTER pFilter)
+{
+ AssertReturn(pFilter->u32Magic == USBFILTER_MAGIC, false);
+
+ for (unsigned i = 0; i < RT_ELEMENTS(pFilter->aFields); i++)
+ {
+ switch (pFilter->aFields[i].enmMatch)
+ {
+ case USBFILTERMATCH_IGNORE:
+ case USBFILTERMATCH_PRESENT:
+ break;
+
+ case USBFILTERMATCH_NUM_EXACT:
+ case USBFILTERMATCH_NUM_EXACT_NP:
+ case USBFILTERMATCH_STR_EXACT:
+ case USBFILTERMATCH_STR_EXACT_NP:
+ return true;
+
+ case USBFILTERMATCH_NUM_EXPRESSION:
+ case USBFILTERMATCH_NUM_EXPRESSION_NP:
+ {
+ const char *psz = usbfilterGetString(pFilter, (USBFILTERIDX)i);
+ if (psz)
+ {
+ while (*psz && (*psz == '|' || RT_C_IS_BLANK(*psz)))
+ psz++;
+ if (*psz)
+ return true;
+ }
+ break;
+ }
+
+ case USBFILTERMATCH_STR_PATTERN:
+ case USBFILTERMATCH_STR_PATTERN_NP:
+ {
+ const char *psz = usbfilterGetString(pFilter, (USBFILTERIDX)i);
+ if (psz)
+ {
+ while (*psz && (*psz == '*' || *psz == '?'))
+ psz++;
+ if (*psz)
+ return true;
+ }
+ break;
+ }
+ }
+ }
+
+ return false;
+}
+
+
+
+/**
+ * Checks whether the specified field is a numeric field or not.
+ *
+ * @returns true / false.
+ * @param enmFieldIdx The field index.
+ */
+USBLIB_DECL(bool) USBFilterIsNumericField(USBFILTERIDX enmFieldIdx)
+{
+ switch (enmFieldIdx)
+ {
+ case USBFILTERIDX_VENDOR_ID:
+ case USBFILTERIDX_PRODUCT_ID:
+ case USBFILTERIDX_DEVICE:
+ case USBFILTERIDX_DEVICE_CLASS:
+ case USBFILTERIDX_DEVICE_SUB_CLASS:
+ case USBFILTERIDX_DEVICE_PROTOCOL:
+ case USBFILTERIDX_BUS:
+ case USBFILTERIDX_PORT:
+ return true;
+
+ default:
+ AssertMsgFailed(("%d\n", enmFieldIdx));
+ RT_FALL_THRU();
+ case USBFILTERIDX_MANUFACTURER_STR:
+ case USBFILTERIDX_PRODUCT_STR:
+ case USBFILTERIDX_SERIAL_NUMBER_STR:
+ return false;
+ }
+}
+
+
+/**
+ * Checks whether the specified field is a string field or not.
+ *
+ * @returns true / false.
+ * @param enmFieldIdx The field index.
+ */
+USBLIB_DECL(bool) USBFilterIsStringField(USBFILTERIDX enmFieldIdx)
+{
+ switch (enmFieldIdx)
+ {
+ default:
+ AssertMsgFailed(("%d\n", enmFieldIdx));
+ RT_FALL_THRU();
+ case USBFILTERIDX_VENDOR_ID:
+ case USBFILTERIDX_PRODUCT_ID:
+ case USBFILTERIDX_DEVICE:
+ case USBFILTERIDX_DEVICE_CLASS:
+ case USBFILTERIDX_DEVICE_SUB_CLASS:
+ case USBFILTERIDX_DEVICE_PROTOCOL:
+ case USBFILTERIDX_BUS:
+ case USBFILTERIDX_PORT:
+ return false;
+
+ case USBFILTERIDX_MANUFACTURER_STR:
+ case USBFILTERIDX_PRODUCT_STR:
+ case USBFILTERIDX_SERIAL_NUMBER_STR:
+ return true;
+ }
+}
+
+
+/**
+ * Checks whether the specified matching method uses a numeric value or not.
+ *
+ * @returns true / false.
+ * @param enmMatchingMethod The matching method.
+ */
+USBLIB_DECL(bool) USBFilterIsMethodUsingNumericValue(USBFILTERMATCH enmMatchingMethod)
+{
+ switch (enmMatchingMethod)
+ {
+ default:
+ AssertMsgFailed(("%d\n", enmMatchingMethod));
+ RT_FALL_THRU();
+ case USBFILTERMATCH_IGNORE:
+ case USBFILTERMATCH_PRESENT:
+ case USBFILTERMATCH_NUM_EXPRESSION:
+ case USBFILTERMATCH_NUM_EXPRESSION_NP:
+ case USBFILTERMATCH_STR_EXACT:
+ case USBFILTERMATCH_STR_EXACT_NP:
+ case USBFILTERMATCH_STR_PATTERN:
+ case USBFILTERMATCH_STR_PATTERN_NP:
+ return false;
+
+ case USBFILTERMATCH_NUM_EXACT:
+ case USBFILTERMATCH_NUM_EXACT_NP:
+ return true;
+ }
+}
+
+
+/**
+ * Checks whether the specified matching method uses a string value or not.
+ *
+ * @returns true / false.
+ * @param enmMatchingMethod The matching method.
+ */
+USBLIB_DECL(bool) USBFilterIsMethodUsingStringValue(USBFILTERMATCH enmMatchingMethod)
+{
+ switch (enmMatchingMethod)
+ {
+ default:
+ AssertMsgFailed(("%d\n", enmMatchingMethod));
+ RT_FALL_THRU();
+ case USBFILTERMATCH_IGNORE:
+ case USBFILTERMATCH_PRESENT:
+ case USBFILTERMATCH_NUM_EXACT:
+ case USBFILTERMATCH_NUM_EXACT_NP:
+ return false;
+
+ case USBFILTERMATCH_NUM_EXPRESSION:
+ case USBFILTERMATCH_NUM_EXPRESSION_NP:
+ case USBFILTERMATCH_STR_EXACT:
+ case USBFILTERMATCH_STR_EXACT_NP:
+ case USBFILTERMATCH_STR_PATTERN:
+ case USBFILTERMATCH_STR_PATTERN_NP:
+ return true;
+ }
+}
+
+
+/**
+ * Checks if a matching method is for numeric fields or not.
+ *
+ * @returns true / false.
+ * @param enmMatchingMethod The matching method.
+ */
+USBLIB_DECL(bool) USBFilterIsMethodNumeric(USBFILTERMATCH enmMatchingMethod)
+{
+ return enmMatchingMethod >= USBFILTERMATCH_NUM_FIRST
+ && enmMatchingMethod <= USBFILTERMATCH_NUM_LAST;
+}
+
+/**
+ * Checks if a matching method is for string fields or not.
+ *
+ * @returns true / false.
+ * @param enmMatchingMethod The matching method.
+ */
+USBLIB_DECL(bool) USBFilterIsMethodString(USBFILTERMATCH enmMatchingMethod)
+{
+ return enmMatchingMethod >= USBFILTERMATCH_STR_FIRST
+ && enmMatchingMethod <= USBFILTERMATCH_STR_LAST;
+}
+
diff --git a/src/VBox/HostDrivers/VBoxUSB/USBLib.cpp b/src/VBox/HostDrivers/VBoxUSB/USBLib.cpp
new file mode 100644
index 00000000..b3f9ec6b
--- /dev/null
+++ b/src/VBox/HostDrivers/VBoxUSB/USBLib.cpp
@@ -0,0 +1,71 @@
+/* $Id: USBLib.cpp $ */
+/** @file
+ * VirtualBox USB Library, Common Bits.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include <VBox/usblib.h>
+
+
+/**
+ * Calculate the hash of the serial string.
+ *
+ * 64bit FNV1a, chosen because it is designed to hash in to a power of two
+ * space, and is much quicker and simpler than, say, a half MD4.
+ *
+ * @returns the hash.
+ * @param pszSerial The serial string.
+ */
+USBLIB_DECL(uint64_t) USBLibHashSerial(const char *pszSerial)
+{
+ if (!pszSerial)
+ pszSerial = "";
+
+ const uint8_t *pu8 = (const uint8_t *)pszSerial;
+ uint64_t u64 = UINT64_C(14695981039346656037);
+ for (;;)
+ {
+ uint8_t u8 = *pu8;
+ if (!u8)
+ break;
+ u64 = (u64 * UINT64_C(1099511628211)) ^ u8;
+ pu8++;
+ }
+
+ return u64;
+}
+
diff --git a/src/VBox/HostDrivers/VBoxUSB/VBoxUSBFilterMgr.cpp b/src/VBox/HostDrivers/VBoxUSB/VBoxUSBFilterMgr.cpp
new file mode 100644
index 00000000..7533e653
--- /dev/null
+++ b/src/VBox/HostDrivers/VBoxUSB/VBoxUSBFilterMgr.cpp
@@ -0,0 +1,571 @@
+/* $Id: VBoxUSBFilterMgr.cpp $ */
+/** @file
+ * VirtualBox Ring-0 USB Filter Manager.
+ */
+
+/*
+ * Copyright (C) 2007-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include <VBox/usbfilter.h>
+#include "VBoxUSBFilterMgr.h"
+
+#include <iprt/err.h>
+#include <iprt/handletable.h>
+#include <iprt/mem.h>
+#ifdef VBOXUSBFILTERMGR_USB_SPINLOCK
+# include <iprt/spinlock.h>
+#else
+# include <iprt/semaphore.h>
+#endif
+#include <iprt/string.h>
+
+
+/*********************************************************************************************************************************
+* Defined Constants And Macros *
+*********************************************************************************************************************************/
+
+/** @def VBOXUSBFILTERMGR_LOCK
+ * Locks the filter list. Careful with scoping since this may
+ * create a temporary variable. Don't call twice in the same function.
+ */
+
+/** @def VBOXUSBFILTERMGR_UNLOCK
+ * Unlocks the filter list.
+ */
+#ifdef VBOXUSBFILTERMGR_USB_SPINLOCK
+
+# define VBOXUSBFILTERMGR_LOCK() \
+ RTSpinlockAcquire(g_Spinlock)
+
+# define VBOXUSBFILTERMGR_UNLOCK() \
+ RTSpinlockRelease(g_Spinlock)
+
+#else
+
+# define VBOXUSBFILTERMGR_LOCK() \
+ do { int rc2 = RTSemFastMutexRequest(g_Mtx); AssertRC(rc2); } while (0)
+
+# define VBOXUSBFILTERMGR_UNLOCK() \
+ do { int rc2 = RTSemFastMutexRelease(g_Mtx); AssertRC(rc2); } while (0)
+
+#endif
+
+
+/*********************************************************************************************************************************
+* Structures and Typedefs *
+*********************************************************************************************************************************/
+/** Pointer to an VBoxUSB filter. */
+typedef struct VBOXUSBFILTER *PVBOXUSBFILTER;
+/** Pointer to PVBOXUSBFILTER. */
+typedef PVBOXUSBFILTER *PPVBOXUSBFILTER;
+
+/**
+ * VBoxUSB internal filter representation.
+ */
+typedef struct VBOXUSBFILTER
+{
+ /** The core filter. */
+ USBFILTER Core;
+ /** The filter owner. */
+ VBOXUSBFILTER_CONTEXT Owner;
+ /** The filter Id. */
+ uint32_t uHnd;
+ /** Pointer to the next filter in the list. */
+ PVBOXUSBFILTER pNext;
+} VBOXUSBFILTER;
+
+/**
+ * VBoxUSB filter list.
+ */
+typedef struct VBOXUSBFILTERLIST
+{
+ /** The head pointer. */
+ PVBOXUSBFILTER pHead;
+ /** The tail pointer. */
+ PVBOXUSBFILTER pTail;
+} VBOXUSBFILTERLIST;
+/** Pointer to a VBOXUSBFILTERLIST. */
+typedef VBOXUSBFILTERLIST *PVBOXUSBFILTERLIST;
+
+
+/*********************************************************************************************************************************
+* Global Variables *
+*********************************************************************************************************************************/
+#ifdef VBOXUSBFILTERMGR_USB_SPINLOCK
+/** Spinlock protecting the filter lists. */
+static RTSPINLOCK g_Spinlock = NIL_RTSPINLOCK;
+#else
+/** Mutex protecting the filter lists. */
+static RTSEMFASTMUTEX g_Mtx = NIL_RTSEMFASTMUTEX;
+#endif
+/** The per-type filter lists.
+ * @remark The first entry is empty (USBFILTERTYPE_INVALID). */
+static VBOXUSBFILTERLIST g_aLists[USBFILTERTYPE_END];
+/** The handle table to match handles to the right filter. */
+static RTHANDLETABLE g_hHndTableFilters = NIL_RTHANDLETABLE;
+
+
+
+/**
+ * Initializes the VBoxUSB filter manager.
+ *
+ * @returns IPRT status code.
+ */
+int VBoxUSBFilterInit(void)
+{
+#ifdef VBOXUSBFILTERMGR_USB_SPINLOCK
+ int rc = RTSpinlockCreate(&g_Spinlock, RTSPINLOCK_FLAGS_INTERRUPT_SAFE, "VBoxUSBFilter");
+#else
+ int rc = RTSemFastMutexCreate(&g_Mtx);
+#endif
+ if (RT_SUCCESS(rc))
+ {
+ uint32_t fFlags;
+#ifdef VBOXUSBFILTERMGR_USB_SPINLOCK
+ fFlags = RTHANDLETABLE_FLAGS_LOCKED_IRQ_SAFE;
+#else
+ fFlags = RTHANDLETABLE_FLAGS_LOCKED;
+#endif
+ rc = RTHandleTableCreateEx(&g_hHndTableFilters, fFlags, 1 /* uBase */, 8192 /* cMax */,
+ NULL, NULL);
+ if (RT_SUCCESS(rc))
+ {
+ /* not really required, but anyway... */
+ for (unsigned i = USBFILTERTYPE_FIRST; i < RT_ELEMENTS(g_aLists); i++)
+ g_aLists[i].pHead = g_aLists[i].pTail = NULL;
+ }
+ else
+ {
+#ifdef VBOXUSBFILTERMGR_USB_SPINLOCK
+ RTSpinlockDestroy(g_Spinlock);
+ g_Spinlock = NIL_RTSPINLOCK;
+#else
+ RTSemFastMutexDestroy(g_Mtx);
+ g_Mtx = NIL_RTSEMFASTMUTEX;
+#endif
+ }
+ }
+ return rc;
+}
+
+
+/**
+ * Internal worker that frees a filter.
+ *
+ * @param pFilter The filter to free.
+ */
+static void vboxUSBFilterFree(PVBOXUSBFILTER pFilter)
+{
+ USBFilterDelete(&pFilter->Core);
+ pFilter->Owner = VBOXUSBFILTER_CONTEXT_NIL;
+ pFilter->pNext = NULL;
+ RTMemFree(pFilter);
+}
+
+
+/**
+ * Terminates the VBoxUSB filter manager.
+ */
+void VBoxUSBFilterTerm(void)
+{
+#ifdef VBOXUSBFILTERMGR_USB_SPINLOCK
+ RTSpinlockDestroy(g_Spinlock);
+ g_Spinlock = NIL_RTSPINLOCK;
+#else
+ RTSemFastMutexDestroy(g_Mtx);
+ g_Mtx = NIL_RTSEMFASTMUTEX;
+#endif
+
+ for (unsigned i = USBFILTERTYPE_FIRST; i < RT_ELEMENTS(g_aLists); i++)
+ {
+ PVBOXUSBFILTER pCur = g_aLists[i].pHead;
+ g_aLists[i].pHead = g_aLists[i].pTail = NULL;
+ while (pCur)
+ {
+ PVBOXUSBFILTER pNext = pCur->pNext;
+ RTHandleTableFree(g_hHndTableFilters, pCur->uHnd);
+ vboxUSBFilterFree(pCur);
+ pCur = pNext;
+ }
+ }
+
+ RTHandleTableDestroy(g_hHndTableFilters, NULL, NULL);
+}
+
+
+/**
+ * Adds a new filter.
+ *
+ * The filter will be validate, duplicated and added.
+ *
+ * @returns IPRT status code.
+ * @param pFilter The filter.
+ * @param Owner The filter owner. Must be non-zero.
+ * @param puId Where to store the filter ID.
+ */
+int VBoxUSBFilterAdd(PCUSBFILTER pFilter, VBOXUSBFILTER_CONTEXT Owner, uintptr_t *puId)
+{
+ /*
+ * Validate input.
+ */
+ int rc = USBFilterValidate(pFilter);
+ if (RT_FAILURE(rc))
+ return rc;
+ if (!Owner || Owner == VBOXUSBFILTER_CONTEXT_NIL)
+ return VERR_INVALID_PARAMETER;
+ if (!RT_VALID_PTR(puId))
+ return VERR_INVALID_POINTER;
+
+ /*
+ * Allocate a new filter.
+ */
+ PVBOXUSBFILTER pNew = (PVBOXUSBFILTER)RTMemAlloc(sizeof(*pNew));
+ if (!pNew)
+ return VERR_NO_MEMORY;
+ memcpy(&pNew->Core, pFilter, sizeof(pNew->Core));
+ pNew->Owner = Owner;
+ pNew->pNext = NULL;
+
+ rc = RTHandleTableAlloc(g_hHndTableFilters, pNew, &pNew->uHnd);
+ if (RT_SUCCESS(rc))
+ {
+ *puId = pNew->uHnd;
+
+ /*
+ * Insert it.
+ */
+ PVBOXUSBFILTERLIST pList = &g_aLists[pFilter->enmType];
+
+ VBOXUSBFILTERMGR_LOCK();
+
+ if (pList->pTail)
+ pList->pTail->pNext = pNew;
+ else
+ pList->pHead = pNew;
+ pList->pTail = pNew;
+
+ VBOXUSBFILTERMGR_UNLOCK();
+ }
+ else
+ RTMemFree(pNew);
+
+ return rc;
+}
+
+
+/**
+ * Removes an existing filter.
+ *
+ * The filter will be validate, duplicated and added.
+ *
+ * @returns IPRT status code.
+ * @retval VINF_SUCCESS if successfully removed.
+ * @retval VERR_FILE_NOT_FOUND if the specified filter/owner cannot be found.
+ *
+ * @param Owner The filter owner.
+ * @param uId The ID of the filter that's to be removed.
+ * Returned by VBoxUSBFilterAdd().
+ */
+int VBoxUSBFilterRemove(VBOXUSBFILTER_CONTEXT Owner, uintptr_t uId)
+{
+ /*
+ * Validate input.
+ */
+ if (!uId || uId != (uint32_t)uId)
+ return VERR_INVALID_PARAMETER;
+ if (!Owner || Owner == VBOXUSBFILTER_CONTEXT_NIL)
+ return VERR_INVALID_PARAMETER;
+
+ /*
+ * Locate and unlink it.
+ */
+ uint32_t uHnd = (uint32_t)uId;
+ PVBOXUSBFILTER pCur = NULL;
+
+ VBOXUSBFILTERMGR_LOCK();
+
+ for (unsigned i = USBFILTERTYPE_FIRST; !pCur && i < RT_ELEMENTS(g_aLists); i++)
+ {
+ PVBOXUSBFILTER pPrev = NULL;
+ pCur = g_aLists[i].pHead;
+ while (pCur)
+ {
+ if ( pCur->uHnd == uHnd
+ && pCur->Owner == Owner)
+ {
+ PVBOXUSBFILTER pNext = pCur->pNext;
+ if (pPrev)
+ pPrev->pNext = pNext;
+ else
+ g_aLists[i].pHead = pNext;
+ if (!pNext)
+ g_aLists[i].pTail = pPrev;
+ break;
+ }
+
+ pPrev = pCur;
+ pCur = pCur->pNext;
+ }
+ }
+
+ VBOXUSBFILTERMGR_UNLOCK();
+
+ /*
+ * Free it (if found).
+ */
+ if (pCur)
+ {
+ void *pv = RTHandleTableFree(g_hHndTableFilters, pCur->uHnd);
+ Assert(pv == pCur); NOREF(pv);
+ vboxUSBFilterFree(pCur);
+ return VINF_SUCCESS;
+ }
+
+ return VERR_FILE_NOT_FOUND;
+}
+
+VBOXUSBFILTER_CONTEXT VBoxUSBFilterGetOwner(uintptr_t uId)
+{
+ Assert(uId);
+ /*
+ * Validate input.
+ */
+ if (!uId || uId != (uint32_t)uId)
+ return VBOXUSBFILTER_CONTEXT_NIL;
+
+ /*
+ * Result.
+ */
+ VBOXUSBFILTER_CONTEXT Owner = VBOXUSBFILTER_CONTEXT_NIL;
+
+ VBOXUSBFILTERMGR_LOCK();
+
+ PVBOXUSBFILTER pCur = (PVBOXUSBFILTER)RTHandleTableLookup(g_hHndTableFilters, (uint32_t)uId);
+ if (pCur)
+ Owner = pCur->Owner;
+
+ Assert(Owner != VBOXUSBFILTER_CONTEXT_NIL);
+
+ VBOXUSBFILTERMGR_UNLOCK();
+
+ return Owner;
+}
+
+/**
+ * Removes all filters belonging to the specified owner.
+ *
+ * This is typically called when an owner disconnects or
+ * terminates unexpectedly.
+ *
+ * @param Owner The owner
+ */
+void VBoxUSBFilterRemoveOwner(VBOXUSBFILTER_CONTEXT Owner)
+{
+ /*
+ * Collect the filters that should be freed.
+ */
+ PVBOXUSBFILTER pToFree = NULL;
+
+ VBOXUSBFILTERMGR_LOCK();
+
+ for (unsigned i = USBFILTERTYPE_FIRST; i < RT_ELEMENTS(g_aLists); i++)
+ {
+ PVBOXUSBFILTER pPrev = NULL;
+ PVBOXUSBFILTER pCur = g_aLists[i].pHead;
+ while (pCur)
+ {
+ if (pCur->Owner == Owner)
+ {
+ PVBOXUSBFILTER pNext = pCur->pNext;
+ if (pPrev)
+ pPrev->pNext = pNext;
+ else
+ g_aLists[i].pHead = pNext;
+ if (!pNext)
+ g_aLists[i].pTail = pPrev;
+
+ pCur->pNext = pToFree;
+ pToFree = pCur;
+
+ pCur = pNext;
+ }
+ else
+ {
+ pPrev = pCur;
+ pCur = pCur->pNext;
+ }
+ }
+ }
+
+ VBOXUSBFILTERMGR_UNLOCK();
+
+ /*
+ * Free any filters we've found.
+ */
+ while (pToFree)
+ {
+ PVBOXUSBFILTER pNext = pToFree->pNext;
+ void *pv = RTHandleTableFree(g_hHndTableFilters, pToFree->uHnd);
+ Assert(pv == pToFree); NOREF(pv);
+ vboxUSBFilterFree(pToFree);
+ pToFree = pNext;
+ }
+}
+
+/**
+ * Match the specified device against the filters.
+ * Unlike the VBoxUSBFilterMatch, returns Owner also if exclude filter is matched
+ *
+ * @returns Owner on if matched, VBOXUSBFILTER_CONTEXT_NIL it not matched.
+ * @param pDevice The device data as a filter structure.
+ * See USBFilterMatch for how to construct this.
+ * @param puId Where to store the filter id (optional).
+ * @param fRemoveFltIfOneShot Whether or not to remove one-shot filters on
+ * match.
+ * @param pfFilter Where to store whether the device must be filtered or not
+ * @param pfIsOneShot Where to return whetehr the match was a one-shot
+ * filter or not. Optional.
+ *
+ */
+VBOXUSBFILTER_CONTEXT VBoxUSBFilterMatchEx(PCUSBFILTER pDevice, uintptr_t *puId,
+ bool fRemoveFltIfOneShot, bool *pfFilter, bool *pfIsOneShot)
+{
+ /*
+ * Validate input.
+ */
+ int rc = USBFilterValidate(pDevice);
+ AssertRCReturn(rc, VBOXUSBFILTER_CONTEXT_NIL);
+
+ *pfFilter = false;
+ if (puId)
+ *puId = 0;
+
+ /*
+ * Search the lists for a match.
+ * (The lists are ordered by priority.)
+ */
+ VBOXUSBFILTERMGR_LOCK();
+
+ for (unsigned i = USBFILTERTYPE_FIRST; i < RT_ELEMENTS(g_aLists); i++)
+ {
+ PVBOXUSBFILTER pPrev = NULL;
+ PVBOXUSBFILTER pCur = g_aLists[i].pHead;
+ while (pCur)
+ {
+ if (USBFilterMatch(&pCur->Core, pDevice))
+ {
+ /*
+ * Take list specific actions and return.
+ *
+ * The code does NOT implement the case where there are two or more
+ * filter clients, and one of them is releasing a device that's
+ * requested by some of the others. It's just too much work for a
+ * situation that noone will encounter.
+ */
+ if (puId)
+ *puId = pCur->uHnd;
+ VBOXUSBFILTER_CONTEXT Owner = pCur->Owner;
+ *pfFilter = !!(i != USBFILTERTYPE_IGNORE
+ && i != USBFILTERTYPE_ONESHOT_IGNORE);
+
+ if ( i == USBFILTERTYPE_ONESHOT_IGNORE
+ || i == USBFILTERTYPE_ONESHOT_CAPTURE)
+ {
+ if (fRemoveFltIfOneShot)
+ {
+ /* unlink */
+ PVBOXUSBFILTER pNext = pCur->pNext;
+ if (pPrev)
+ pPrev->pNext = pNext;
+ else
+ g_aLists[i].pHead = pNext;
+ if (!pNext)
+ g_aLists[i].pTail = pPrev;
+ }
+ }
+
+ VBOXUSBFILTERMGR_UNLOCK();
+
+ if ( i == USBFILTERTYPE_ONESHOT_IGNORE
+ || i == USBFILTERTYPE_ONESHOT_CAPTURE)
+ {
+ if (fRemoveFltIfOneShot)
+ {
+ void *pv = RTHandleTableFree(g_hHndTableFilters, pCur->uHnd);
+ Assert(pv == pCur); NOREF(pv);
+ vboxUSBFilterFree(pCur);
+ }
+ if (pfIsOneShot)
+ *pfIsOneShot = true;
+ }
+ else
+ {
+ if (pfIsOneShot)
+ *pfIsOneShot = false;
+ }
+ return Owner;
+ }
+
+ pPrev = pCur;
+ pCur = pCur->pNext;
+ }
+ }
+
+ VBOXUSBFILTERMGR_UNLOCK();
+ return VBOXUSBFILTER_CONTEXT_NIL;
+}
+
+/**
+ * Match the specified device against the filters.
+ *
+ * @returns Owner on if matched, VBOXUSBFILTER_CONTEXT_NIL it not matched.
+ * @param pDevice The device data as a filter structure.
+ * See USBFilterMatch for how to construct this.
+ * @param puId Where to store the filter id (optional).
+ */
+VBOXUSBFILTER_CONTEXT VBoxUSBFilterMatch(PCUSBFILTER pDevice, uintptr_t *puId)
+{
+ bool fFilter = false;
+ VBOXUSBFILTER_CONTEXT Owner = VBoxUSBFilterMatchEx(pDevice, puId,
+ true, /* remove filter is it's a one-shot*/
+ &fFilter, NULL /* bool * fIsOneShot */);
+ if (fFilter)
+ {
+ Assert(Owner != VBOXUSBFILTER_CONTEXT_NIL);
+ return Owner;
+ }
+ return VBOXUSBFILTER_CONTEXT_NIL;
+}
+
diff --git a/src/VBox/HostDrivers/VBoxUSB/VBoxUSBFilterMgr.h b/src/VBox/HostDrivers/VBoxUSB/VBoxUSBFilterMgr.h
new file mode 100644
index 00000000..170c750a
--- /dev/null
+++ b/src/VBox/HostDrivers/VBoxUSB/VBoxUSBFilterMgr.h
@@ -0,0 +1,68 @@
+/* $Id: VBoxUSBFilterMgr.h $ */
+/** @file
+ * VirtualBox Ring-0 USB Filter Manager.
+ */
+
+/*
+ * Copyright (C) 2007-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+#ifndef VBOX_INCLUDED_SRC_VBoxUSB_VBoxUSBFilterMgr_h
+#define VBOX_INCLUDED_SRC_VBoxUSB_VBoxUSBFilterMgr_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+#include <VBox/usbfilter.h>
+
+RT_C_DECLS_BEGIN
+
+/** @todo r=bird: VBOXUSBFILTER_CONTEXT isn't following the coding
+ * guildlines. Don't know which clueless dude did this... */
+#if defined(RT_OS_WINDOWS)
+typedef struct VBOXUSBFLTCTX *VBOXUSBFILTER_CONTEXT;
+#define VBOXUSBFILTER_CONTEXT_NIL NULL
+#else
+typedef RTPROCESS VBOXUSBFILTER_CONTEXT;
+#define VBOXUSBFILTER_CONTEXT_NIL NIL_RTPROCESS
+#endif
+
+int VBoxUSBFilterInit(void);
+void VBoxUSBFilterTerm(void);
+void VBoxUSBFilterRemoveOwner(VBOXUSBFILTER_CONTEXT Owner);
+int VBoxUSBFilterAdd(PCUSBFILTER pFilter, VBOXUSBFILTER_CONTEXT Owner, uintptr_t *puId);
+int VBoxUSBFilterRemove(VBOXUSBFILTER_CONTEXT Owner, uintptr_t uId);
+VBOXUSBFILTER_CONTEXT VBoxUSBFilterMatch(PCUSBFILTER pDevice, uintptr_t *puId);
+VBOXUSBFILTER_CONTEXT VBoxUSBFilterMatchEx(PCUSBFILTER pDevice, uintptr_t *puId, bool fRemoveFltIfOneShot, bool *pfFilter, bool *pfIsOneShot);
+VBOXUSBFILTER_CONTEXT VBoxUSBFilterGetOwner(uintptr_t uId);
+
+RT_C_DECLS_END
+
+#endif /* !VBOX_INCLUDED_SRC_VBoxUSB_VBoxUSBFilterMgr_h */
diff --git a/src/VBox/HostDrivers/VBoxUSB/os2/Makefile.kup b/src/VBox/HostDrivers/VBoxUSB/os2/Makefile.kup
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/src/VBox/HostDrivers/VBoxUSB/os2/Makefile.kup
diff --git a/src/VBox/HostDrivers/VBoxUSB/os2/usbcalls.c b/src/VBox/HostDrivers/VBoxUSB/os2/usbcalls.c
new file mode 100644
index 00000000..5950cc6f
--- /dev/null
+++ b/src/VBox/HostDrivers/VBoxUSB/os2/usbcalls.c
@@ -0,0 +1,1278 @@
+#define INCL_DOSERRORS
+#define INCL_DOSMEMMGR
+#define INCL_DOSSEMAPHORES
+#define INCL_DOSDEVICES
+#define INCL_DOSDEVIOCTL
+#define INCL_DOSMODULEMGR
+#include <os2.h>
+
+#if !defined(__GNUC__) || defined(STATIC_USBCALLS)
+#include <string.h>
+#else
+#define memcpy __builtin_memcpy
+#endif
+#include <stdlib.h>
+#include <stdio.h>
+
+#define LOG_GROUP LOG_GROUP_DRV_USBPROXY
+#include <VBox/log.h>
+
+#ifdef __GNUC__
+# define APIEXPORT __declspec(dllexport)
+#else
+# define APIEXPORT
+#endif
+
+#ifndef ERROR_USER_DEFINED_BASE
+/*#define ERROR_USER_DEFINED_BASE 0xFF00 */
+
+#define ERROR_I24_WRITE_PROTECT 0
+#define ERROR_I24_BAD_UNIT 1
+#define ERROR_I24_NOT_READY 2
+#define ERROR_I24_BAD_COMMAND 3
+#define ERROR_I24_CRC 4
+#define ERROR_I24_BAD_LENGTH 5
+#define ERROR_I24_SEEK 6
+#define ERROR_I24_NOT_DOS_DISK 7
+#define ERROR_I24_SECTOR_NOT_FOUND 8
+#define ERROR_I24_OUT_OF_PAPER 9
+#define ERROR_I24_WRITE_FAULT 10
+#define ERROR_I24_READ_FAULT 11
+#define ERROR_I24_GEN_FAILURE 12
+#define ERROR_I24_DISK_CHANGE 13
+#define ERROR_I24_WRONG_DISK 15
+#define ERROR_I24_UNCERTAIN_MEDIA 16
+#define ERROR_I24_CHAR_CALL_INTERRUPTED 17
+#define ERROR_I24_NO_MONITOR_SUPPORT 18
+#define ERROR_I24_INVALID_PARAMETER 19
+#define ERROR_I24_DEVICE_IN_USE 20
+#define ERROR_I24_QUIET_INIT_FAIL 21
+#endif
+
+#include "usbcalls.h"
+
+#define IOCAT_USBRES 0x000000A0 /* USB Resource device control */
+#define IOCTLF_NUMDEVICE 0x00000031 /* Get Number of plugged in Devices */
+#define IOCTLF_GETINFO 0x00000032 /* Get Info About a device */
+#define IOCTLF_AQUIREDEVICE 0x00000033
+#define IOCTLF_RELEASEDEVICE 0x00000034
+#define IOCTLF_GETSTRING 0x00000035
+#define IOCTLF_SENDCONTROLURB 0x00000036
+#define IOCTLF_SENDBULKURB 0x00000037 /* Send */
+#define IOCTLF_START_IRQ_PROC 0x00000038 /* Start IRQ polling in a buffer */
+#define IOCTLF_GETDEVINFO 0x00000039 /* Get information about device */
+#define IOCTLF_STOP_IRQ_PROC 0x0000003A /* Stop IRQ Polling */
+#define IOCTLF_START_ISO_PROC 0x0000003B /* Start ISO buffering in a Ringbuffer */
+#define IOCTLF_STOP_ISO_PROC 0x0000003C /* Stop ISO buffering */
+#define IOCTLF_CANCEL_IORB 0x0000003D /* Abort an IORB; */
+#define IOCTLF_SELECT_BULKPIPE 0x0000003E /* Select which Bulk endpoints can be used via Read/Write */
+#define IOCTLF_SENDIRQURB 0x0000003F /* Start IRQ polling in a buffer */
+#define IOCTLF_FIXUPDEVUCE 0x00000040 /* Fixup USB device configuration data */
+#define IOCTLF_REG_STATUSSEM 0x00000041 /* Register Semaphore for general Statuschange */
+#define IOCTLF_DEREG_STATUSSEM 0x00000042 /* Deregister Semaphore */
+#define IOCTLF_REG_DEVICESEM 0x00000043 /* Register Semaphore for a vendor&deviceID */
+#define IOCTLF_DEREG_DEVICESEM 0x00000044 /* Deregister Semaphore */
+
+
+#define NOTIFY_FREE 0
+#define NOTIFY_CHANGE 1
+#define NOTIFY_DEVICE 2
+#define MAX_NOTIFICATIONS 256
+
+#pragma pack(1)
+
+typedef struct
+{
+ HEV hDeviceAdded;
+ HEV hDeviceRemoved;
+ USHORT usFlags;
+ USHORT usVendor;
+ USHORT usProduct;
+ USHORT usBCDDevice;
+} NOTIFYENTRY, *PNOTIFYENTRY;
+
+#define DEV_SEM_ADD 0x00000001
+#define DEV_SEM_REMOVE 0x00000002
+#define DEV_SEM_MASK 0x00000003
+#define DEV_SEM_VENDORID 0x00000004
+#define DEV_SEM_PRODUCTID 0x00000008
+#define DEV_SEM_BCDDEVICE 0x00000010
+
+typedef struct{
+ ULONG ulSize;
+ ULONG ulCaps;
+ ULONG ulSemDeviceAdd;
+ ULONG ulSemDeviceRemove;
+} STATUSEVENTSET, * PSTATUSEVENTSET;
+
+
+typedef struct{
+ ULONG ulSize;
+ ULONG ulCaps;
+ ULONG ulSemDeviceAdd;
+ ULONG ulSemDeviceRemove;
+ USHORT usVendorID;
+ USHORT usProductID;
+ USHORT usBCDDevice;
+ USHORT usStatus;
+} DEVEVENTSET, * PDEVEVENTSET;
+
+typedef struct
+{
+ USHORT usVendorID;
+ USHORT usProductID;
+ USHORT usBCDDevice;
+ USHORT usDeviceNumber; /* Get the usDeviceNumber device in the system fi. if 2 acquire the 2nd device
+ 0 means first not acquired device. */
+} AQUIREDEV, *PAQUIREDEV;
+
+typedef struct
+{
+ UCHAR bRequestType;
+ UCHAR bRequest;
+ USHORT wValue;
+ USHORT wIndex;
+ USHORT wLength;
+ ULONG ulTimeout; /* in milliseconds */
+} SETUPPACKET, *PSETUPPACKET;
+
+typedef struct
+{
+ ULONG ulHandle;
+ UCHAR bRequestType;
+ UCHAR bRequest;
+ USHORT wValue;
+ USHORT wIndex;
+ USHORT wLength;
+ ULONG ulTimeout; /* in milliseconds */
+ USHORT usStatus;
+} USBCALLS_CTRL_REQ, *PUSBCALLS_CTRL_REQ;
+
+typedef struct
+{
+ ULONG ulDevHandle;
+ UCHAR ucEndpoint;
+ UCHAR ucAltInterface;
+ USHORT usStatus;
+ ULONG ulEvent;
+ ULONG ulID;
+} USBCALLS_ISO_START, *NPUSBCALLS_ISO_START, FAR *PUSBCALLS_ISO_START,
+ USBCALLS_IRQ_START, *NPUSBCALLS_IRQ_START, FAR *PUSBCALLS_IRQ_START,
+ USBCALLS_CANCEL_REQ, *NPUSBCALLS_CANCEL_REQ, FAR *PUSBCALLS_CANCEL_REQ;
+
+#define ISO_DIRMASK 0x80
+typedef struct
+{
+ ULONG hSemAccess; /* Synchronise access to the Pos values */
+ ULONG hDevice;
+ USHORT usPosWrite;
+ USHORT usPosRead;
+ USHORT usBufSize;
+ UCHAR ucEndpoint;
+ UCHAR ucAltInterface;
+ UCHAR ucBuffer[16*1023];
+} ISORINGBUFFER, *PISORINGBUFFER;
+
+typedef USBCALLS_ISO_START USBCALLS_ISO_STOP, * NPUSBCALLS_ISO_STOP, FAR *PUSBCALLS_ISO_STOP;
+typedef USBCALLS_ISO_START USBCALLS_IRQ_STOP, * NPUSBCALLS_IRQ_STOP, FAR *PUSBCALLS_IRQ_STOP;
+
+#define USB_TRANSFER_FULL_SIZE 0x01
+
+typedef struct
+{
+ ULONG ulDevHandle;
+ UCHAR ucEndpoint;
+ UCHAR ucAltInterface;
+ USHORT usStatus;
+ ULONG ulEvent;
+/* ULONG ulID; - yeah, right */
+ ULONG ulTimeout;
+ USHORT usDataProcessed;
+ USHORT usDataRemain;
+ USHORT usFlags;
+} USBCALLS_BULK_REQ, *PUSBCALLS_BULK_REQ;
+
+typedef struct
+{
+ ULONG ulDevHandle;
+ UCHAR ucEndpoint;
+ UCHAR ucAltInterface;
+ USHORT usStatus;
+ ULONG ulEvent;
+ ULONG ulID;
+ ULONG ulTimeout;
+ USHORT usDataLen;
+} LIBUSB_IRQ_REQ, *NPLIBUSB_IRQ_REQ, FAR *PLIBUSB_IRQ_REQ;
+
+typedef struct
+{
+ ULONG ulDevHandle;
+ UCHAR ucConfiguration;
+ UCHAR ucAltInterface;
+ USHORT usStatus;
+} LIBUSB_FIXUP, *NPLIBUSB_FIXUP, FAR *PLIBUSB_FIXUP;
+
+#pragma pack()
+
+/******************************************************************************/
+
+HFILE g_hUSBDrv;
+ULONG g_cInit;
+ULONG g_ulFreeNotifys;
+HMTX g_hSemNotifytable;
+NOTIFYENTRY g_Notifications[MAX_NOTIFICATIONS];
+
+HMTX g_hSemRingBuffers;
+PISORINGBUFFER g_pIsoRingBuffers;
+ULONG g_ulNumIsoRingBuffers;
+
+APIEXPORT APIRET APIENTRY
+InitUsbCalls(void)
+{
+ int i;
+ ULONG ulAction;
+ APIRET rc;
+
+ if (++g_cInit > 1)
+ return NO_ERROR;
+
+ rc = DosOpen( (PCSZ)"USBRESM$",
+ &g_hUSBDrv,
+ &ulAction,
+ 0,
+ FILE_NORMAL,
+ OPEN_ACTION_OPEN_IF_EXISTS,
+ OPEN_ACCESS_READWRITE |
+ OPEN_FLAGS_NOINHERIT |
+ OPEN_SHARE_DENYNONE,
+ 0 );
+ if(rc)
+ {
+ g_hUSBDrv = 0;
+ g_cInit = 0;
+
+ }
+ else
+ {
+ /* @@ToDO Add EnvVar or INI for dynamically setting the number */
+ g_ulNumIsoRingBuffers = 8;
+ for(i=0;i<MAX_NOTIFICATIONS;i++)
+ {
+ g_Notifications[i].usFlags = NOTIFY_FREE;
+ g_Notifications[i].hDeviceAdded = 0;
+ g_Notifications[i].hDeviceRemoved = 0;
+ g_Notifications[i].usVendor = 0;
+ g_Notifications[i].usProduct = 0;
+ g_Notifications[i].usBCDDevice = 0;
+ }
+ rc = DosAllocMem( (PPVOID)&g_pIsoRingBuffers,
+ g_ulNumIsoRingBuffers * sizeof(ISORINGBUFFER),
+ PAG_WRITE | PAG_COMMIT | OBJ_TILE);
+ if(!rc)
+ {
+ PISORINGBUFFER pIter = g_pIsoRingBuffers;
+ for(i=0;i< g_ulNumIsoRingBuffers;i++,pIter++)
+ {
+ pIter->hDevice = 0;
+ pIter->hSemAccess = 0; /* Synchronise access to the Pos values */
+ pIter->usPosWrite = 0;
+ pIter->usPosRead = 0;
+ pIter->usBufSize = 16*1023;
+ pIter->ucEndpoint = 0;
+ pIter->ucAltInterface = 0;
+ /*pIter->ucBuffer */
+ }
+ rc=DosCreateMutexSem(NULL,&g_hSemRingBuffers,DC_SEM_SHARED,FALSE);
+ if(!rc)
+ {
+ rc=DosCreateMutexSem(NULL,&g_hSemNotifytable,DC_SEM_SHARED,FALSE);
+ if(rc)
+ {
+ DosCloseMutexSem(g_hSemRingBuffers);
+ DosFreeMem(g_pIsoRingBuffers);
+ }
+ }
+ else
+ {
+ DosFreeMem(g_pIsoRingBuffers);
+ }
+ }
+
+ if(rc)
+ {
+ DosClose(g_hUSBDrv);
+ g_hUSBDrv = 0;
+ g_cInit = 0;
+ }
+ }
+ return g_cInit ? NO_ERROR : rc ? rc : ERROR_GEN_FAILURE;
+}
+
+APIEXPORT APIRET APIENTRY
+TermUsbCalls(void)
+{
+ if (!g_cInit)
+ return ERROR_GEN_FAILURE;
+ if(!--g_cInit)
+ {
+ int i;
+ for(i=0;i<MAX_NOTIFICATIONS;i++)
+ if( g_Notifications[i].usFlags != NOTIFY_FREE);
+ UsbDeregisterNotification((USBNOTIFY)(&g_Notifications[i]));
+
+ DosClose(g_hUSBDrv);
+ g_hUSBDrv = NULLHANDLE;
+
+ if (g_pIsoRingBuffers)
+ DosFreeMem(g_pIsoRingBuffers);
+
+ DosCloseMutexSem(g_hSemRingBuffers);
+ g_hSemRingBuffers = NULLHANDLE;
+ DosCloseMutexSem(g_hSemNotifytable);
+ g_hSemNotifytable = NULLHANDLE;
+ }
+ return NO_ERROR;
+}
+
+
+#ifdef VBOX /* complete wast of time */
+# define IsBadReadPointer(pBase, ulSize) (FALSE)
+# define IsBadWritePointer(pBase, ulSize) (FALSE)
+#else
+static BOOL IsBadReadPointer(PVOID pBase, ULONG ulSize)
+{
+ APIRET rc;
+ ULONG ulFlags;
+ rc = DosQueryMem(pBase, &ulSize, &ulFlags);
+
+ return rc!=0?TRUE:(ulFlags&PAG_READ)&&(ulFlags&PAG_COMMIT)?FALSE:TRUE;
+}
+
+static BOOL IsBadWritePointer(PVOID pBase, ULONG ulSize)
+{
+ APIRET rc;
+ ULONG ulFlags;
+ rc = DosQueryMem(pBase, &ulSize, &ulFlags);
+
+ return rc!=0?TRUE:((ulFlags&PAG_WRITE)==PAG_WRITE&&(ulFlags&PAG_COMMIT)==PAG_COMMIT)?FALSE:TRUE;
+}
+#endif
+
+APIEXPORT APIRET APIENTRY
+UsbQueryNumberDevices(ULONG *pulNumDev)
+{
+ APIRET rc;
+ ULONG ulLength;
+ if(!g_cInit)
+ return USB_NOT_INIT;
+
+ if( IsBadWritePointer(pulNumDev,sizeof(ULONG)) )
+ return ERROR_INVALID_PARAMETER;
+ ulLength=sizeof(ULONG);
+ *pulNumDev = 0;
+ rc = DosDevIOCtl( g_hUSBDrv,
+ IOCAT_USBRES, IOCTLF_NUMDEVICE,
+ NULL, 0, NULL,
+ pulNumDev, ulLength, &ulLength);
+ return rc;
+}
+
+APIEXPORT APIRET APIENTRY
+UsbQueryDeviceReport(ULONG ulDevNumber, ULONG *pulBufLen, PVOID pData)
+{
+ APIRET rc;
+ ULONG ulParmLen;
+
+ if(!g_cInit)
+ return USB_NOT_INIT;
+
+ if( IsBadWritePointer(pulBufLen, sizeof(ULONG)) )
+ return ERROR_INVALID_PARAMETER;
+
+ if( pData!=NULL && IsBadWritePointer(pData,*pulBufLen) )
+ return ERROR_INVALID_PARAMETER;
+ if(pData==NULL)
+ *pulBufLen = 0;
+ ulParmLen = sizeof(ulDevNumber);
+ rc = DosDevIOCtl( g_hUSBDrv,
+ IOCAT_USBRES, IOCTLF_GETINFO,
+ (PVOID)&ulDevNumber, ulParmLen, &ulParmLen,
+ pData, *pulBufLen, pulBufLen);
+ return rc;
+}
+
+APIEXPORT APIRET APIENTRY
+UsbRegisterChangeNotification( PUSBNOTIFY pNotifyID,
+ HEV hDeviceAdded,
+ HEV hDeviceRemoved)
+{
+ APIRET rc;
+ int i;
+ STATUSEVENTSET EventSet;
+ ULONG ulSize;
+
+ if(!g_cInit)
+ return USB_NOT_INIT;
+
+ if( IsBadWritePointer(pNotifyID, sizeof(ULONG)) ||
+ (hDeviceAdded==0 && hDeviceRemoved==0) )
+ return ERROR_INVALID_PARAMETER;
+
+ ulSize = sizeof(EventSet);
+ EventSet.ulSize = ulSize;
+ EventSet.ulCaps = 0;
+
+ if(hDeviceAdded!=0)
+ {
+ ULONG ulCnt;
+ rc = DosQueryEventSem(hDeviceAdded,&ulCnt);
+ if(rc)
+ return rc;
+ EventSet.ulCaps |= DEV_SEM_ADD;
+ EventSet.ulSemDeviceAdd = hDeviceAdded;
+ }
+
+ if(hDeviceRemoved!=0)
+ {
+ ULONG ulCnt;
+ rc = DosQueryEventSem(hDeviceRemoved,&ulCnt);
+ if(rc)
+ return rc;
+ EventSet.ulCaps |= DEV_SEM_REMOVE;
+ EventSet.ulSemDeviceRemove = hDeviceRemoved;
+ }
+
+ rc = DosRequestMutexSem(g_hSemNotifytable,SEM_INDEFINITE_WAIT);
+ if(rc)
+ return rc;
+
+ for(i=0;i<MAX_NOTIFICATIONS;i++)
+ {
+ if( g_Notifications[i].usFlags == NOTIFY_FREE)
+ {
+ g_Notifications[i].usFlags = NOTIFY_CHANGE;
+ g_Notifications[i].hDeviceAdded = hDeviceAdded;
+ g_Notifications[i].hDeviceRemoved = hDeviceRemoved;
+ g_Notifications[i].usVendor = 0;
+ g_Notifications[i].usProduct = 0;
+ g_Notifications[i].usBCDDevice = 0;
+ break;
+ }
+ }
+ DosReleaseMutexSem(g_hSemNotifytable);
+ if(i==MAX_NOTIFICATIONS)
+ return USB_ERROR_NO_MORE_NOTIFICATIONS;
+
+ /* @@ToDo come up with a better way to generate IDs */
+ *pNotifyID = (USBNOTIFY) (&g_Notifications[i]);
+ rc = DosDevIOCtl( g_hUSBDrv,
+ IOCAT_USBRES, IOCTLF_REG_STATUSSEM,
+ NULL, 0, NULL,
+ &EventSet,ulSize, &ulSize);
+ if(rc)
+ {
+ g_Notifications[i].usFlags = NOTIFY_FREE;
+ *pNotifyID = 0;
+ }
+ return rc;
+}
+
+APIEXPORT APIRET APIENTRY
+UsbRegisterDeviceNotification( PUSBNOTIFY pNotifyID,
+ HEV hDeviceAdded,
+ HEV hDeviceRemoved,
+ USHORT usVendor,
+ USHORT usProduct,
+ USHORT usBCDVersion)
+{
+ DEVEVENTSET EventSet;
+ ULONG ulCnt,ulSize;
+ int i;
+ APIRET rc;
+
+ if(!g_cInit)
+ return USB_NOT_INIT;
+
+ if( IsBadWritePointer(pNotifyID, sizeof(ULONG)) ||
+ hDeviceAdded==0 || hDeviceRemoved==0 ||
+ usVendor == 0 || usVendor == 0xFFFF ||
+ usProduct == 0 || usProduct == 0xFFFF )
+ return ERROR_INVALID_PARAMETER;
+
+
+ rc = DosQueryEventSem(hDeviceAdded,&ulCnt);
+ if(rc)
+ return rc;
+ rc = DosQueryEventSem(hDeviceRemoved,&ulCnt);
+ if(rc)
+ return rc;
+
+ ulSize = sizeof(EventSet);
+ EventSet.ulSize = ulSize;
+ EventSet.ulCaps = DEV_SEM_ADD | DEV_SEM_REMOVE |
+ DEV_SEM_VENDORID | DEV_SEM_PRODUCTID |
+ DEV_SEM_BCDDEVICE ;
+ EventSet.ulSemDeviceAdd = hDeviceAdded;
+ EventSet.ulSemDeviceRemove = hDeviceRemoved;
+ EventSet.usVendorID = usVendor;
+ EventSet.usProductID = usProduct;
+ EventSet.usBCDDevice = usBCDVersion;
+ EventSet.usStatus = 0;
+
+ rc = DosRequestMutexSem(g_hSemNotifytable,SEM_INDEFINITE_WAIT);
+
+ if(rc)
+ return rc;
+
+ for(i=0;i<MAX_NOTIFICATIONS;i++)
+ {
+ if( g_Notifications[i].usFlags == NOTIFY_FREE)
+ {
+ g_Notifications[i].usFlags = NOTIFY_DEVICE;
+ g_Notifications[i].hDeviceAdded = hDeviceAdded;
+ g_Notifications[i].hDeviceRemoved = hDeviceRemoved;
+ g_Notifications[i].usVendor = usVendor;
+ g_Notifications[i].usProduct = usProduct;
+ g_Notifications[i].usBCDDevice = usBCDVersion;
+ break;
+ }
+ }
+ DosReleaseMutexSem(g_hSemNotifytable);
+ if(i==MAX_NOTIFICATIONS)
+ return USB_ERROR_NO_MORE_NOTIFICATIONS;
+
+ /* @@ToDo come up with a better way to generate IDs */
+ *pNotifyID = (USBNOTIFY) (&g_Notifications[i]);
+ rc = DosDevIOCtl( g_hUSBDrv,
+ IOCAT_USBRES, IOCTLF_REG_DEVICESEM,
+ NULL, 0, NULL,
+ &EventSet,ulSize, &ulSize);
+ if(rc)
+ {
+ if( rc == (ERROR_USER_DEFINED_BASE|ERROR_I24_INVALID_PARAMETER) )
+ rc= ERROR_INVALID_PARAMETER;
+ if( rc == (ERROR_USER_DEFINED_BASE|ERROR_I24_GEN_FAILURE) )
+ rc= EventSet.usStatus;
+
+ g_Notifications[i].usFlags = NOTIFY_FREE;
+ *pNotifyID = 0;
+ }
+ return rc;
+}
+
+APIEXPORT APIRET APIENTRY
+UsbDeregisterNotification( USBNOTIFY NotifyID )
+{
+ DEVEVENTSET EventSet;
+ USBNOTIFY MinID,MaxID;
+ ULONG Index, ulFunction, ulSize;
+ APIRET rc;
+
+ if(!g_cInit)
+ return USB_NOT_INIT;
+
+ MinID = (USBNOTIFY) (&g_Notifications[0]);
+ MaxID = (USBNOTIFY) (&g_Notifications[MAX_NOTIFICATIONS-1]);
+
+ if(NotifyID<MinID || NotifyID>MaxID)
+ return ERROR_INVALID_PARAMETER;
+
+ Index = NotifyID - MinID;
+
+ if(Index % sizeof(NOTIFYENTRY))
+ return ERROR_INVALID_PARAMETER;
+
+ Index /= sizeof(NOTIFYENTRY);
+
+ rc = DosRequestMutexSem(g_hSemNotifytable,SEM_INDEFINITE_WAIT);
+
+ switch(g_Notifications[Index].usFlags)
+ {
+ case NOTIFY_FREE:
+ DosReleaseMutexSem(g_hSemNotifytable);
+ return ERROR_INVALID_PARAMETER;
+ case NOTIFY_CHANGE:
+ ulFunction = IOCTLF_DEREG_STATUSSEM;
+ ulSize = sizeof(STATUSEVENTSET);
+ EventSet.ulSize = ulSize;
+ EventSet.ulCaps = DEV_SEM_ADD | DEV_SEM_REMOVE;
+ EventSet.ulSemDeviceAdd = g_Notifications[Index].hDeviceAdded;
+ EventSet.ulSemDeviceRemove = g_Notifications[Index].hDeviceRemoved;
+ break;
+ case NOTIFY_DEVICE:
+ ulFunction = IOCTLF_DEREG_DEVICESEM;
+ ulSize = sizeof(DEVEVENTSET);
+ EventSet.ulSize = ulSize;
+ EventSet.ulCaps = DEV_SEM_ADD | DEV_SEM_REMOVE |
+ DEV_SEM_VENDORID | DEV_SEM_PRODUCTID |
+ DEV_SEM_BCDDEVICE ;
+ EventSet.ulSemDeviceAdd = g_Notifications[Index].hDeviceAdded;
+ EventSet.ulSemDeviceRemove = g_Notifications[Index].hDeviceRemoved;
+ EventSet.usVendorID = g_Notifications[Index].usVendor;
+ EventSet.usProductID = g_Notifications[Index].usProduct;
+ EventSet.usBCDDevice = g_Notifications[Index].usBCDDevice;
+ EventSet.usStatus = 0;
+ break;
+ default:
+ DosReleaseMutexSem(g_hSemNotifytable);
+ return ERROR_GEN_FAILURE;
+ }
+
+ rc = DosDevIOCtl( g_hUSBDrv,
+ IOCAT_USBRES, ulFunction,
+ NULL, 0, NULL,
+ &EventSet,ulSize, &ulSize);
+ if(0==rc)
+ {
+ g_Notifications[Index].usFlags = NOTIFY_FREE;
+ g_Notifications[Index].hDeviceAdded = 0;
+ g_Notifications[Index].hDeviceRemoved = 0;
+ g_Notifications[Index].usVendor = 0;
+ g_Notifications[Index].usProduct = 0;
+ g_Notifications[Index].usBCDDevice = 0;
+ } else
+ {
+ if( rc == (ERROR_USER_DEFINED_BASE|ERROR_I24_INVALID_PARAMETER) )
+ rc= ERROR_INVALID_PARAMETER;
+ if( rc == (ERROR_USER_DEFINED_BASE|ERROR_I24_GEN_FAILURE) )
+ rc= EventSet.usStatus;
+ }
+ DosReleaseMutexSem(g_hSemNotifytable);
+
+ return rc;
+}
+
+APIEXPORT APIRET APIENTRY
+UsbOpen( PUSBHANDLE pHandle,
+ USHORT usVendor,
+ USHORT usProduct,
+ USHORT usBCDDevice,
+ USHORT usEnumDevice)
+{
+ ULONG ulCat, ulFunc;
+ ULONG ulParmLen, ulDataLen;
+ AQUIREDEV Aquire;
+ APIRET rc;
+
+ if(!g_cInit)
+ return USB_NOT_INIT;
+ if(IsBadWritePointer(pHandle,sizeof(USBHANDLE)) )
+ return ERROR_INVALID_PARAMETER;
+
+ Aquire.usVendorID = usVendor;
+ Aquire.usProductID = usProduct;
+ Aquire.usBCDDevice = usBCDDevice;
+ Aquire.usDeviceNumber = usEnumDevice;
+ ulCat = 0xA0;
+ ulFunc = 0x33;
+ ulParmLen = sizeof(Aquire);
+ ulDataLen = sizeof(USBHANDLE);
+ rc = DosDevIOCtl( g_hUSBDrv,
+ ulCat,ulFunc, /*IOCAT_USBRES, IOCTLF_AQUIREDEVICE, */
+ &Aquire, ulParmLen, &ulParmLen,
+ pHandle, ulDataLen, &ulDataLen);
+
+ /* @@ ToDO maybe gether some info about device here (endpoints etc for safety checks) */
+ return rc;
+
+}
+
+APIEXPORT APIRET APIENTRY
+UsbClose( USBHANDLE Handle)
+{
+ APIRET rc;
+ ULONG ulDataLen,ulParmLen;
+ if(!g_cInit)
+ return USB_NOT_INIT;
+
+ ulParmLen = sizeof(USBHANDLE);
+ ulDataLen = 0;
+
+ rc = DosDevIOCtl( g_hUSBDrv,
+ IOCAT_USBRES, IOCTLF_RELEASEDEVICE,
+ (PVOID)&Handle, ulParmLen, &ulParmLen,
+ NULL, ulDataLen, &ulDataLen);
+ return rc;
+}
+
+APIEXPORT APIRET APIENTRY
+UsbCtrlMessage( USBHANDLE Handle,
+ UCHAR ucRequestType,
+ UCHAR ucRequest,
+ USHORT usValue,
+ USHORT usIndex,
+ USHORT usLength,
+ PVOID pData,
+ ULONG ulTimeout)
+{
+ APIRET rc;
+ USBCALLS_CTRL_REQ CtrlRequest;
+ ULONG ulParmLen, ulDataLen;
+
+ if(!g_cInit)
+ return USB_NOT_INIT;
+
+ ulParmLen = sizeof(USBCALLS_CTRL_REQ);
+ CtrlRequest.ulHandle = Handle;
+ CtrlRequest.bRequestType = ucRequestType;
+ CtrlRequest.bRequest = ucRequest;
+ CtrlRequest.wValue = usValue;
+ CtrlRequest.wIndex = usIndex;
+ CtrlRequest.wLength = usLength;
+ CtrlRequest.ulTimeout = ulTimeout;
+ ulDataLen = usLength;
+
+ rc = DosDevIOCtl( g_hUSBDrv,
+ IOCAT_USBRES, IOCTLF_SENDCONTROLURB,
+ (PVOID)&CtrlRequest, ulParmLen, &ulParmLen,
+ ulDataLen>0?(PVOID)pData:NULL,
+ ulDataLen,
+ ulDataLen>0?&ulDataLen:NULL);
+ if( rc != NO_ERROR )
+ {
+ if( rc == (ERROR_USER_DEFINED_BASE|ERROR_I24_INVALID_PARAMETER) )
+ rc= ERROR_INVALID_PARAMETER;
+ if( rc == (ERROR_USER_DEFINED_BASE|ERROR_I24_GEN_FAILURE) )
+ rc= CtrlRequest.usStatus;
+ }
+ return rc;
+}
+
+APIEXPORT APIRET APIENTRY
+UsbBulkRead( USBHANDLE Handle,
+ UCHAR Endpoint,
+ UCHAR AltInterface,
+ ULONG *ulNumBytes,
+ PVOID pvData,
+ ULONG ulTimeout)
+{
+ return UsbBulkRead2(Handle, Endpoint, AltInterface, TRUE /* fShortOk */, ulNumBytes, pvData, ulTimeout);
+}
+
+APIEXPORT APIRET APIENTRY
+UsbBulkRead2( USBHANDLE Handle,
+ UCHAR Endpoint,
+ UCHAR AltInterface,
+ BOOL fShortOk,
+ ULONG *ulNumBytes,
+ PVOID pvData,
+ ULONG ulTimeout)
+{
+ APIRET rc;
+ ULONG ulParmLen, ulDataLen, ulToProcess, ulTotalProcessed;
+ USBCALLS_BULK_REQ BulkRequest;
+
+ if(!g_cInit)
+ return USB_NOT_INIT;
+
+ if(*ulNumBytes==0)
+ return 0;
+
+ /* just require this */
+ if ((ULONG)pvData & 0xfff)
+ return ERROR_INVALID_ADDRESS;
+ if ((ULONG)pvData >= 0x20000000)
+ return ERROR_INVALID_ADDRESS;
+
+ ulToProcess = *ulNumBytes;
+ ulTotalProcessed = 0;
+
+ do
+ {
+ /* Process up to 64k, making sure we're working on segments. */
+ ulDataLen = 0x10000 - ((ULONG)pvData & 0xffff);
+ if (ulDataLen > ulToProcess)
+ ulDataLen = ulToProcess;
+
+ ulParmLen = sizeof(USBCALLS_BULK_REQ);
+
+ memset(&BulkRequest, 0, sizeof(BulkRequest));
+ BulkRequest.ulDevHandle = Handle;
+ BulkRequest.ucEndpoint = Endpoint;
+ BulkRequest.ucAltInterface = AltInterface;
+ BulkRequest.usStatus = 0;
+ BulkRequest.ulEvent = 0;
+ //BulkRequest.ulID = (ULONG)pvData;
+ BulkRequest.ulTimeout = ulTimeout;
+ BulkRequest.usDataProcessed = 0;
+ BulkRequest.usDataRemain = ulDataLen;
+ BulkRequest.usFlags = fShortOk && ulDataLen == ulToProcess ? 0 : USB_TRANSFER_FULL_SIZE;
+
+ rc = DosDevIOCtl( g_hUSBDrv,
+ IOCAT_USBRES, IOCTLF_SENDBULKURB,
+ (PVOID)&BulkRequest, ulParmLen, &ulParmLen,
+ pvData, ulDataLen, &ulDataLen);
+ Log(("BulkRead: usStatus=%d rc=%ld usDataProcessed=%d usDataRemain=%d ulDataLen=%ld\n",
+ BulkRequest.usStatus, rc, BulkRequest.usDataProcessed, BulkRequest.usDataRemain, ulDataLen));
+
+ if (rc != NO_ERROR)
+ {
+ if( rc == (ERROR_USER_DEFINED_BASE|ERROR_I24_INVALID_PARAMETER) )
+ rc= ERROR_INVALID_PARAMETER;
+ if( rc == (ERROR_USER_DEFINED_BASE|ERROR_I24_GEN_FAILURE) )
+ rc= BulkRequest.usStatus;
+ break;
+ }
+
+ /* Adjust count and source pointer */
+ ulToProcess -= ulDataLen;
+ pvData = (PBYTE)pvData + ulDataLen;
+ ulTotalProcessed += BulkRequest.usDataProcessed;
+
+ if (BulkRequest.usDataProcessed != ulDataLen)
+ {
+ /* Transferred less than we wanted? so something is wrong,
+ or device doesn't wish to send more, exit loop */
+ rc = USB_ERROR_LESSTRANSFERED;
+ break;
+ }
+ } while( ulToProcess>0 );
+
+ *ulNumBytes = ulTotalProcessed;
+
+ return rc;
+}
+
+APIEXPORT APIRET APIENTRY
+UsbBulkWrite( USBHANDLE Handle,
+ UCHAR Endpoint,
+ UCHAR AltInterface,
+ ULONG ulNumBytes,
+ PVOID pvData,
+ ULONG ulTimeout)
+{
+ return UsbBulkWrite2(Handle, Endpoint, AltInterface, FALSE /* fShortOk */, ulNumBytes, pvData, ulTimeout);
+}
+
+APIEXPORT APIRET APIENTRY
+UsbBulkWrite2( USBHANDLE Handle,
+ UCHAR Endpoint,
+ UCHAR AltInterface,
+ BOOL fShortOk,
+ ULONG ulNumBytes,
+ PVOID pvData,
+ ULONG ulTimeout)
+{
+ APIRET rc;
+ ULONG ulParmLen, ulDataLen;
+ USBCALLS_BULK_REQ BulkRequest;
+
+ if(!g_cInit)
+ return USB_NOT_INIT;
+
+ /* just require this */
+ if ((ULONG)pvData & 0xfff)
+ return ERROR_INVALID_ADDRESS;
+ if ((ULONG)pvData >= 0x20000000)
+ return ERROR_INVALID_ADDRESS;
+
+ do
+ {
+ /* Process up to 64k, making sure we're working on segments. */
+ ulDataLen = 0x10000 - ((ULONG)pvData & 0xffff);
+ if (ulDataLen > ulNumBytes)
+ ulDataLen = ulNumBytes;
+
+ ulParmLen = sizeof(USBCALLS_BULK_REQ);
+
+ memset(&BulkRequest, 0, sizeof(BulkRequest));
+ BulkRequest.ulDevHandle = Handle;
+ BulkRequest.ucEndpoint = Endpoint;
+ BulkRequest.ucAltInterface = AltInterface;
+ BulkRequest.usStatus = 0;
+ BulkRequest.ulEvent = 0;
+ //BulkRequest.ulID = (ULONG)pvData;
+ BulkRequest.ulTimeout = ulTimeout;
+ BulkRequest.usDataProcessed = 0;
+ BulkRequest.usDataRemain = ulDataLen;
+ BulkRequest.usFlags = fShortOk && ulDataLen == ulNumBytes ? 0 : USB_TRANSFER_FULL_SIZE;
+
+ rc = DosDevIOCtl( g_hUSBDrv,
+ IOCAT_USBRES, IOCTLF_SENDBULKURB,
+ &BulkRequest, ulParmLen, &ulParmLen,
+ pvData, ulDataLen, &ulDataLen );
+ Log(("BulkWrite: usStatus=%d rc=%ld usDataProcessed=%d usDataRemain=%d ulDataLen=%ld\n",
+ BulkRequest.usStatus, rc, BulkRequest.usDataProcessed, BulkRequest.usDataRemain, ulDataLen));
+ if (rc != NO_ERROR)
+ {
+ if( rc == (ERROR_USER_DEFINED_BASE|ERROR_I24_INVALID_PARAMETER) )
+ rc= ERROR_INVALID_PARAMETER;
+ if( rc == (ERROR_USER_DEFINED_BASE|ERROR_I24_GEN_FAILURE) )
+ rc= BulkRequest.usStatus;
+ break;
+ }
+ /* Adjust count and source pointer */
+ ulNumBytes -= ulDataLen;
+ pvData = (PBYTE)pvData + ulDataLen;
+ } while( ulNumBytes > 0 );
+
+ return rc;
+}
+
+APIRET APIENTRY
+UsbIrqStart( USBHANDLE Handle,
+ UCHAR Endpoint,
+ UCHAR AltInterface,
+ USHORT ulNumBytes,
+ PVOID pData,
+ PHEV pHevModified)
+{
+ APIRET rc;
+ ULONG ulParmLen, ulDataLen;
+ USBCALLS_IRQ_START IrqStart;
+ HEV hEvent;
+
+ if(!g_cInit)
+ return USB_NOT_INIT;
+
+ if(0==ulNumBytes || IsBadWritePointer(pData, ulNumBytes))
+ return ERROR_INVALID_PARAMETER;
+
+ rc = DosCreateEventSem( NULL,
+ &hEvent,
+ DC_SEM_SHARED,
+ FALSE);
+ if(rc)
+ return rc;
+
+ IrqStart.ulDevHandle = Handle;
+ IrqStart.ucEndpoint = Endpoint;
+ IrqStart.ucAltInterface = AltInterface;
+ IrqStart.usStatus = 0;
+ IrqStart.ulEvent = hEvent;
+ ulParmLen = sizeof(IrqStart);
+ ulDataLen = ulNumBytes;
+
+ rc = DosDevIOCtl( g_hUSBDrv,
+ IOCAT_USBRES, IOCTLF_START_IRQ_PROC,
+ (PVOID)&IrqStart, ulParmLen, &ulParmLen,
+ pData, ulDataLen,&ulDataLen);
+ if(rc)
+ DosCloseEventSem(hEvent);
+ else
+ *pHevModified = hEvent;
+ return rc;
+}
+
+APIEXPORT APIRET APIENTRY
+UsbIrqStop( USBHANDLE Handle,
+ HEV HevModified)
+{
+ APIRET rc;
+ ULONG ulParmLen, ulDataLen;
+
+ if(!g_cInit)
+ return USB_NOT_INIT;
+
+ ulParmLen = sizeof(Handle);
+ ulDataLen = sizeof(HevModified);
+ rc = DosDevIOCtl( g_hUSBDrv,
+ IOCAT_USBRES, IOCTLF_STOP_IRQ_PROC,
+ (PVOID)&Handle, ulParmLen, &ulParmLen,
+ &HevModified, ulDataLen, &ulDataLen);
+ if(!rc)
+ DosCloseEventSem(HevModified);
+
+ return rc;
+}
+
+APIEXPORT APIRET APIENTRY
+UsbIsoStart( USBHANDLE Handle,
+ UCHAR Endpoint,
+ UCHAR AltInterface,
+ ISOHANDLE *phIso)
+{
+ APIRET rc;
+ PISORINGBUFFER pIter = g_pIsoRingBuffers;
+ USBCALLS_ISO_START IsoStart;
+ ULONG ulParmLen, ulDataLen;
+ int i;
+
+ if(!g_cInit)
+ return USB_NOT_INIT;
+
+ rc = DosRequestMutexSem(g_hSemRingBuffers,SEM_INDEFINITE_WAIT);
+ if(rc)
+ return rc;
+
+ for(i=0;i< g_ulNumIsoRingBuffers;i++,pIter++)
+ {
+ if (pIter->hDevice==0)
+ {
+ pIter->hDevice = Handle;
+ break;
+ }
+ }
+ DosReleaseMutexSem(g_hSemRingBuffers);
+
+ if(i==g_ulNumIsoRingBuffers)
+ return USB_ERROR_OUTOF_RESOURCES;
+
+ IsoStart.ulDevHandle = Handle;
+ IsoStart.ucEndpoint = Endpoint;
+ IsoStart.ucAltInterface = AltInterface;
+ ulParmLen = sizeof(IsoStart);
+ ulDataLen = sizeof(ISORINGBUFFER);
+
+ rc = DosDevIOCtl( g_hUSBDrv,
+ IOCAT_USBRES, IOCTLF_STOP_IRQ_PROC,
+ (PVOID)&IsoStart, ulParmLen, &ulParmLen,
+ pIter, ulDataLen, &ulDataLen);
+ if(rc)
+ {
+ pIter->hDevice = 0;
+ *phIso = 0;
+ }
+ else
+ {
+ pIter->ucEndpoint = Endpoint;
+ pIter->ucAltInterface = AltInterface;
+ }
+ return rc;
+}
+
+static APIRET IsInvalidIsoHandle(const ISOHANDLE hIso)
+{
+ PISORINGBUFFER pIter;
+ ULONG i;
+ pIter = g_pIsoRingBuffers;
+
+ for(i=0;i<g_ulNumIsoRingBuffers;i++,pIter++)
+ {
+ if(pIter==(PISORINGBUFFER)hIso && pIter->hDevice)
+ return 0;
+ }
+ return ERROR_INVALID_PARAMETER;
+}
+
+APIEXPORT APIRET APIENTRY
+UsbIsoStop( ISOHANDLE hIso)
+{
+
+ APIRET rc = NO_ERROR;
+ if(!g_cInit)
+ return USB_NOT_INIT;
+
+/* rc = DosDevIOCtl( g_hUSBDrv, */
+ return rc;
+}
+
+APIEXPORT APIRET APIENTRY
+UsbIsoDequeue( ISOHANDLE hIso,
+ PVOID pBuffer,
+ ULONG ulNumBytes)
+{
+ APIRET rc;
+ PISORINGBUFFER pRB = (PISORINGBUFFER)hIso;
+
+ rc = IsInvalidIsoHandle(hIso);
+ if(rc)
+ return rc;
+ if(!(pRB->ucEndpoint & ISO_DIRMASK))
+ return ERROR_INVALID_PARAMETER;
+
+ return rc;
+}
+
+APIEXPORT APIRET APIENTRY
+UsbIsoPeekQueue( ISOHANDLE hIso,
+ UCHAR * pByte,
+ ULONG ulOffset)
+{
+ APIRET rc;
+ PISORINGBUFFER pRB = (PISORINGBUFFER)hIso;
+
+ rc = IsInvalidIsoHandle(hIso);
+ if(rc)
+ return rc;
+ if(!(pRB->ucEndpoint & ISO_DIRMASK))
+ return ERROR_INVALID_PARAMETER;
+ return rc;
+}
+
+APIEXPORT APIRET APIENTRY
+UsbIsoEnqueue( ISOHANDLE hIso,
+ const UCHAR * pBuffer,
+ ULONG ulNumBytes)
+{
+ APIRET rc;
+ PISORINGBUFFER pRB = (PISORINGBUFFER)hIso;
+
+ rc = IsInvalidIsoHandle(hIso);
+ if(rc)
+ return rc;
+ if(pRB->ucEndpoint & ISO_DIRMASK)
+ return ERROR_INVALID_PARAMETER;
+
+ return rc;
+}
+
+APIEXPORT APIRET APIENTRY
+UsbIsoGetLength( ISOHANDLE hIso,
+ ULONG *pulLength)
+{
+ APIRET rc;
+ PISORINGBUFFER pRB = (PISORINGBUFFER) hIso;
+ USHORT ri,wi;
+
+ rc = IsInvalidIsoHandle(hIso);
+ if(rc)
+ return rc;
+ wi = pRB->usPosWrite;
+ ri = pRB->usPosRead;
+
+ if (ri == wi)
+ *pulLength = 0;
+ else if (ri < wi)
+ *pulLength = wi - ri;
+ else
+ *pulLength = wi + (pRB->usBufSize - ri);
+
+ return 0;
+}
+
+APIEXPORT APIRET APIENTRY
+UsbIrqRead( USBHANDLE Handle,
+ UCHAR Endpoint,
+ UCHAR AltInterface,
+ ULONG *ulNumBytes,
+ PVOID pData,
+ ULONG ulTimeout)
+{
+ APIRET rc;
+ ULONG ulParmLen, ulDataLen;
+ LIBUSB_IRQ_REQ IrqRequest;
+
+ if(!g_cInit)
+ return USB_NOT_INIT;
+
+ /* 10 01 2003 - KIEWITZ -> Still @@ToDo Add Endpoint check based on descriptors
+ We currently only allow Endpoint-addresses 80h->8Fh here */
+ if ((Endpoint<0x80) || (Endpoint>0x8F))
+ return USB_ERROR_INVALID_ENDPOINT;
+
+ if(*ulNumBytes==0)
+ return 0;
+
+ IrqRequest.ulDevHandle = Handle;
+ IrqRequest.ucEndpoint = Endpoint;
+ IrqRequest.ucAltInterface = AltInterface;
+ IrqRequest.usStatus = 0;
+ IrqRequest.ulEvent = 0;
+ IrqRequest.ulTimeout = ulTimeout;
+ ulParmLen = sizeof(LIBUSB_IRQ_REQ);
+ ulDataLen = *ulNumBytes;
+
+ rc = DosDevIOCtl( g_hUSBDrv,
+ IOCAT_USBRES, IOCTLF_SENDIRQURB,
+ (PVOID)&IrqRequest, ulParmLen, &ulParmLen,
+ pData, ulDataLen, &ulDataLen);
+
+ if( rc == NO_ERROR )
+ {
+ *ulNumBytes = IrqRequest.usDataLen;
+ } else
+ {
+ if( rc == (ERROR_USER_DEFINED_BASE|ERROR_I24_INVALID_PARAMETER) )
+ rc= ERROR_INVALID_PARAMETER;
+ if( rc == (ERROR_USER_DEFINED_BASE|ERROR_I24_GEN_FAILURE) )
+ rc= IrqRequest.usStatus;
+ }
+ return rc;
+}
+
+
+APIEXPORT APIRET APIENTRY
+UsbFixupDevice( USBHANDLE Handle,
+ UCHAR ucConfiguration,
+ UCHAR *pucConfigurationData,
+ ULONG ulConfigurationLen )
+{
+ LIBUSB_FIXUP request;
+ ULONG ulParmLen;
+ APIRET rc;
+
+ request.ulDevHandle= Handle;
+ request.ucConfiguration= ucConfiguration;
+ request.usStatus= 0;
+ ulParmLen= sizeof(LIBUSB_FIXUP);
+ rc = DosDevIOCtl( g_hUSBDrv,
+ IOCAT_USBRES, IOCTLF_FIXUPDEVUCE,
+ (PVOID)&request, ulParmLen, &ulParmLen,
+ pucConfigurationData, ulConfigurationLen, &ulConfigurationLen);
+ if( rc != NO_ERROR )
+ {
+ if( rc == (ERROR_USER_DEFINED_BASE|ERROR_I24_INVALID_PARAMETER) )
+ rc= ERROR_INVALID_PARAMETER;
+ if( rc == (ERROR_USER_DEFINED_BASE|ERROR_I24_GEN_FAILURE) )
+ rc= request.usStatus;
+ }
+ return rc;
+}
+
+#ifndef STATIC_USBCALLS
+ /*+-------------------------------------------------------------------+*/
+ /*| _CRT_init is the C run-time environment initialization function. |*/
+ /*|It will return 0 to indicate success and -1 to indicate failure. |*/
+ /*+-------------------------------------------------------------------+*/
+
+/* int _CRT_init (void); */
+
+ /*+-------------------------------------------------------------------+*/
+ /*| _CRT_term is the C run-time environment termination function. |*/
+ /*+-------------------------------------------------------------------+*/
+
+/* void _CRT_term (unsigned long);*/
+
+ /*+-------------------------------------------------------------------+*/
+ /*| _DLL_InitTerm is the function that gets called by the operating |*/
+ /*| system loader when it loads and frees this DLL for each process |*/
+ /*| that accesses this DLL. However, it only gets called the first |*/
+ /*| time the DLL is loaded and the last time it is freed for a |*/
+ /*| particular process. The system linkage convention must be used |*/
+ /*| because the operating system loader is calling this function. |*/
+ /*+-------------------------------------------------------------------+*/
+
+#ifdef STATIC_LINK
+int _CRT_init (void);
+void _CRT_term(0UL);
+#endif
+
+unsigned long _System _DLL_InitTerm (unsigned long modhandle, unsigned long flag)
+{
+
+ /* If flag is zero then the DLL is being loaded so initialization */
+ /* should be performed. If flag is 1 then the DLL is being freed */
+ /* so termination should be performed. */
+
+ switch (flag)
+ {
+ case 0:
+ /* The C run-time environment initialization function must */
+ /* be called before any calls to C run-time functions that */
+ /* are not inlined. */
+
+#ifdef STATIC_LINK
+ if (_CRT_init () == -1)
+ return 0UL;
+#endif
+ InitUsbCalls();
+ break;
+
+ case 1:
+ TermUsbCalls();
+#ifdef STATIC_LINK
+ _CRT_term(0UL);
+#endif
+ break;
+
+ default:
+ return 0UL;
+
+ }
+
+ /* A nonzero value must be returned to indicate success. */
+ return 1UL;
+}
+#endif /* !STATIC_USBCALLS */
+
diff --git a/src/VBox/HostDrivers/VBoxUSB/os2/usbcalls.h b/src/VBox/HostDrivers/VBoxUSB/os2/usbcalls.h
new file mode 100644
index 00000000..151a5bc1
--- /dev/null
+++ b/src/VBox/HostDrivers/VBoxUSB/os2/usbcalls.h
@@ -0,0 +1,242 @@
+
+#ifndef VBOX_INCLUDED_SRC_VBoxUSB_os2_usbcalls_h
+#define VBOX_INCLUDED_SRC_VBoxUSB_os2_usbcalls_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+#ifdef __cplusplus
+ extern "C" {
+#endif
+
+typedef ULONG USBHANDLE, *PUSBHANDLE;
+typedef ULONG USBNOTIFY, *PUSBNOTIFY;
+typedef ULONG ISOHANDLE, *PISOHANDLE;
+
+#define USB_NOT_INIT 7000
+#define USB_ERROR_NO_MORE_NOTIFICATIONS 7001
+#define USB_ERROR_OUTOF_RESOURCES 7002
+#define USB_ERROR_INVALID_ENDPOINT 7003
+#define USB_ERROR_LESSTRANSFERED 7004
+
+/* IORB status codes */
+#define USB_IORB_DONE 0x0000
+#define USB_IORB_FAILED 0x8000
+
+#define USB_ANY_PRODUCTVERSION 0xFFFF
+#define USB_OPEN_FIRST_UNUSED 0
+
+#ifdef USB_BIND_DYNAMIC
+ typedef APIRET APIENTRY USBREGISTERDEVICENOTIFICATION( PUSBNOTIFY pNotifyID,
+ HEV hDeviceAdded,
+ HEV hDeviceRemoved,
+ USHORT usVendor,
+ USHORT usProduct,
+ USHORT usBCDVersion);
+ typedef USBREGISTERDEVICENOTIFICATION *PUSBREGISTERDEVICENOTIFICATION;
+
+ typedef APIRET APIENTRY USBDEREGISTERNOTIFICATION( USBNOTIFY NotifyID);
+
+ typedef USBDEREGISTERNOTIFICATION *PUSBDEREGISTERNOTIFICATION;
+
+ typedef APIRET APIENTRY USBOPEN( PUSBHANDLE pHandle,
+ USHORT usVendor,
+ USHORT usProduct,
+ USHORT usBCDDevice,
+ USHORT usEnumDevice);
+ typedef USBOPEN *PUSBOPEN;
+ typedef APIRET APIENTRY USBCLOSE( USBHANDLE Handle);
+ typedef USBCLOSE *PUSBCLOSE;
+
+ typedef APIRET APIENTRY USBCTRLMESSAGE( USBHANDLE Handle,
+ UCHAR ucRequestType,
+ UCHAR ucRequest,
+ USHORT usValue,
+ USHORT usIndex,
+ USHORT usLength,
+ PVOID pvData,
+ ULONG ulTimeout);
+ typedef USBCTRLMESSAGE *PUSBCTRLMESSAGE;
+#else
+ APIRET APIENTRY UsbQueryNumberDevices( ULONG *pulNumDev);
+
+ APIRET APIENTRY UsbQueryDeviceReport( ULONG ulDevNumber,
+ ULONG *ulBufLen,
+ PVOID pvData);
+ APIRET APIENTRY UsbRegisterChangeNotification( PUSBNOTIFY pNotifyID,
+ HEV hDeviceAdded,
+ HEV hDeviceRemoved);
+
+ APIRET APIENTRY UsbRegisterDeviceNotification( PUSBNOTIFY pNotifyID,
+ HEV hDeviceAdded,
+ HEV hDeviceRemoved,
+ USHORT usVendor,
+ USHORT usProduct,
+ USHORT usBCDVersion);
+
+ APIRET APIENTRY UsbDeregisterNotification( USBNOTIFY NotifyID);
+
+ APIRET APIENTRY UsbOpen( PUSBHANDLE pHandle,
+ USHORT usVendor,
+ USHORT usProduct,
+ USHORT usBCDDevice,
+ USHORT usEnumDevice);
+
+ APIRET APIENTRY UsbClose( USBHANDLE Handle);
+
+ APIRET APIENTRY UsbCtrlMessage( USBHANDLE Handle,
+ UCHAR ucRequestType,
+ UCHAR ucRequest,
+ USHORT usValue,
+ USHORT usIndex,
+ USHORT usLength,
+ PVOID pvData,
+ ULONG ulTimeout);
+
+ APIRET APIENTRY UsbBulkRead( USBHANDLE Handle,
+ UCHAR Endpoint,
+ UCHAR Interface,
+ ULONG *ulNumBytes,
+ PVOID pvData,
+ ULONG ulTimeout);
+
+ APIRET APIENTRY UsbBulkRead2( USBHANDLE Handle,
+ UCHAR Endpoint,
+ UCHAR Interface,
+ BOOL fShortOk,
+ ULONG *ulNumBytes,
+ PVOID pvData,
+ ULONG ulTimeout);
+
+ APIRET APIENTRY UsbBulkWrite( USBHANDLE Handle,
+ UCHAR Endpoint,
+ UCHAR Interface,
+ ULONG ulNumBytes,
+ PVOID pvData,
+ ULONG ulTimeout);
+
+ APIRET APIENTRY UsbBulkWrite2( USBHANDLE Handle,
+ UCHAR Endpoint,
+ UCHAR AltInterface,
+ BOOL fShortOk,
+ ULONG ulNumBytes,
+ PVOID pvData,
+ ULONG ulTimeout);
+
+ APIRET APIENTRY UsbIrqStart( USBHANDLE Handle,
+ UCHAR Endpoint,
+ UCHAR Interface,
+ USHORT usNumBytes,
+ PVOID pvData,
+ PHEV pHevModified);
+ APIRET APIENTRY UsbIrqStop( USBHANDLE Handle,
+ HEV HevModified);
+
+ APIRET APIENTRY UsbIsoStart( USBHANDLE Handle,
+ UCHAR Endpoint,
+ UCHAR Interface,
+ ISOHANDLE *phIso);
+ APIRET APIENTRY UsbIsoStop( ISOHANDLE hIso);
+
+ APIRET APIENTRY UsbIsoDequeue( ISOHANDLE hIso,
+ PVOID pBuffer,
+ ULONG ulNumBytes);
+ APIRET APIENTRY UsbIsoEnqueue( ISOHANDLE hIso,
+ const UCHAR * pBuffer,
+ ULONG ulNumBytes);
+ APIRET APIENTRY UsbIsoPeekQueue( ISOHANDLE hIso,
+ UCHAR * pByte,
+ ULONG ulOffset);
+ APIRET APIENTRY UsbIsoGetLength( ISOHANDLE hIso,
+ ULONG *pulLength);
+
+ APIRET APIENTRY UsbIrqRead( USBHANDLE Handle,
+ UCHAR Endpoint,
+ UCHAR Interface,
+ ULONG *ulNumBytes,
+ PVOID pvData,
+ ULONG ulTimeout);
+
+ APIRET APIENTRY UsbFixupDevice( USBHANDLE Handle,
+ UCHAR ucConfiguration,
+ UCHAR *pucConfigurationData,
+ ULONG ulConfigurationLen );
+
+ APIRET APIENTRY InitUsbCalls(void);
+ APIRET APIENTRY TermUsbCalls(void);
+
+ /* Standard USB Requests See 9.4. in USB 1.1. spec. */
+
+ /* 09 01 2003 - KIEWITZ */
+ #define UsbDeviceClearFeature(HANDLE, FEAT) \
+ UsbCtrlMessage(HANDLE, 0x00, 0x01, FEAT, 0, 0, NULL, 0)
+ #define UsbDeviceSetFeature(HANDLE, FEAT) \
+ UsbCtrlMessage(HANDLE, 0x80, 0x03, FEAT, 0, 0, NULL, 0)
+ #define UsbInterfaceClearFeature(HANDLE, IFACE, FEAT) \
+ UsbCtrlMessage(HANDLE, 0x01, 0x01, FEAT, IFACE, 0, NULL, 0)
+ #define UsbInterfaceSetFeature(HANDLE, IFACE, FEAT) \
+ UsbCtrlMessage(HANDLE, 0x80, 0x03, FEAT, IFACE, 0, NULL, 0)
+ #define UsbEndpointClearFeature(HANDLE, ENDPOINT, FEAT) \
+ UsbCtrlMessage(HANDLE, 0x02, 0x01, FEAT, ENDPOINT, 0, NULL, 0)
+ #define UsbEndpointSetFeature(HANDLE, ENDPOINT, FEAT) \
+ UsbCtrlMessage(HANDLE, 0x80, 0x03, FEAT, ENDPOINT, 0, NULL, 0)
+ #define FEATURE_DEVICE_REMOTE_WAKEUP 1
+ #define FEATURE_ENDPOINT_HALT 0
+ #define UsbEndpointClearHalt(HANDLE, ENDPOINT) \
+ UsbEndpointClearFeature(HANDLE, ENDPOINT, FEATURE_ENDPOINT_HALT)
+
+ #define UsbDeviceGetConfiguration(HANDLE, DATA) \
+ UsbCtrlMessage(HANDLE, 0x80, 0x08, 0, 0, 1, DATA, 0)
+ #define UsbDeviceSetConfiguration(HANDLE, CONFIG) \
+ UsbCtrlMessage(HANDLE, 0x00, 0x09, CONFIG, 0, 0, NULL, 0)
+
+ #define UsbDeviceGetStatus(HANDLE, STATUS) \
+ UsbCtrlMessage(HANDLE, 0x80, 0x00, 0, 0, 2, STATUS, 0)
+ #define UsbInterfaceGetStatus(HANDLE, IFACE, STATUS) \
+ UsbCtrlMessage(HANDLE, 0x80, 0x00, 0, IFACE, 2, STATUS, 0)
+ #define UsbEndpointGetStatus(HANDLE, ENDPOINT, STATUS) \
+ UsbCtrlMessage(HANDLE, 0x80, 0x00, 0, ENDPOINT, 2, STATUS, 0)
+
+ #define STATUS_ENDPOINT_HALT 0x0001
+ #define STATUS_DEVICE_SELFPOWERD 0x0001
+ #define STATUS_DEVICE_REMOTEWAKEUP 0x0002
+
+ #define UsbDeviceSetAddress(HANDLE, ADDRESS) \
+ UsbCtrlMessage(HANDLE, 0x80, 0x05, ADDRESS, 0, 0, NULL, 0)
+
+ #define UsbDeviceGetDescriptor(HANDLE, INDEX, LID, LEN, DATA) \
+ UsbCtrlMessage(HANDLE, 0x80, 0x06, (0x0100|INDEX), LID, LEN, DATA, 0)
+ #define UsbDeviceSetDescriptor(HANDLE, INDEX, LID, LEN, DATA) \
+ UsbCtrlMessage(HANDLE, 0x80, 0x07, (0x0100|INDEX), LID, LEN, DATA, 0)
+ #define UsbConfigurationGetDescriptor(HANDLE, INDEX, LID, LEN, DATA) \
+ UsbCtrlMessage(HANDLE, 0x80, 0x06, (0x0200|INDEX), LID, LEN, DATA, 0)
+ #define UsbConfigurationSetDescriptor(HANDLE, INDEX, LID, LEN, DATA) \
+ UsbCtrlMessage(HANDLE, 0x80, 0x07, (0x0200|INDEX), LID, LEN, DATA, 0)
+ #define UsbStringGetDescriptor(HANDLE, INDEX, LID, LEN, DATA) \
+ UsbCtrlMessage(HANDLE, 0x80, 0x06, (0x0300|INDEX), LID, LEN, DATA, 0)
+ #define UsbStringSetDescriptor(HANDLE, INDEX, LID, LEN, DATA) \
+ UsbCtrlMessage(HANDLE, 0x80, 0x07, (0x0300|INDEX), LID, LEN, DATA, 0)
+ #define UsbInterfaceGetDescriptor(HANDLE, INDEX, LID, LEN, DATA) \
+ UsbCtrlMessage(HANDLE, 0x80, 0x06, (0x0400|INDEX), LID, LEN, DATA, 0)
+ #define UsbInterfaceSetDescriptor(HANDLE, INDEX, LID, LEN, DATA) \
+ UsbCtrlMessage(HANDLE, 0x80, 0x07, (0x0400|INDEX), LID, LEN, DATA, 0)
+ #define UsbEndpointGetDescriptor(HANDLE, INDEX, LID, LEN, DATA) \
+ UsbCtrlMessage(HANDLE, 0x80, 0x06, (0x0500|INDEX), LID, LEN, DATA, 0)
+ #define UsbEndpointSetDescriptor(HANDLE, INDEX, LID, LEN, DATA) \
+ UsbCtrlMessage(HANDLE, 0x80, 0x07, (0x0500|INDEX), LID, LEN, DATA, 0)
+
+ #define UsbInterfaceGetAltSetting(HANDLE, IFACE, SETTING) \
+ UsbCtrlMessage(HANDLE, 0x81, 0x0A, 0, IFACE, 1, SETTING, 0)
+ #define UsbInterfaceSetAltSetting(HANDLE, IFACE, ALTSET) \
+ UsbCtrlMessage(HANDLE, 0x01, 0x0B, ALTSET, IFACE, 0, NULL, 0)
+
+ #define UsbEndpointSynchFrame(HANDLE, ENDPOINT, FRAMENUM) \
+ UsbCtrlMessage(HANDLE, 0x82, 0x0B, 0, ENDPOINT, 2, FRAMENUM, 0)
+#endif
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* !VBOX_INCLUDED_SRC_VBoxUSB_os2_usbcalls_h */
diff --git a/src/VBox/HostDrivers/VBoxUSB/solaris/Makefile.kmk b/src/VBox/HostDrivers/VBoxUSB/solaris/Makefile.kmk
new file mode 100644
index 00000000..2ba0f963
--- /dev/null
+++ b/src/VBox/HostDrivers/VBoxUSB/solaris/Makefile.kmk
@@ -0,0 +1,67 @@
+# $Id: Makefile.kmk $
+## @file
+# Sub-Makefile for the Solaris VBoxUSB kernel extension.
+#
+
+#
+# Copyright (C) 2008-2023 Oracle and/or its affiliates.
+#
+# This file is part of VirtualBox base platform packages, as
+# available from https://www.virtualbox.org.
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation, in version 3 of the
+# License.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, see <https://www.gnu.org/licenses>.
+#
+# The contents of this file may alternatively be used under the terms
+# of the Common Development and Distribution License Version 1.0
+# (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+# in the VirtualBox distribution, in which case the provisions of the
+# CDDL are applicable instead of those of the GPL.
+#
+# You may elect to license modified versions of this file under the
+# terms and conditions of either the GPL or the CDDL or both.
+#
+# SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+#
+
+SUB_DEPTH = ../../../../..
+include $(KBUILD_PATH)/subheader.kmk
+
+#
+# vboxusbmon - The Solaris USB Monitor Kernel module.
+#
+SYSMODS.solaris += vboxusbmon
+vboxusbmon_TEMPLATE = VBoxR0Drv
+vboxusbmon_DEFS = IN_RT_R0 VBOX_SVN_REV=$(VBOX_SVN_REV)
+vboxusbmon_DEPS += $(VBOX_SVN_REV_KMK)
+vboxusbmon_INCS := . .. include
+vboxusbmon_LDFLAGS += -N drv/vboxdrv -N misc/usba
+vboxusbmon_SOURCES = \
+ VBoxUSBMon-solaris.c \
+ ../USBFilter.cpp \
+ ../VBoxUSBFilterMgr.cpp
+
+#
+# vboxusb - The Solaris Generic USB Client Kernel module.
+#
+SYSMODS.solaris += vboxusb
+vboxusb_TEMPLATE = VBoxR0Drv
+vboxusb_DEFS = IN_RT_R0 IN_SUP_R0 VBOX_SVN_REV=$(VBOX_SVN_REV)
+vboxusb_DEPS += $(VBOX_SVN_REV_KMK)
+vboxusb_INCS := . include/
+vboxusb_LDFLAGS += -N drv/vboxdrv -N drv/vboxusbmon -N misc/usba
+vboxusb_SOURCES = \
+ VBoxUSB-solaris.c
+
+include $(FILE_KBUILD_SUB_FOOTER)
+
diff --git a/src/VBox/HostDrivers/VBoxUSB/solaris/USBLib-solaris.cpp b/src/VBox/HostDrivers/VBoxUSB/solaris/USBLib-solaris.cpp
new file mode 100644
index 00000000..10eaf026
--- /dev/null
+++ b/src/VBox/HostDrivers/VBoxUSB/solaris/USBLib-solaris.cpp
@@ -0,0 +1,279 @@
+/** $Id: USBLib-solaris.cpp $ */
+/** @file
+ * USBLib - Library for wrapping up the VBoxUSB functionality, Solaris flavor.
+ */
+
+/*
+ * Copyright (C) 2008-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include <VBox/usblib.h>
+#include <VBox/err.h>
+#include <VBox/log.h>
+#include <iprt/assert.h>
+#include <iprt/asm.h>
+#include <iprt/file.h>
+#include <iprt/mem.h>
+#include <iprt/process.h>
+#include <iprt/env.h>
+#include <iprt/path.h>
+#include <iprt/string.h>
+
+# include <sys/types.h>
+# include <sys/stat.h>
+# include <errno.h>
+# include <unistd.h>
+# include <string.h>
+# include <limits.h>
+# include <strings.h>
+
+
+/*********************************************************************************************************************************
+* Defined Constants And Macros *
+*********************************************************************************************************************************/
+/** Logging class. */
+#define USBLIBR3 "USBLibR3"
+
+
+/*********************************************************************************************************************************
+* Global Variables *
+*********************************************************************************************************************************/
+/** Reference counter. */
+static uint32_t volatile g_cUsers = 0;
+/** VBoxUSB Device handle. */
+static RTFILE g_hFile = NIL_RTFILE;
+
+
+/*********************************************************************************************************************************
+* Internal Functions *
+*********************************************************************************************************************************/
+static int usblibDoIOCtl(unsigned iFunction, void *pvData, size_t cbData);
+
+
+USBLIB_DECL(int) USBLibInit(void)
+{
+ LogFlow((USBLIBR3 ":USBLibInit\n"));
+
+ /*
+ * Already open?
+ * This isn't properly serialized, but we'll be fine with the current usage.
+ */
+ if (g_cUsers)
+ {
+ ASMAtomicIncU32(&g_cUsers);
+ return VINF_SUCCESS;
+ }
+
+ RTFILE File;
+ int rc = RTFileOpen(&File, VBOXUSB_DEVICE_NAME, RTFILE_O_READWRITE | RTFILE_O_OPEN | RTFILE_O_DENY_NONE);
+ if (RT_FAILURE(rc))
+ {
+ LogRel((USBLIBR3 ":failed to open the VBoxUSB monitor device node '%s' rc=%Rrc\n", VBOXUSB_DEVICE_NAME, rc));
+ return rc;
+ }
+ g_hFile = File;
+
+ ASMAtomicIncU32(&g_cUsers);
+ /*
+ * Check the USBMonitor version.
+ */
+ VBOXUSBREQ_GET_VERSION Req;
+ bzero(&Req, sizeof(Req));
+ rc = usblibDoIOCtl(VBOXUSBMON_IOCTL_GET_VERSION, &Req, sizeof(Req));
+ if (RT_SUCCESS(rc))
+ {
+ if ( Req.u32Major != VBOXUSBMON_VERSION_MAJOR
+ || Req.u32Minor < VBOXUSBMON_VERSION_MINOR)
+ {
+ rc = VERR_VERSION_MISMATCH;
+ LogRel((USBLIBR3 ":USBMonitor version mismatch! driver v%d.%d, expecting ~v%d.%d\n",
+ Req.u32Major, Req.u32Minor, VBOXUSBMON_VERSION_MAJOR, VBOXUSBMON_VERSION_MINOR));
+
+ RTFileClose(File);
+ g_hFile = NIL_RTFILE;
+ ASMAtomicDecU32(&g_cUsers);
+ return rc;
+ }
+ }
+ else
+ {
+ LogRel((USBLIBR3 ":USBMonitor driver version query failed. rc=%Rrc\n", rc));
+ RTFileClose(File);
+ g_hFile = NIL_RTFILE;
+ ASMAtomicDecU32(&g_cUsers);
+ return rc;
+ }
+
+ return VINF_SUCCESS;
+}
+
+
+USBLIB_DECL(int) USBLibTerm(void)
+{
+ LogFlow((USBLIBR3 ":USBLibTerm\n"));
+
+ if (!g_cUsers)
+ return VERR_WRONG_ORDER;
+ if (ASMAtomicDecU32(&g_cUsers) != 0)
+ return VINF_SUCCESS;
+
+ /*
+ * We're the last guy, close down the connection.
+ */
+ RTFILE File = g_hFile;
+ g_hFile = NIL_RTFILE;
+ if (File == NIL_RTFILE)
+ return VERR_INTERNAL_ERROR;
+
+ int rc = RTFileClose(File);
+ AssertRC(rc);
+ return rc;
+}
+
+
+USBLIB_DECL(void *) USBLibAddFilter(PCUSBFILTER pFilter)
+{
+ LogFlow((USBLIBR3 ":USBLibAddFilter pFilter=%p\n", pFilter));
+
+ VBOXUSBREQ_ADD_FILTER Req;
+ Req.Filter = *pFilter;
+ Req.uId = 0;
+
+ int rc = usblibDoIOCtl(VBOXUSBMON_IOCTL_ADD_FILTER, &Req, sizeof(Req));
+ if (RT_SUCCESS(rc))
+ return (void *)Req.uId;
+
+ AssertMsgFailed((USBLIBR3 ":VBOXUSBMON_IOCTL_ADD_FILTER failed! rc=%Rrc\n", rc));
+ return NULL;
+}
+
+
+USBLIB_DECL(void) USBLibRemoveFilter(void *pvId)
+{
+ LogFlow((USBLIBR3 ":USBLibRemoveFilter pvId=%p\n", pvId));
+
+ VBOXUSBREQ_REMOVE_FILTER Req;
+ Req.uId = (uintptr_t)pvId;
+
+ int rc = usblibDoIOCtl(VBOXUSBMON_IOCTL_REMOVE_FILTER, &Req, sizeof(Req));
+ if (RT_SUCCESS(rc))
+ return;
+
+ AssertMsgFailed((USBLIBR3 ":VBOXUSBMON_IOCTL_REMOVE_FILTER failed! rc=%Rrc\n", rc));
+}
+
+
+USBLIB_DECL(int) USBLibGetClientInfo(char *pszDeviceIdent, char **ppszClientPath, int *pInstance)
+{
+ LogFlow((USBLIBR3 ":USBLibGetClientInfo pszDeviceIdent=%s ppszClientPath=%p pInstance=%p\n",
+ pszDeviceIdent, ppszClientPath, pInstance));
+
+ AssertPtrReturn(pInstance, VERR_INVALID_PARAMETER);
+ AssertPtrReturn(ppszClientPath, VERR_INVALID_PARAMETER);
+ AssertPtrReturn(pszDeviceIdent, VERR_INVALID_PARAMETER);
+
+ VBOXUSBREQ_CLIENT_INFO Req;
+ bzero(&Req, sizeof(Req));
+ RTStrPrintf(Req.szDeviceIdent, sizeof(Req.szDeviceIdent), "%s", pszDeviceIdent);
+
+ int rc = usblibDoIOCtl(VBOXUSBMON_IOCTL_CLIENT_INFO, &Req, sizeof(Req));
+ if (RT_SUCCESS(rc))
+ {
+ *pInstance = Req.Instance;
+ rc = RTStrDupEx(ppszClientPath, Req.szClientPath);
+ if (RT_SUCCESS(rc))
+ return VINF_SUCCESS;
+
+ LogRel((USBLIBR3 ":USBLibGetClientInfo RTStrDupEx failed! rc=%Rrc szClientPath=%s\n", rc, Req.szClientPath));
+ }
+ else
+ LogRel((USBLIBR3 ":USBLibGetClientInfo VBOXUSBMON_IOCTL_CLIENTPATH failed! rc=%Rrc\n", rc));
+
+ return rc;
+}
+
+
+USBLIB_DECL(int) USBLibResetDevice(char *pszDevicePath, bool fReattach)
+{
+ LogFlow((USBLIBR3 ":USBLibResetDevice pszDevicePath=%s\n", pszDevicePath));
+
+ size_t cbPath = strlen(pszDevicePath) + 1;
+ size_t cbReq = sizeof(VBOXUSBREQ_RESET_DEVICE) + cbPath;
+ VBOXUSBREQ_RESET_DEVICE *pReq = (VBOXUSBREQ_RESET_DEVICE *)RTMemTmpAllocZ(cbReq);
+ if (RT_UNLIKELY(!pReq))
+ return VERR_NO_MEMORY;
+
+ pReq->fReattach = fReattach;
+ if (strlcpy(pReq->szDevicePath, pszDevicePath, cbPath) >= cbPath)
+ {
+ LogRel((USBLIBR3 ":USBLibResetDevice buffer overflow. cbPath=%u pszDevicePath=%s\n", cbPath, pszDevicePath));
+ return VERR_BUFFER_OVERFLOW;
+ }
+
+ int rc = usblibDoIOCtl(VBOXUSBMON_IOCTL_RESET_DEVICE, pReq, cbReq);
+ if (RT_FAILURE(rc))
+ LogRel((USBLIBR3 ":VBOXUSBMON_IOCTL_RESET_DEVICE failed! rc=%Rrc\n", rc));
+
+ RTMemFree(pReq);
+ return rc;
+}
+
+
+static int usblibDoIOCtl(unsigned iFunction, void *pvData, size_t cbData)
+{
+ if (g_hFile == NIL_RTFILE)
+ {
+ LogRel((USBLIBR3 ":IOCtl failed, device not open.\n"));
+ return VERR_FILE_NOT_FOUND;
+ }
+
+ VBOXUSBREQ Hdr;
+ Hdr.u32Magic = VBOXUSBMON_MAGIC;
+ Hdr.cbData = cbData; /* Don't include full size because the header size is fixed. */
+ Hdr.pvDataR3 = pvData;
+
+ int rc = ioctl(RTFileToNative(g_hFile), iFunction, &Hdr);
+ if (rc < 0)
+ {
+ rc = errno;
+ LogRel((USBLIBR3 ":IOCtl failed iFunction=%x errno=%d g_file=%d\n", iFunction, rc, RTFileToNative(g_hFile)));
+ return RTErrConvertFromErrno(rc);
+ }
+
+ rc = Hdr.rc;
+ if (RT_UNLIKELY(RT_FAILURE(rc)))
+ LogRel((USBLIBR3 ":Function (%x) failed. rc=%Rrc\n", iFunction, rc));
+
+ return rc;
+}
diff --git a/src/VBox/HostDrivers/VBoxUSB/solaris/VBoxUSB-solaris.c b/src/VBox/HostDrivers/VBoxUSB/solaris/VBoxUSB-solaris.c
new file mode 100644
index 00000000..0568bf66
--- /dev/null
+++ b/src/VBox/HostDrivers/VBoxUSB/solaris/VBoxUSB-solaris.c
@@ -0,0 +1,4054 @@
+/* $Id: VBoxUSB-solaris.c $ */
+/** @file
+ * VirtualBox USB Client Driver, Solaris Hosts.
+ */
+
+/*
+ * Copyright (C) 2008-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#define LOG_GROUP LOG_GROUP_USB_DRV
+#include <VBox/version.h>
+#include <VBox/log.h>
+#include <VBox/err.h>
+#include <VBox/cdefs.h>
+#include <VBox/sup.h>
+#include <VBox/usblib-solaris.h>
+
+#include <iprt/assert.h>
+#include <iprt/initterm.h>
+#include <iprt/semaphore.h>
+#include <iprt/mem.h>
+#include <iprt/process.h>
+#include <iprt/string.h>
+#include <iprt/path.h>
+#include <iprt/thread.h>
+#include <iprt/dbg.h>
+
+#define USBDRV_MAJOR_VER 2
+#define USBDRV_MINOR_VER 0
+#include <sys/usb/usba.h>
+#include <sys/strsun.h>
+#include "usbai_private.h"
+
+
+/*********************************************************************************************************************************
+* Defined Constants And Macros *
+*********************************************************************************************************************************/
+/** The module name. */
+#define DEVICE_NAME "vboxusb"
+/** The module description as seen in 'modinfo'. */
+#define DEVICE_DESC_DRV "VirtualBox USB"
+
+/** -=-=-=-=-=-=- Standard Specifics -=-=-=-=-=-=- */
+/** Max. supported endpoints. */
+#define VBOXUSB_MAX_ENDPOINTS 32
+/** Size of USB Ctrl Xfer Header in bytes. */
+#define VBOXUSB_CTRL_XFER_SIZE 8
+/**
+ * USB2.0 (Sec. 9-13) Bits 10..0 is the max packet size; for high speed Isoc/Intr, bits 12..11 is
+ * number of additional transaction opportunities per microframe.
+ */
+#define VBOXUSB_PKT_SIZE(pkt) (pkt & 0x07FF) * (1 + ((pkt >> 11) & 3))
+/** Endpoint Xfer Type. */
+#define VBOXUSB_XFER_TYPE(endp) ((endp)->EpDesc.bmAttributes & USB_EP_ATTR_MASK)
+/** Endpoint Xfer Direction. */
+#define VBOXUSB_XFER_DIR(endp) ((endp)->EpDesc.bEndpointAddress & USB_EP_DIR_IN)
+/** Create an Endpoint index from an Endpoint address. */
+#define VBOXUSB_GET_EP_INDEX(epaddr) (((epaddr) & USB_EP_NUM_MASK) + \
+ (((epaddr) & USB_EP_DIR_MASK) ? 16 : 0))
+
+
+/** -=-=-=-=-=-=- Tunable Parameters -=-=-=-=-=-=- */
+/** Time to wait while draining inflight UBRs on suspend, in seconds. */
+#define VBOXUSB_DRAIN_TIME 20
+/** Ctrl Xfer timeout in seconds. */
+#define VBOXUSB_CTRL_XFER_TIMEOUT 15
+/** Maximum URB queue length. */
+#define VBOXUSB_URB_QUEUE_SIZE 512
+/** Maximum asynchronous requests per pipe. */
+#define VBOXUSB_MAX_PIPE_ASYNC_REQS 2
+
+/** For enabling global symbols while debugging. **/
+#if defined(DEBUG_ramshankar)
+# define LOCAL
+#else
+# define LOCAL static
+#endif
+
+
+/*********************************************************************************************************************************
+* Kernel Entry Hooks *
+*********************************************************************************************************************************/
+int VBoxUSBSolarisOpen(dev_t *pDev, int fFlag, int fType, cred_t *pCred);
+int VBoxUSBSolarisClose(dev_t Dev, int fFlag, int fType, cred_t *pCred);
+int VBoxUSBSolarisRead(dev_t Dev, struct uio *pUio, cred_t *pCred);
+int VBoxUSBSolarisWrite(dev_t Dev, struct uio *pUio, cred_t *pCred);
+int VBoxUSBSolarisIOCtl(dev_t Dev, int Cmd, intptr_t pArg, int Mode, cred_t *pCred, int *pVal);
+int VBoxUSBSolarisPoll(dev_t Dev, short fEvents, int fAnyYet, short *pReqEvents, struct pollhead **ppPollHead);
+int VBoxUSBSolarisGetInfo(dev_info_t *pDip, ddi_info_cmd_t enmCmd, void *pArg, void **ppResult);
+int VBoxUSBSolarisAttach(dev_info_t *pDip, ddi_attach_cmd_t enmCmd);
+int VBoxUSBSolarisDetach(dev_info_t *pDip, ddi_detach_cmd_t enmCmd);
+int VBoxUSBSolarisPower(dev_info_t *pDip, int Component, int Level);
+
+
+/*********************************************************************************************************************************
+* Structures and Typedefs *
+*********************************************************************************************************************************/
+/**
+ * cb_ops: for drivers that support char/block entry points
+ */
+static struct cb_ops g_VBoxUSBSolarisCbOps =
+{
+ VBoxUSBSolarisOpen,
+ VBoxUSBSolarisClose,
+ nodev, /* b strategy */
+ nodev, /* b dump */
+ nodev, /* b print */
+ VBoxUSBSolarisRead,
+ VBoxUSBSolarisWrite,
+ VBoxUSBSolarisIOCtl,
+ nodev, /* c devmap */
+ nodev, /* c mmap */
+ nodev, /* c segmap */
+ VBoxUSBSolarisPoll,
+ ddi_prop_op, /* property ops */
+ NULL, /* streamtab */
+ D_NEW | D_MP, /* compat. flag */
+ CB_REV, /* revision */
+ nodev, /* c aread */
+ nodev /* c awrite */
+};
+
+/**
+ * dev_ops: for driver device operations
+ */
+static struct dev_ops g_VBoxUSBSolarisDevOps =
+{
+ DEVO_REV, /* driver build revision */
+ 0, /* ref count */
+ VBoxUSBSolarisGetInfo,
+ nulldev, /* identify */
+ nulldev, /* probe */
+ VBoxUSBSolarisAttach,
+ VBoxUSBSolarisDetach,
+ nodev, /* reset */
+ &g_VBoxUSBSolarisCbOps,
+ NULL, /* bus ops */
+ VBoxUSBSolarisPower,
+ ddi_quiesce_not_needed
+};
+
+/**
+ * modldrv: export driver specifics to the kernel
+ */
+static struct modldrv g_VBoxUSBSolarisModule =
+{
+ &mod_driverops, /* extern from kernel */
+ DEVICE_DESC_DRV " " VBOX_VERSION_STRING "r" RT_XSTR(VBOX_SVN_REV),
+ &g_VBoxUSBSolarisDevOps
+};
+
+/**
+ * modlinkage: export install/remove/info to the kernel
+ */
+static struct modlinkage g_VBoxUSBSolarisModLinkage =
+{
+ MODREV_1,
+ &g_VBoxUSBSolarisModule,
+ NULL,
+};
+
+/**
+ * vboxusb_ep_t: Endpoint structure with info. for managing an endpoint.
+ */
+typedef struct vboxusb_ep_t
+{
+ bool fInitialized; /* Whether this Endpoint is initialized */
+ usb_ep_descr_t EpDesc; /* Endpoint descriptor */
+ usb_pipe_handle_t pPipe; /* Endpoint pipe handle */
+ usb_pipe_policy_t PipePolicy; /* Endpoint policy */
+ bool fIsocPolling; /* Whether Isoc. IN polling is enabled */
+ list_t hIsocInUrbs; /* Isoc. IN inflight URBs */
+ uint16_t cIsocInUrbs; /* Number of Isoc. IN inflight URBs */
+ list_t hIsocInLandedReqs; /* Isoc. IN landed requests */
+ uint16_t cbIsocInLandedReqs; /* Cumulative size of landed Isoc. IN requests */
+ size_t cbMaxIsocData; /* Maximum size of Isoc. IN landed buffer */
+} vboxusb_ep_t;
+
+/**
+ * vboxusb_isoc_req_t: Isoc IN. requests queued from device till they are reaped.
+ */
+typedef struct vboxusb_isoc_req_t
+{
+ mblk_t *pMsg; /* Pointer to the data buffer */
+ uint32_t cIsocPkts; /* Number of Isoc pkts */
+ VUSBISOC_PKT_DESC aIsocPkts[8]; /* Array of Isoc pkt descriptors */
+ list_node_t hListLink;
+} vboxusb_isoc_req_t;
+
+/**
+ * VBOXUSB_URB_STATE: Internal USB URB state.
+ */
+typedef enum VBOXUSB_URB_STATE
+{
+ VBOXUSB_URB_STATE_FREE = 0x00,
+ VBOXUSB_URB_STATE_INFLIGHT = 0x04,
+ VBOXUSB_URB_STATE_LANDED = 0x08
+} VBOXUSB_URB_STATE;
+
+/**
+ * vboxusb_urb_t: kernel URB representation.
+ */
+typedef struct vboxusb_urb_t
+{
+ void *pvUrbR3; /* Userspace URB address (untouched, returned while reaping) */
+ uint8_t bEndpoint; /* Endpoint address */
+ VUSBXFERTYPE enmType; /* Xfer type */
+ VUSBDIRECTION enmDir; /* Xfer direction */
+ VUSBSTATUS enmStatus; /* URB status */
+ bool fShortOk; /* Whether receiving less data than requested is acceptable */
+ RTR3PTR pvDataR3; /* Userspace address of the original data buffer */
+ size_t cbDataR3; /* Size of the data buffer */
+ mblk_t *pMsg; /* Pointer to the data buffer */
+ uint32_t cIsocPkts; /* Number of Isoc pkts */
+ VUSBISOC_PKT_DESC aIsocPkts[8]; /* Array of Isoc pkt descriptors */
+ VBOXUSB_URB_STATE enmState; /* URB state (free/in-flight/landed). */
+ struct vboxusb_state_t *pState; /* Pointer to the device instance */
+ list_node_t hListLink; /* List node link handle */
+} vboxusb_urb_t;
+
+/**
+ * vboxusb_power_t: Per Device Power Management info.
+ */
+typedef struct vboxusb_power_t
+{
+ uint_t PowerStates; /* Bit mask of the power states */
+ int PowerBusy; /* Busy reference counter */
+ bool fPowerWakeup; /* Whether remote power wakeup is enabled */
+ bool fPowerRaise; /* Whether to raise the power level */
+ uint8_t PowerLevel; /* Current power level */
+} vboxusb_power_t;
+
+/**
+ * vboxusb_state_t: Per Device instance state info.
+ */
+typedef struct vboxusb_state_t
+{
+ dev_info_t *pDip; /* Per instance device info. */
+ usb_client_dev_data_t *pDevDesc; /* Parsed & complete device descriptor */
+ uint8_t DevState; /* Current USB Device state */
+ bool fDefaultPipeOpen; /* Whether the device (default control pipe) is closed */
+ bool fPollPending; /* Whether the userland process' poll is pending */
+ kmutex_t Mtx; /* Mutex state protection */
+ usb_serialization_t StateMulti; /* State serialization */
+ size_t cbMaxBulkXfer; /* Maximum bulk xfer size */
+ vboxusb_ep_t aEps[VBOXUSB_MAX_ENDPOINTS]; /* Array of all endpoints structures */
+ list_t hFreeUrbs; /* List of free URBs */
+ list_t hInflightUrbs; /* List of inflight URBs */
+ list_t hLandedUrbs; /* List of landed URBs */
+ uint32_t cFreeUrbs; /* Number of free URBs */
+ uint32_t cInflightUrbs; /* Number of inflight URBs */
+ uint32_t cLandedUrbs; /* Number of landed URBs */
+ pollhead_t PollHead; /* Handle to pollhead for waking polling processes */
+ RTPROCESS Process; /* The process (pid) of the user session */
+ VBOXUSBREQ_CLIENT_INFO ClientInfo; /* Registration data */
+ vboxusb_power_t *pPower; /* Power Management */
+ char szMfg[255]; /* Parsed manufacturer string */
+ char szProduct[255]; /* Parsed product string */
+} vboxusb_state_t;
+AssertCompileMemberSize(vboxusb_state_t, szMfg, USB_MAXSTRINGLEN);
+AssertCompileMemberSize(vboxusb_state_t, szProduct, USB_MAXSTRINGLEN);
+
+
+/*********************************************************************************************************************************
+* Internal Functions *
+*********************************************************************************************************************************/
+LOCAL int vboxUsbSolarisInitEp(vboxusb_state_t *pState, usb_ep_data_t *pEpData);
+LOCAL int vboxUsbSolarisInitEpsForCfg(vboxusb_state_t *pState);
+LOCAL int vboxUsbSolarisInitEpsForIfAlt(vboxusb_state_t *pState, uint8_t bIf, uint8_t bAlt);
+LOCAL void vboxUsbSolarisDestroyAllEps(vboxusb_state_t *pState);
+LOCAL void vboxUsbSolarisDestroyEp(vboxusb_state_t *pState, vboxusb_ep_t *pEp);
+LOCAL void vboxUsbSolarisCloseAllPipes(vboxusb_state_t *pState, bool fControlPipe);
+LOCAL int vboxUsbSolarisOpenPipe(vboxusb_state_t *pState, vboxusb_ep_t *pEp);
+LOCAL void vboxUsbSolarisClosePipe(vboxusb_state_t *pState, vboxusb_ep_t *pEp);
+LOCAL int vboxUsbSolarisCtrlXfer(vboxusb_state_t *pState, vboxusb_ep_t *pEp, vboxusb_urb_t *pUrb);
+LOCAL void vboxUsbSolarisCtrlXferCompleted(usb_pipe_handle_t pPipe, usb_ctrl_req_t *pReq);
+LOCAL int vboxUsbSolarisBulkXfer(vboxusb_state_t *pState, vboxusb_ep_t *pEp, vboxusb_urb_t *purb);
+LOCAL void vboxUsbSolarisBulkXferCompleted(usb_pipe_handle_t pPipe, usb_bulk_req_t *pReq);
+LOCAL int vboxUsbSolarisIntrXfer(vboxusb_state_t *pState, vboxusb_ep_t *pEp, vboxusb_urb_t *pUrb);
+LOCAL void vboxUsbSolarisIntrXferCompleted(usb_pipe_handle_t pPipe, usb_intr_req_t *pReq);
+LOCAL int vboxUsbSolarisIsocXfer(vboxusb_state_t *pState, vboxusb_ep_t *pEp, vboxusb_urb_t *pUrb);
+LOCAL void vboxUsbSolarisIsocInXferCompleted(usb_pipe_handle_t pPipe, usb_isoc_req_t *pReq);
+LOCAL void vboxUsbSolarisIsocInXferError(usb_pipe_handle_t pPipe, usb_isoc_req_t *pReq);
+LOCAL void vboxUsbSolarisIsocOutXferCompleted(usb_pipe_handle_t pPipe, usb_isoc_req_t *pReq);
+LOCAL vboxusb_urb_t *vboxUsbSolarisGetIsocInUrb(vboxusb_state_t *pState, PVBOXUSBREQ_URB pUrbReq);
+LOCAL vboxusb_urb_t *vboxUsbSolarisQueueUrb(vboxusb_state_t *pState, PVBOXUSBREQ_URB pUrbReq, mblk_t *pMsg);
+LOCAL VUSBSTATUS vboxUsbSolarisGetUrbStatus(usb_cr_t Status);
+LOCAL void vboxUsbSolarisConcatMsg(vboxusb_urb_t *pUrb);
+LOCAL void vboxUsbSolarisDeQueueUrb(vboxusb_urb_t *pUrb, int URBStatus);
+LOCAL void vboxUsbSolarisNotifyComplete(vboxusb_state_t *pState);
+LOCAL int vboxUsbSolarisProcessIOCtl(int iFunction, void *pvState, int Mode, PVBOXUSBREQ pUSBReq, void *pvBuf,
+ size_t *pcbDataOut);
+LOCAL bool vboxUsbSolarisIsUSBDevice(dev_info_t *pDip);
+
+/** @name Device Operation Hooks
+ * @{ */
+LOCAL int vboxUsbSolarisSendUrb(vboxusb_state_t *pState, PVBOXUSBREQ_URB pUrbReq, int Mode);
+LOCAL int vboxUsbSolarisReapUrb(vboxusb_state_t *pState, PVBOXUSBREQ_URB pUrbReq, int Mode);
+LOCAL int vboxUsbSolarisClearEndPoint(vboxusb_state_t *pState, uint8_t bEndpoint);
+LOCAL int vboxUsbSolarisSetConfig(vboxusb_state_t *pState, uint8_t bCfgValue);
+LOCAL int vboxUsbSolarisGetConfig(vboxusb_state_t *pState, uint8_t *pCfgValue);
+LOCAL int vboxUsbSolarisSetInterface(vboxusb_state_t *pState, uint8_t bIf, uint8_t bAlt);
+LOCAL int vboxUsbSolarisCloseDevice(vboxusb_state_t *pState, VBOXUSB_RESET_LEVEL enmReset);
+LOCAL int vboxUsbSolarisAbortPipe(vboxusb_state_t *pState, uint8_t bEndpoint);
+LOCAL int vboxUsbSolarisGetConfigIndex(vboxusb_state_t *pState, uint_t bCfgValue);
+/** @} */
+
+/** @name Hotplug & Power Management Hooks
+ * @{ */
+LOCAL void vboxUsbSolarisNotifyUnplug(vboxusb_state_t *pState);
+LOCAL int vboxUsbSolarisDeviceDisconnected(dev_info_t *pDip);
+LOCAL int vboxUsbSolarisDeviceReconnected(dev_info_t *pDip);
+
+LOCAL int vboxUsbSolarisInitPower(vboxusb_state_t *pState);
+LOCAL void vboxUsbSolarisDestroyPower(vboxusb_state_t *pState);
+LOCAL int vboxUsbSolarisDeviceSuspend(vboxusb_state_t *pState);
+LOCAL void vboxUsbSolarisDeviceResume(vboxusb_state_t *pState);
+LOCAL void vboxUsbSolarisDeviceRestore(vboxusb_state_t *pState);
+LOCAL void vboxUsbSolarisPowerBusy(vboxusb_state_t *pState);
+LOCAL void vboxUsbSolarisPowerIdle(vboxusb_state_t *pState);
+/** @} */
+
+/** @name Monitor Hooks
+ * @{ */
+int VBoxUSBMonSolarisRegisterClient(dev_info_t *pClientDip, PVBOXUSB_CLIENT_INFO pClientInfo);
+int VBoxUSBMonSolarisUnregisterClient(dev_info_t *pClientDip);
+/** @} */
+
+/** @name Callbacks from Monitor
+ * @{ */
+LOCAL int vboxUsbSolarisSetConsumerCredentials(RTPROCESS Process, int Instance, void *pvReserved);
+/** @} */
+
+
+/*********************************************************************************************************************************
+* Global Variables *
+*********************************************************************************************************************************/
+/** Global list of all device instances. */
+static void *g_pVBoxUSBSolarisState;
+
+/** The default endpoint descriptor */
+static usb_ep_descr_t g_VBoxUSBSolarisDefaultEpDesc = { 7, 5, 0, USB_EP_ATTR_CONTROL, 8, 0 };
+
+/** Size of the usb_ep_data_t struct (used to index into data). */
+static size_t g_cbUsbEpData = ~0UL;
+
+/** The offset of usb_ep_data_t::ep_desc. */
+static size_t g_offUsbEpDataDescr = ~0UL;
+
+
+#ifdef LOG_ENABLED
+/**
+ * Gets the description of an Endpoint's transfer type.
+ *
+ * @param pEp The Endpoint.
+ * @returns The type of the Endpoint.
+ */
+static const char *vboxUsbSolarisEpType(vboxusb_ep_t *pEp)
+{
+ uint8_t uType = VBOXUSB_XFER_TYPE(pEp);
+ switch (uType)
+ {
+ case 0: return "CTRL";
+ case 1: return "ISOC";
+ case 2: return "BULK";
+ default: return "INTR";
+ }
+}
+
+
+/**
+ * Gets the description of an Endpoint's direction.
+ *
+ * @param pEp The Endpoint.
+ * @returns The direction of the Endpoint.
+ */
+static const char *vboxUsbSolarisEpDir(vboxusb_ep_t *pEp)
+{
+ return VBOXUSB_XFER_DIR(pEp) == USB_EP_DIR_IN ? "IN " : "OUT";
+}
+#endif
+
+
+/**
+ * Caches device strings from the parsed device descriptors.
+ *
+ * @param pState The USB device instance.
+ *
+ * @remarks Must only be called after usb_get_dev_data().
+ */
+static void vboxUsbSolarisGetDeviceStrings(vboxusb_state_t *pState)
+{
+ AssertReturnVoid(pState);
+ AssertReturnVoid(pState->pDevDesc);
+
+ if (pState->pDevDesc->dev_product)
+ strlcpy(&pState->szMfg[0], pState->pDevDesc->dev_mfg, sizeof(pState->szMfg));
+ else
+ strlcpy(&pState->szMfg[0], "<Unknown Manufacturer>", sizeof(pState->szMfg));
+
+ if (pState->pDevDesc->dev_product)
+ strlcpy(&pState->szProduct[0], pState->pDevDesc->dev_product, sizeof(pState->szProduct));
+ else
+ strlcpy(&pState->szProduct[0], "<Unnamed USB device>", sizeof(pState->szProduct));
+}
+
+
+/**
+ * Queries the necessary symbols at runtime.
+ *
+ * @returns VBox status code.
+ */
+LOCAL int vboxUsbSolarisQuerySymbols(void)
+{
+ RTDBGKRNLINFO hKrnlDbgInfo;
+ int rc = RTR0DbgKrnlInfoOpen(&hKrnlDbgInfo, 0 /* fFlags */);
+ if (RT_SUCCESS(rc))
+ {
+ /*
+ * Query and sanitize the size of usb_ep_data_t struct.
+ */
+ size_t cbPrevUsbEpData = g_cbUsbEpData;
+ rc = RTR0DbgKrnlInfoQuerySize(hKrnlDbgInfo, "usba", "usb_ep_data_t", &g_cbUsbEpData);
+ if (RT_FAILURE(rc))
+ {
+ LogRel(("Failed to query size of \"usb_ep_data_t\" in the \"usba\" module, rc=%Rrc\n", rc));
+ return rc;
+ }
+ if (g_cbUsbEpData > _4K)
+ {
+ LogRel(("Size of \"usb_ep_data_t\" (%u bytes) seems implausible, too paranoid to continue\n", g_cbUsbEpData));
+ return VERR_MISMATCH;
+ }
+
+ /*
+ * Query and sanitizie the offset of usb_ep_data_t::ep_descr.
+ */
+ size_t offPrevUsbEpDataDescr = g_offUsbEpDataDescr;
+ rc = RTR0DbgKrnlInfoQueryMember(hKrnlDbgInfo, "usba", "usb_ep_data_t", "ep_descr", &g_offUsbEpDataDescr);
+ if (RT_FAILURE(rc))
+ {
+ LogRel(("Failed to query offset of usb_ep_data_t::ep_descr, rc=%Rrc\n", rc));
+ return rc;
+ }
+ if (g_offUsbEpDataDescr > _4K - sizeof(usb_ep_descr_t))
+ {
+ LogRel(("Offset of \"ep_desrc\" (%u) seems implausible, too paranoid to continue\n", g_offUsbEpDataDescr));
+ return VERR_MISMATCH;
+ }
+
+ /*
+ * Log only when it changes / first time, since _init() seems to be called often (e.g. on failed attaches).
+ * cmn_err, CE_CONT and '!' is used to not show the message on console during boot each time.
+ */
+ if ( cbPrevUsbEpData != g_cbUsbEpData
+ || offPrevUsbEpDataDescr != g_offUsbEpDataDescr)
+ {
+ cmn_err(CE_CONT, "!usba_ep_data_t is %lu bytes\n", g_cbUsbEpData);
+ cmn_err(CE_CONT, "!usba_ep_data_t::ep_descr @ 0x%lx (%ld)\n", g_offUsbEpDataDescr, g_offUsbEpDataDescr);
+ }
+
+ RTR0DbgKrnlInfoRelease(hKrnlDbgInfo);
+ }
+
+ return rc;
+}
+
+
+/**
+ * Kernel entry points
+ */
+int _init(void)
+{
+ LogFunc((DEVICE_NAME ": _init\n"));
+
+ /*
+ * Prevent module autounloading.
+ */
+ modctl_t *pModCtl = mod_getctl(&g_VBoxUSBSolarisModLinkage);
+ if (pModCtl)
+ pModCtl->mod_loadflags |= MOD_NOAUTOUNLOAD;
+ else
+ LogRel((DEVICE_NAME ": _init: failed to disable autounloading!\n"));
+
+ /*
+ * Initialize IPRT R0 driver, which internally calls OS-specific r0 init.
+ */
+ int rc = RTR0Init(0);
+ if (RT_SUCCESS(rc))
+ {
+ rc = vboxUsbSolarisQuerySymbols();
+ if (RT_FAILURE(rc))
+ {
+ RTR0Term();
+ return EINVAL;
+ }
+
+ rc = ddi_soft_state_init(&g_pVBoxUSBSolarisState, sizeof(vboxusb_state_t), 4 /* pre-alloc */);
+ if (!rc)
+ {
+ rc = mod_install(&g_VBoxUSBSolarisModLinkage);
+ if (!rc)
+ return rc;
+
+ LogRel((DEVICE_NAME ": _init: mod_install failed! rc=%d\n", rc));
+ ddi_soft_state_fini(&g_pVBoxUSBSolarisState);
+ }
+ else
+ LogRel((DEVICE_NAME ": _init: failed to initialize soft state\n"));
+
+ RTR0Term();
+ }
+ else
+ LogRel((DEVICE_NAME ": _init: RTR0Init failed! rc=%d\n", rc));
+ return RTErrConvertToErrno(rc);
+}
+
+
+int _fini(void)
+{
+ int rc;
+
+ LogFunc((DEVICE_NAME ": _fini\n"));
+
+ rc = mod_remove(&g_VBoxUSBSolarisModLinkage);
+ if (!rc)
+ {
+ ddi_soft_state_fini(&g_pVBoxUSBSolarisState);
+ RTR0Term();
+ }
+
+ return rc;
+}
+
+
+int _info(struct modinfo *pModInfo)
+{
+ LogFunc((DEVICE_NAME ": _info\n"));
+
+ return mod_info(&g_VBoxUSBSolarisModLinkage, 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)
+ *
+ * @returns Solaris error code.
+ */
+int VBoxUSBSolarisAttach(dev_info_t *pDip, ddi_attach_cmd_t enmCmd)
+{
+ LogFunc((DEVICE_NAME ": VBoxUSBSolarisAttach: pDip=%p enmCmd=%d\n", pDip, enmCmd));
+
+ int rc;
+ int instance = ddi_get_instance(pDip);
+ vboxusb_state_t *pState = NULL;
+
+ switch (enmCmd)
+ {
+ case DDI_ATTACH:
+ {
+ rc = ddi_soft_state_zalloc(g_pVBoxUSBSolarisState, instance);
+ if (rc == DDI_SUCCESS)
+ {
+ pState = ddi_get_soft_state(g_pVBoxUSBSolarisState, instance);
+ if (RT_LIKELY(pState))
+ {
+ pState->pDip = pDip;
+ pState->pDevDesc = NULL;
+ pState->fPollPending = false;
+ pState->cInflightUrbs = 0;
+ pState->cFreeUrbs = 0;
+ pState->cLandedUrbs = 0;
+ pState->Process = NIL_RTPROCESS;
+ pState->pPower = NULL;
+ bzero(pState->aEps, sizeof(pState->aEps));
+ list_create(&pState->hFreeUrbs, sizeof(vboxusb_urb_t), offsetof(vboxusb_urb_t, hListLink));
+ list_create(&pState->hInflightUrbs, sizeof(vboxusb_urb_t), offsetof(vboxusb_urb_t, hListLink));
+ list_create(&pState->hLandedUrbs, sizeof(vboxusb_urb_t), offsetof(vboxusb_urb_t, hListLink));
+
+ /*
+ * There is a bug in usb_client_attach() as of Nevada 120 which panics when we bind to
+ * a non-USB device. So check if we are really binding to a USB device or not.
+ */
+ if (vboxUsbSolarisIsUSBDevice(pState->pDip))
+ {
+ /*
+ * Here starts the USB specifics.
+ */
+ rc = usb_client_attach(pState->pDip, USBDRV_VERSION, 0);
+ if (rc == USB_SUCCESS)
+ {
+ pState->fDefaultPipeOpen = true;
+
+ /*
+ * Parse out the entire descriptor.
+ */
+ rc = usb_get_dev_data(pState->pDip, &pState->pDevDesc, USB_PARSE_LVL_ALL, 0 /* Unused */);
+ if (rc == USB_SUCCESS)
+ {
+ /*
+ * Cache some device descriptor strings.
+ */
+ vboxUsbSolarisGetDeviceStrings(pState);
+#ifdef DEBUG_ramshankar
+ usb_print_descr_tree(pState->pDip, pState->pDevDesc);
+#endif
+
+ /*
+ * Initialize state locks.
+ */
+ mutex_init(&pState->Mtx, NULL, MUTEX_DRIVER, pState->pDevDesc->dev_iblock_cookie);
+ pState->StateMulti = usb_init_serialization(pState->pDip, USB_INIT_SER_CHECK_SAME_THREAD);
+
+ /*
+ * Get maximum bulk transfer size supported by the HCD.
+ */
+ rc = usb_pipe_get_max_bulk_transfer_size(pState->pDip, &pState->cbMaxBulkXfer);
+ if (rc == USB_SUCCESS)
+ {
+ Log((DEVICE_NAME ": VBoxUSBSolarisAttach: cbMaxBulkXfer=%d\n", pState->cbMaxBulkXfer));
+
+ /*
+ * Initialize the default endpoint.
+ */
+ rc = vboxUsbSolarisInitEp(pState, NULL /* pEp */);
+ if (RT_SUCCESS(rc))
+ {
+ /*
+ * Set the device state.
+ */
+ pState->DevState = USB_DEV_ONLINE;
+
+ /*
+ * Initialize power management for the device.
+ */
+ rc = vboxUsbSolarisInitPower(pState);
+ if (RT_SUCCESS(rc))
+ {
+ /*
+ * Initialize endpoints for the current config.
+ */
+ rc = vboxUsbSolarisInitEpsForCfg(pState);
+ AssertRC(rc);
+
+ /*
+ * Publish the minor node.
+ */
+ rc = ddi_create_priv_minor_node(pDip, DEVICE_NAME, S_IFCHR, instance, DDI_PSEUDO, 0,
+ "none", "none", 0666);
+ if (RT_LIKELY(rc == DDI_SUCCESS))
+ {
+ /*
+ * Register hotplug callbacks.
+ */
+ rc = usb_register_hotplug_cbs(pState->pDip, &vboxUsbSolarisDeviceDisconnected,
+ &vboxUsbSolarisDeviceReconnected);
+ if (RT_LIKELY(rc == USB_SUCCESS))
+ {
+ /*
+ * Register with our monitor driver.
+ */
+ bzero(&pState->ClientInfo, sizeof(pState->ClientInfo));
+ char szDevicePath[MAXPATHLEN];
+ ddi_pathname(pState->pDip, szDevicePath);
+ RTStrPrintf(pState->ClientInfo.szClientPath,
+ sizeof(pState->ClientInfo.szClientPath),
+ "/devices%s:%s", szDevicePath, DEVICE_NAME);
+ RTStrPrintf(pState->ClientInfo.szDeviceIdent,
+ sizeof(pState->ClientInfo.szDeviceIdent),
+ "%#x:%#x:%d:%s",
+ pState->pDevDesc->dev_descr->idVendor,
+ pState->pDevDesc->dev_descr->idProduct,
+ pState->pDevDesc->dev_descr->bcdDevice, szDevicePath);
+ pState->ClientInfo.Instance = instance;
+ pState->ClientInfo.pfnSetConsumerCredentials = &vboxUsbSolarisSetConsumerCredentials;
+ rc = VBoxUSBMonSolarisRegisterClient(pState->pDip, &pState->ClientInfo);
+ if (RT_SUCCESS(rc))
+ {
+#if 0
+ LogRel((DEVICE_NAME ": Captured %s %s (Ident=%s)\n", pState->szMfg,
+ pState->szProduct, pState->ClientInfo.szDeviceIdent));
+#else
+ /* Until IPRT R0 logging is fixed. See @bugref{6657#c7} */
+ cmn_err(CE_CONT, "Captured %s %s (Ident=%s)\n", pState->szMfg,
+ pState->szProduct, pState->ClientInfo.szDeviceIdent);
+#endif
+ return DDI_SUCCESS;
+ }
+
+ LogRel((DEVICE_NAME ": VBoxUSBMonSolarisRegisterClient failed! rc=%d "
+ "path=%s instance=%d\n", rc, pState->ClientInfo.szClientPath,
+ instance));
+
+ usb_unregister_hotplug_cbs(pState->pDip);
+ }
+ else
+ LogRel((DEVICE_NAME ": VBoxUSBSolarisAttach: Failed to register hotplug callbacks! rc=%d\n", rc));
+
+ ddi_remove_minor_node(pState->pDip, NULL);
+ }
+ else
+ LogRel((DEVICE_NAME ": VBoxUSBSolarisAttach: ddi_create_minor_node failed! rc=%d\n", rc));
+
+ mutex_enter(&pState->Mtx);
+ vboxUsbSolarisDestroyPower(pState);
+ mutex_exit(&pState->Mtx);
+ }
+ else
+ LogRel((DEVICE_NAME ": VBoxUSBSolarisAttach: Failed to init power management! rc=%d\n", rc));
+ }
+ else
+ LogRel((DEVICE_NAME ": VBoxUSBSolarisAttach: vboxUsbSolarisInitEp failed! rc=%d\n", rc));
+ }
+ else
+ LogRel((DEVICE_NAME ": VBoxUSBSolarisAttach: usb_pipe_get_max_bulk_transfer_size failed! rc=%d\n", rc));
+
+ usb_fini_serialization(pState->StateMulti);
+ mutex_destroy(&pState->Mtx);
+ usb_free_dev_data(pState->pDip, pState->pDevDesc);
+ }
+ else
+ LogRel((DEVICE_NAME ": VBoxUSBSolarisAttach: Failed to get device descriptor. rc=%d\n", rc));
+
+ usb_client_detach(pState->pDip, NULL);
+ }
+ else
+ LogRel((DEVICE_NAME ": VBoxUSBSolarisAttach: usb_client_attach failed! rc=%d\n", rc));
+ }
+ else
+ {
+ /* This would appear on every boot if it were LogRel() */
+ Log((DEVICE_NAME ": VBoxUSBSolarisAttach: Not a USB device\n"));
+ }
+ }
+ else
+ LogRel((DEVICE_NAME ": VBoxUSBSolarisAttach: Failed to get soft state\n", sizeof(*pState)));
+
+ ddi_soft_state_free(g_pVBoxUSBSolarisState, instance);
+ }
+ else
+ LogRel((DEVICE_NAME ": VBoxUSBSolarisAttach: Failed to alloc soft state. rc=%d\n", rc));
+
+ return DDI_FAILURE;
+ }
+
+ case DDI_RESUME:
+ {
+ pState = ddi_get_soft_state(g_pVBoxUSBSolarisState, instance);
+ if (RT_UNLIKELY(!pState))
+ {
+ LogRel((DEVICE_NAME ": VBoxUSBSolarisAttach: DDI_RESUME failed to get soft state on detach\n"));
+ return DDI_FAILURE;
+ }
+
+ vboxUsbSolarisDeviceResume(pState);
+ 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)
+ *
+ * @returns Solaris error code.
+ */
+int VBoxUSBSolarisDetach(dev_info_t *pDip, ddi_detach_cmd_t enmCmd)
+{
+ LogFunc((DEVICE_NAME ": VBoxUSBSolarisDetach: pDip=%p enmCmd=%d\n", pDip, enmCmd));
+
+ int instance = ddi_get_instance(pDip);
+ vboxusb_state_t *pState = ddi_get_soft_state(g_pVBoxUSBSolarisState, instance);
+ if (RT_UNLIKELY(!pState))
+ {
+ LogRel((DEVICE_NAME ": VBoxUSBSolarisDetach: Failed to get soft state on detach\n"));
+ return DDI_FAILURE;
+ }
+
+ switch (enmCmd)
+ {
+ case DDI_DETACH:
+ {
+ /*
+ * At this point it must be assumed that the default control pipe has
+ * already been closed by userland (via VBoxUSBSolarisClose() entry point).
+ * Once it's closed we can no longer open or reference the device here.
+ */
+
+ /*
+ * Notify userland if any that we're gone (while resetting device held by us).
+ */
+ mutex_enter(&pState->Mtx);
+ pState->DevState = USB_DEV_DISCONNECTED;
+ vboxUsbSolarisNotifyUnplug(pState);
+ mutex_exit(&pState->Mtx);
+
+
+ /*
+ * Unregister hotplug callback events first without holding the mutex as the callbacks
+ * would otherwise block on the mutex.
+ */
+ usb_unregister_hotplug_cbs(pDip);
+
+ /*
+ * Serialize: paranoid; drain other driver activity.
+ */
+ usb_serialize_access(pState->StateMulti, USB_WAIT, 0 /* timeout */);
+ usb_release_access(pState->StateMulti);
+ mutex_enter(&pState->Mtx);
+
+ /*
+ * Close all pipes.
+ */
+ vboxUsbSolarisCloseAllPipes(pState, true /* ControlPipe */);
+ Assert(!pState->fDefaultPipeOpen);
+
+ /*
+ * Deinitialize power, destroy all endpoints.
+ */
+ vboxUsbSolarisDestroyPower(pState);
+ vboxUsbSolarisDestroyAllEps(pState);
+
+ /*
+ * Free up all URB lists.
+ */
+ vboxusb_urb_t *pUrb = NULL;
+ while ((pUrb = list_remove_head(&pState->hFreeUrbs)) != NULL)
+ {
+ if (pUrb->pMsg)
+ freemsg(pUrb->pMsg);
+ RTMemFree(pUrb);
+ }
+ while ((pUrb = list_remove_head(&pState->hInflightUrbs)) != NULL)
+ {
+ if (pUrb->pMsg)
+ freemsg(pUrb->pMsg);
+ RTMemFree(pUrb);
+ }
+ while ((pUrb = list_remove_head(&pState->hLandedUrbs)) != NULL)
+ {
+ if (pUrb->pMsg)
+ freemsg(pUrb->pMsg);
+ RTMemFree(pUrb);
+ }
+ pState->cFreeUrbs = 0;
+ pState->cLandedUrbs = 0;
+ pState->cInflightUrbs = 0;
+ list_destroy(&pState->hFreeUrbs);
+ list_destroy(&pState->hInflightUrbs);
+ list_destroy(&pState->hLandedUrbs);
+
+ /*
+ * Destroy locks, free up descriptor and detach from USBA.
+ */
+ mutex_exit(&pState->Mtx);
+ usb_fini_serialization(pState->StateMulti);
+ mutex_destroy(&pState->Mtx);
+
+ usb_free_dev_data(pState->pDip, pState->pDevDesc);
+ usb_client_detach(pState->pDip, NULL);
+
+ /*
+ * Deregister with our Monitor driver.
+ */
+ VBoxUSBMonSolarisUnregisterClient(pState->pDip);
+
+ ddi_remove_minor_node(pState->pDip, NULL);
+
+#if 0
+ LogRel((DEVICE_NAME ": Released %s %s (Ident=%s)\n", pState->szMfg, pState->szProduct,
+ pState->ClientInfo.szDeviceIdent));
+#else
+ /* Until IPRT R0 logging is fixed. See @bugref{6657#c7} */
+ cmn_err(CE_CONT, "Released %s %s (Ident=%s)\n", pState->szMfg, pState->szProduct, pState->ClientInfo.szDeviceIdent);
+#endif
+
+ ddi_soft_state_free(g_pVBoxUSBSolarisState, instance);
+ pState = NULL;
+ return DDI_SUCCESS;
+ }
+
+ case DDI_SUSPEND:
+ {
+ int rc = vboxUsbSolarisDeviceSuspend(pState);
+ if (RT_SUCCESS(rc))
+ return DDI_SUCCESS;
+
+ return DDI_FAILURE;
+ }
+
+ default:
+ return DDI_FAILURE;
+ }
+}
+
+
+/**
+ * Info entry point, called by solaris kernel for obtaining driver info.
+ *
+ * @param pDip The module structure instance (do not use).
+ * @param enmCmd Information request type.
+ * @param pvArg Type specific argument.
+ * @param ppvResult Where to store the requested info.
+ *
+ * @returns Solaris error code.
+ */
+int VBoxUSBSolarisGetInfo(dev_info_t *pDip, ddi_info_cmd_t enmCmd, void *pvArg, void **ppvResult)
+{
+ LogFunc((DEVICE_NAME ": VBoxUSBSolarisGetInfo\n"));
+
+ vboxusb_state_t *pState = NULL;
+ int instance = getminor((dev_t)pvArg);
+
+ switch (enmCmd)
+ {
+ case DDI_INFO_DEVT2DEVINFO:
+ {
+ /*
+ * One is to one mapping of instance & minor number as we publish only one minor node per device.
+ */
+ pState = ddi_get_soft_state(g_pVBoxUSBSolarisState, instance);
+ if (pState)
+ {
+ *ppvResult = (void *)pState->pDip;
+ return DDI_SUCCESS;
+ }
+ else
+ LogRel((DEVICE_NAME ": VBoxUSBSolarisGetInfo: Failed to get device state\n"));
+ return DDI_FAILURE;
+ }
+
+ case DDI_INFO_DEVT2INSTANCE:
+ {
+ *ppvResult = (void *)(uintptr_t)instance;
+ return DDI_SUCCESS;
+ }
+
+ default:
+ return DDI_FAILURE;
+ }
+}
+
+
+/**
+ * Callback invoked from the VirtualBox USB Monitor driver when a VM process
+ * tries to access this USB client instance.
+ *
+ * This determines which VM process will be allowed to open and access this USB
+ * device.
+ *
+ * @returns VBox status code.
+ *
+ * @param Process The VM process performing the client info. query.
+ * @param Instance This client instance (the one set while we register
+ * ourselves to the Monitor driver)
+ * @param pvReserved Reserved for future, unused.
+ */
+LOCAL int vboxUsbSolarisSetConsumerCredentials(RTPROCESS Process, int Instance, void *pvReserved)
+{
+ LogFunc((DEVICE_NAME ": vboxUsbSolarisSetConsumerCredentials: Process=%u Instance=%d\n", Process, Instance));
+ vboxusb_state_t *pState = ddi_get_soft_state(g_pVBoxUSBSolarisState, Instance);
+ if (!pState)
+ {
+ LogRel((DEVICE_NAME ": vboxUsbSolarisSetConsumerCredentials: Failed to get device state for instance %d\n", Instance));
+ return VERR_INVALID_STATE;
+ }
+
+ int rc = VINF_SUCCESS;
+ mutex_enter(&pState->Mtx);
+
+ if (pState->Process == NIL_RTPROCESS)
+ pState->Process = Process;
+ else
+ {
+ LogRel((DEVICE_NAME ": vboxUsbSolarisSetConsumerCredentials: Failed! Process %u already has client open\n",
+ pState->Process));
+ rc = VERR_RESOURCE_BUSY;
+ }
+
+ mutex_exit(&pState->Mtx);
+
+ return rc;
+}
+
+
+int VBoxUSBSolarisOpen(dev_t *pDev, int fFlag, int fType, cred_t *pCred)
+{
+ LogFunc((DEVICE_NAME ": VBoxUSBSolarisOpen: pDev=%p fFlag=%d fType=%d pCred=%p\n", pDev, fFlag, fType, pCred));
+
+ /*
+ * Verify we are being opened as a character device
+ */
+ if (fType != OTYP_CHR)
+ return EINVAL;
+
+ /*
+ * One is to one mapping. (Minor<=>Instance).
+ */
+ int instance = getminor((dev_t)*pDev);
+ vboxusb_state_t *pState = ddi_get_soft_state(g_pVBoxUSBSolarisState, instance);
+ if (!pState)
+ {
+ LogRel((DEVICE_NAME ": VBoxUSBSolarisOpen: Failed to get device state for instance %d\n", instance));
+ return ENXIO;
+ }
+
+ mutex_enter(&pState->Mtx);
+
+ /*
+ * Only one user process can open a device instance at a time.
+ */
+ if (pState->Process != RTProcSelf())
+ {
+ if (pState->Process == NIL_RTPROCESS)
+ LogRel((DEVICE_NAME ": VBoxUSBSolarisOpen: No prior information about authorized process\n"));
+ else
+ LogRel((DEVICE_NAME ": VBoxUSBSolarisOpen: Process %u is already using this device instance\n", pState->Process));
+
+ mutex_exit(&pState->Mtx);
+ return EPERM;
+ }
+
+ mutex_exit(&pState->Mtx);
+
+ NOREF(fFlag);
+ NOREF(pCred);
+
+ return 0;
+}
+
+
+int VBoxUSBSolarisClose(dev_t Dev, int fFlag, int fType, cred_t *pCred)
+{
+ LogFunc((DEVICE_NAME ": VBoxUSBSolarisClose: Dev=%d fFlag=%d fType=%d pCred=%p\n", Dev, fFlag, fType, pCred));
+
+ int instance = getminor((dev_t)Dev);
+ vboxusb_state_t *pState = ddi_get_soft_state(g_pVBoxUSBSolarisState, instance);
+ if (RT_UNLIKELY(!pState))
+ {
+ LogRel((DEVICE_NAME ": VBoxUSBSolarisClose: Failed to get device state for instance %d\n", instance));
+ return ENXIO;
+ }
+
+ mutex_enter(&pState->Mtx);
+ pState->fPollPending = false;
+ pState->Process = NIL_RTPROCESS;
+ mutex_exit(&pState->Mtx);
+
+ return 0;
+}
+
+
+int VBoxUSBSolarisRead(dev_t Dev, struct uio *pUio, cred_t *pCred)
+{
+ LogFunc((DEVICE_NAME ": VBoxUSBSolarisRead\n"));
+ return ENOTSUP;
+}
+
+
+int VBoxUSBSolarisWrite(dev_t Dev, struct uio *pUio, cred_t *pCred)
+{
+ LogFunc((DEVICE_NAME ": VBoxUSBSolarisWrite\n"));
+ return ENOTSUP;
+}
+
+
+int VBoxUSBSolarisPoll(dev_t Dev, short fEvents, int fAnyYet, short *pReqEvents, struct pollhead **ppPollHead)
+{
+ LogFunc((DEVICE_NAME ": VBoxUSBSolarisPoll: Dev=%d fEvents=%d fAnyYet=%d pReqEvents=%p\n", Dev, fEvents, fAnyYet, pReqEvents));
+
+ /*
+ * Get the device state (one to one mapping).
+ */
+ int instance = getminor((dev_t)Dev);
+ vboxusb_state_t *pState = ddi_get_soft_state(g_pVBoxUSBSolarisState, instance);
+ if (RT_UNLIKELY(!pState))
+ {
+ LogRel((DEVICE_NAME ": VBoxUSBSolarisPoll: No state data for %d\n", instance));
+ return ENXIO;
+ }
+
+ mutex_enter(&pState->Mtx);
+
+ /*
+ * Disconnect event (POLLHUP) is invalid in "fEvents".
+ */
+ if (pState->DevState == USB_DEV_DISCONNECTED)
+ *pReqEvents |= POLLHUP;
+ else if (pState->cLandedUrbs)
+ *pReqEvents |= POLLIN;
+ else
+ {
+ *pReqEvents = 0;
+ if (!fAnyYet)
+ {
+ *ppPollHead = &pState->PollHead;
+ pState->fPollPending = true;
+ }
+ }
+
+ mutex_exit(&pState->Mtx);
+
+ return 0;
+}
+
+
+int VBoxUSBSolarisPower(dev_info_t *pDip, int Component, int Level)
+{
+ LogFunc((DEVICE_NAME ": VBoxUSBSolarisPower: pDip=%p Component=%d Level=%d\n", pDip, Component, Level));
+
+ int instance = ddi_get_instance(pDip);
+ vboxusb_state_t *pState = ddi_get_soft_state(g_pVBoxUSBSolarisState, instance);
+ if (RT_UNLIKELY(!pState))
+ {
+ LogRel((DEVICE_NAME ": VBoxUSBSolarisPower: Failed! State Gone\n"));
+ return DDI_FAILURE;
+ }
+
+ if (!pState->pPower)
+ return DDI_SUCCESS;
+
+ usb_serialize_access(pState->StateMulti, USB_WAIT, 0);
+ mutex_enter(&pState->Mtx);
+
+ int rc = USB_FAILURE;
+ if (pState->DevState == USB_DEV_ONLINE)
+ {
+ /*
+ * Check if we are transitioning to a valid power state.
+ */
+ if (!USB_DEV_PWRSTATE_OK(pState->pPower->PowerStates, Level))
+ {
+ switch (Level)
+ {
+ case USB_DEV_OS_PWR_OFF:
+ {
+ if (pState->pPower->PowerBusy)
+ break;
+
+ /*
+ * USB D3 command.
+ */
+ pState->pPower->PowerLevel = USB_DEV_OS_PWR_OFF;
+ mutex_exit(&pState->Mtx);
+ rc = USB_SUCCESS; /* usb_set_device_pwrlvl3(pDip); */
+ mutex_enter(&pState->Mtx);
+ break;
+ }
+
+ case USB_DEV_OS_FULL_PWR:
+ {
+ /*
+ * Can happen during shutdown of the OS.
+ */
+ pState->pPower->PowerLevel = USB_DEV_OS_FULL_PWR;
+ mutex_exit(&pState->Mtx);
+ rc = USB_SUCCESS; /* usb_set_device_pwrlvl0(pDip); */
+ mutex_enter(&pState->Mtx);
+ break;
+ }
+
+ default: /* Power levels 1, 2 not implemented */
+ break;
+ }
+ }
+ else
+ Log((DEVICE_NAME ": VBoxUSBSolarisPower: USB_DEV_PWRSTATE_OK failed\n"));
+ }
+ else
+ rc = USB_SUCCESS;
+
+ mutex_exit(&pState->Mtx);
+ usb_release_access(pState->StateMulti);
+ return rc == USB_SUCCESS ? DDI_SUCCESS : DDI_FAILURE;
+}
+
+
+/** @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(Code) (((Code) >> 16) & IOCPARM_MASK)
+#endif
+
+int VBoxUSBSolarisIOCtl(dev_t Dev, int Cmd, intptr_t pArg, int Mode, cred_t *pCred, int *pVal)
+{
+ /* LogFunc((DEVICE_NAME ": VBoxUSBSolarisIOCtl: Dev=%d Cmd=%d pArg=%p Mode=%d\n", Dev, Cmd, pArg)); */
+
+ /*
+ * Get the device state (one to one mapping).
+ */
+ int instance = getminor((dev_t)Dev);
+ vboxusb_state_t *pState = ddi_get_soft_state(g_pVBoxUSBSolarisState, instance);
+ if (RT_UNLIKELY(!pState))
+ {
+ LogRel((DEVICE_NAME ": VBoxUSBSolarisIOCtl: No state data for %d\n", instance));
+ return EINVAL;
+ }
+
+ /*
+ * Read the request wrapper.
+ */
+ VBOXUSBREQ ReqWrap;
+ if (IOCPARM_LEN(Cmd) != sizeof(ReqWrap))
+ {
+ LogRel((DEVICE_NAME ": VBoxUSBSolarisIOCtl: Bad request %#x size=%d expected=%d\n", Cmd, IOCPARM_LEN(Cmd),
+ sizeof(ReqWrap)));
+ return ENOTTY;
+ }
+
+ int rc = ddi_copyin((void *)pArg, &ReqWrap, sizeof(ReqWrap), Mode);
+ if (RT_UNLIKELY(rc))
+ {
+ LogRel((DEVICE_NAME ": VBoxUSBSolarisIOCtl: ddi_copyin failed to read header pArg=%p Cmd=%d. rc=%d\n", pArg, Cmd, rc));
+ return EINVAL;
+ }
+
+ if (ReqWrap.u32Magic != VBOXUSB_MAGIC)
+ {
+ LogRel((DEVICE_NAME ": VBoxUSBSolarisIOCtl: Bad magic %#x; pArg=%p Cmd=%d\n", ReqWrap.u32Magic, pArg, Cmd));
+ return EINVAL;
+ }
+ if (RT_UNLIKELY( ReqWrap.cbData == 0
+ || ReqWrap.cbData > _1M*16))
+ {
+ LogRel((DEVICE_NAME ": VBoxUSBSolarisIOCtl: Bad size %#x; pArg=%p Cmd=%d\n", ReqWrap.cbData, pArg, Cmd));
+ return EINVAL;
+ }
+
+ /*
+ * Read the request.
+ */
+ void *pvBuf = RTMemTmpAlloc(ReqWrap.cbData);
+ if (RT_UNLIKELY(!pvBuf))
+ {
+ LogRel((DEVICE_NAME ": VBoxUSBSolarisIOCtl: RTMemTmpAlloc failed to alloc %d bytes\n", ReqWrap.cbData));
+ return ENOMEM;
+ }
+
+ rc = ddi_copyin((void *)(uintptr_t)ReqWrap.pvDataR3, pvBuf, ReqWrap.cbData, Mode);
+ if (RT_UNLIKELY(rc))
+ {
+ RTMemTmpFree(pvBuf);
+ LogRel((DEVICE_NAME ": VBoxUSBSolarisIOCtl: ddi_copyin failed! pvBuf=%p pArg=%p Cmd=%d. rc=%d\n", pvBuf, pArg, Cmd, rc));
+ return EFAULT;
+ }
+ if (RT_UNLIKELY( ReqWrap.cbData == 0
+ || pvBuf == NULL))
+ {
+ RTMemTmpFree(pvBuf);
+ LogRel((DEVICE_NAME ": VBoxUSBSolarisIOCtl: Invalid request! pvBuf=%p cbData=%d\n", pvBuf, ReqWrap.cbData));
+ return EINVAL;
+ }
+
+ /*
+ * Process the IOCtl.
+ */
+ size_t cbDataOut = 0;
+ rc = vboxUsbSolarisProcessIOCtl(Cmd, pState, Mode, &ReqWrap, pvBuf, &cbDataOut);
+ ReqWrap.rc = rc;
+ rc = 0;
+
+ if (RT_UNLIKELY(cbDataOut > ReqWrap.cbData))
+ {
+ LogRel((DEVICE_NAME ": VBoxUSBSolarisIOCtl: Too much output data %d expected %d Truncating!\n", cbDataOut,
+ ReqWrap.cbData));
+ cbDataOut = ReqWrap.cbData;
+ }
+
+ ReqWrap.cbData = cbDataOut;
+
+ /*
+ * Copy VBOXUSBREQ back to userspace (which contains rc for USB operation).
+ */
+ rc = ddi_copyout(&ReqWrap, (void *)pArg, sizeof(ReqWrap), Mode);
+ if (RT_LIKELY(!rc))
+ {
+ /*
+ * Copy payload (if any) back to userspace.
+ */
+ if (cbDataOut > 0)
+ {
+ rc = ddi_copyout(pvBuf, (void *)(uintptr_t)ReqWrap.pvDataR3, cbDataOut, Mode);
+ if (RT_UNLIKELY(rc))
+ {
+ LogRel((DEVICE_NAME ": VBoxUSBSolarisIOCtl: ddi_copyout failed! pvBuf=%p pArg=%p Cmd=%d. rc=%d\n", pvBuf, pArg,
+ Cmd, rc));
+ rc = EFAULT;
+ }
+ }
+ }
+ else
+ {
+ LogRel((DEVICE_NAME ": VBoxUSBSolarisIOCtl: ddi_copyout(1)failed! pReqWrap=%p pArg=%p Cmd=%d. rc=%d\n", &ReqWrap, pArg,
+ Cmd, rc));
+ rc = EFAULT;
+ }
+
+ *pVal = rc;
+ RTMemTmpFree(pvBuf);
+ return rc;
+}
+
+
+/**
+ * IOCtl processor for user to kernel and kernel to kernel communication.
+ *
+ * @returns VBox status code.
+ *
+ * @param iFunction The requested function.
+ * @param pvState The USB device instance.
+ * @param Mode The IOCtl mode.
+ * @param pUSBReq Pointer to the VBOXUSB request.
+ * @param pvBuf Pointer to the ring-3 URB.
+ * @param pcbDataOut Where to store the IOCtl OUT data size.
+ */
+LOCAL int vboxUsbSolarisProcessIOCtl(int iFunction, void *pvState, int Mode, PVBOXUSBREQ pUSBReq, void *pvBuf,
+ size_t *pcbDataOut)
+{
+ /* LogFunc((DEVICE_NAME ": vboxUsbSolarisProcessIOCtl: iFunction=%d pvState=%p pUSBReq=%p\n", iFunction, pvState, pUSBReq)); */
+
+ AssertPtrReturn(pvState, VERR_INVALID_PARAMETER);
+ vboxusb_state_t *pState = (vboxusb_state_t *)pvState;
+ size_t cbData = pUSBReq->cbData;
+ int rc;
+
+#define CHECKRET_MIN_SIZE(mnemonic, cbMin) \
+ do { \
+ if (RT_UNLIKELY(cbData < (cbMin))) \
+ { \
+ LogRel((DEVICE_NAME ": vboxUsbSolarisProcessIOCtl: " mnemonic ": cbData=%#zx (%zu) min is %#zx (%zu)\n", \
+ cbData, cbData, (size_t)(cbMin), (size_t)(cbMin))); \
+ return VERR_BUFFER_OVERFLOW; \
+ } \
+ if (RT_UNLIKELY((cbMin) != 0 && !RT_VALID_PTR(pvBuf))) \
+ { \
+ LogRel((DEVICE_NAME ": vboxUsbSolarisProcessIOCtl: " mnemonic ": Invalid pointer %p\n", pvBuf)); \
+ return VERR_INVALID_PARAMETER; \
+ } \
+ } while (0)
+
+ switch (iFunction)
+ {
+ case VBOXUSB_IOCTL_SEND_URB:
+ {
+ CHECKRET_MIN_SIZE("SEND_URB", sizeof(VBOXUSBREQ_URB));
+
+ PVBOXUSBREQ_URB pUrbReq = (PVBOXUSBREQ_URB)pvBuf;
+ rc = vboxUsbSolarisSendUrb(pState, pUrbReq, Mode);
+ *pcbDataOut = 0;
+ Log((DEVICE_NAME ": vboxUsbSolarisProcessIOCtl: SEND_URB returned %d\n", rc));
+ break;
+ }
+
+ case VBOXUSB_IOCTL_REAP_URB:
+ {
+ CHECKRET_MIN_SIZE("REAP_URB", sizeof(VBOXUSBREQ_URB));
+
+ PVBOXUSBREQ_URB pUrbReq = (PVBOXUSBREQ_URB)pvBuf;
+ rc = vboxUsbSolarisReapUrb(pState, pUrbReq, Mode);
+ *pcbDataOut = sizeof(VBOXUSBREQ_URB);
+ Log((DEVICE_NAME ": vboxUsbSolarisProcessIOCtl: REAP_URB returned %d\n", rc));
+ break;
+ }
+
+ case VBOXUSB_IOCTL_CLEAR_EP:
+ {
+ CHECKRET_MIN_SIZE("CLEAR_EP", sizeof(VBOXUSBREQ_CLEAR_EP));
+
+ PVBOXUSBREQ_CLEAR_EP pClearEpReq = (PVBOXUSBREQ_CLEAR_EP)pvBuf;
+ rc = vboxUsbSolarisClearEndPoint(pState, pClearEpReq->bEndpoint);
+ *pcbDataOut = 0;
+ Log((DEVICE_NAME ": vboxUsbSolarisProcessIOCtl: CLEAR_EP returned %d\n", rc));
+ break;
+ }
+
+ case VBOXUSB_IOCTL_SET_CONFIG:
+ {
+ CHECKRET_MIN_SIZE("SET_CONFIG", sizeof(VBOXUSBREQ_SET_CONFIG));
+
+ PVBOXUSBREQ_SET_CONFIG pSetCfgReq = (PVBOXUSBREQ_SET_CONFIG)pvBuf;
+ rc = vboxUsbSolarisSetConfig(pState, pSetCfgReq->bConfigValue);
+ *pcbDataOut = 0;
+ Log((DEVICE_NAME ": vboxUsbSolarisProcessIOCtl: SET_CONFIG returned %d\n", rc));
+ break;
+ }
+
+ case VBOXUSB_IOCTL_SET_INTERFACE:
+ {
+ CHECKRET_MIN_SIZE("SET_INTERFACE", sizeof(VBOXUSBREQ_SET_INTERFACE));
+
+ PVBOXUSBREQ_SET_INTERFACE pSetInterfaceReq = (PVBOXUSBREQ_SET_INTERFACE)pvBuf;
+ rc = vboxUsbSolarisSetInterface(pState, pSetInterfaceReq->bInterface, pSetInterfaceReq->bAlternate);
+ *pcbDataOut = 0;
+ Log((DEVICE_NAME ": vboxUsbSolarisProcessIOCtl: SET_INTERFACE returned %d\n", rc));
+ break;
+ }
+
+ case VBOXUSB_IOCTL_CLOSE_DEVICE:
+ {
+ CHECKRET_MIN_SIZE("CLOSE_DEVICE", sizeof(VBOXUSBREQ_CLOSE_DEVICE));
+
+ PVBOXUSBREQ_CLOSE_DEVICE pCloseDeviceReq = (PVBOXUSBREQ_CLOSE_DEVICE)pvBuf;
+ if ( pCloseDeviceReq->ResetLevel != VBOXUSB_RESET_LEVEL_REATTACH
+ || (Mode & FKIOCTL))
+ {
+ rc = vboxUsbSolarisCloseDevice(pState, pCloseDeviceReq->ResetLevel);
+ }
+ else
+ {
+ /* Userland IOCtls are not allowed to perform a reattach of the device. */
+ rc = VERR_NOT_SUPPORTED;
+ }
+ *pcbDataOut = 0;
+ Log((DEVICE_NAME ": vboxUsbSolarisProcessIOCtl: CLOSE_DEVICE returned %d\n", rc));
+ break;
+ }
+
+ case VBOXUSB_IOCTL_ABORT_PIPE:
+ {
+ CHECKRET_MIN_SIZE("ABORT_PIPE", sizeof(VBOXUSBREQ_ABORT_PIPE));
+
+ PVBOXUSBREQ_ABORT_PIPE pAbortPipeReq = (PVBOXUSBREQ_ABORT_PIPE)pvBuf;
+ rc = vboxUsbSolarisAbortPipe(pState, pAbortPipeReq->bEndpoint);
+ *pcbDataOut = 0;
+ Log((DEVICE_NAME ": vboxUsbSolarisProcessIOCtl: ABORT_PIPE returned %d\n", rc));
+ break;
+ }
+
+ case VBOXUSB_IOCTL_GET_CONFIG:
+ {
+ CHECKRET_MIN_SIZE("GET_CONFIG", sizeof(VBOXUSBREQ_GET_CONFIG));
+
+ PVBOXUSBREQ_GET_CONFIG pGetCfgReq = (PVBOXUSBREQ_GET_CONFIG)pvBuf;
+ rc = vboxUsbSolarisGetConfig(pState, &pGetCfgReq->bConfigValue);
+ *pcbDataOut = sizeof(VBOXUSBREQ_GET_CONFIG);
+ Log((DEVICE_NAME ": vboxUsbSolarisProcessIOCtl: GET_CONFIG returned %d\n", rc));
+ break;
+ }
+
+ case VBOXUSB_IOCTL_GET_VERSION:
+ {
+ CHECKRET_MIN_SIZE("GET_VERSION", sizeof(VBOXUSBREQ_GET_VERSION));
+
+ PVBOXUSBREQ_GET_VERSION pGetVersionReq = (PVBOXUSBREQ_GET_VERSION)pvBuf;
+ pGetVersionReq->u32Major = VBOXUSB_VERSION_MAJOR;
+ pGetVersionReq->u32Minor = VBOXUSB_VERSION_MINOR;
+ *pcbDataOut = sizeof(VBOXUSBREQ_GET_VERSION);
+ rc = VINF_SUCCESS;
+ Log((DEVICE_NAME ": vboxUsbSolarisProcessIOCtl: GET_VERSION returned %d\n", rc));
+ break;
+ }
+
+ default:
+ {
+ LogRel((DEVICE_NAME ": vboxUsbSolarisProcessIOCtl: Unknown request %#x\n", iFunction));
+ rc = VERR_NOT_SUPPORTED;
+ *pcbDataOut = 0;
+ break;
+ }
+ }
+
+ pUSBReq->cbData = *pcbDataOut;
+ return rc;
+}
+
+
+/**
+ * Initializes device power management.
+ *
+ * @param pState The USB device instance.
+ *
+ * @returns VBox status code.
+ */
+LOCAL int vboxUsbSolarisInitPower(vboxusb_state_t *pState)
+{
+ LogFunc((DEVICE_NAME ": vboxUsbSolarisInitPower: pState=%p\n", pState));
+
+ int rc = usb_handle_remote_wakeup(pState->pDip, USB_REMOTE_WAKEUP_ENABLE);
+ if (rc == USB_SUCCESS)
+ {
+ vboxusb_power_t *pPower = RTMemAllocZ(sizeof(vboxusb_power_t));
+ if (RT_LIKELY(pPower))
+ {
+ mutex_enter(&pState->Mtx);
+ pState->pPower = pPower;
+ pState->pPower->fPowerWakeup = false;
+ mutex_exit(&pState->Mtx);
+
+ uint_t PowerStates;
+ rc = usb_create_pm_components(pState->pDip, &PowerStates);
+ if (rc == USB_SUCCESS)
+ {
+ pState->pPower->fPowerWakeup = true;
+ pState->pPower->PowerLevel = USB_DEV_OS_FULL_PWR;
+ pState->pPower->PowerStates = PowerStates;
+
+ rc = pm_raise_power(pState->pDip, 0 /* component */, USB_DEV_OS_FULL_PWR);
+
+ if (rc != DDI_SUCCESS)
+ {
+ LogRel((DEVICE_NAME ": vboxUsbSolarisInitPower: Failed to raise power level usb(%#x,%#x)\n",
+ pState->pDevDesc->dev_descr->idVendor, pState->pDevDesc->dev_descr->idProduct));
+ }
+ }
+ else
+ Log((DEVICE_NAME ": vboxUsbSolarisInitPower: Failed to create power components\n"));
+
+ return VINF_SUCCESS;
+ }
+ else
+ rc = VERR_NO_MEMORY;
+ }
+ else
+ {
+ Log((DEVICE_NAME ": vboxUsbSolarisInitPower: Failed to enable remote wakeup, No PM!\n"));
+ rc = VINF_SUCCESS;
+ }
+
+ return rc;
+}
+
+
+/**
+ * Destroys device power management.
+ *
+ * @param pState The USB device instance.
+ * @remarks Requires the device state mutex to be held.
+ */
+LOCAL void vboxUsbSolarisDestroyPower(vboxusb_state_t *pState)
+{
+ LogFunc((DEVICE_NAME ": vboxUsbSolarisDestroyPower: pState=%p\n", pState));
+
+ if (pState->pPower)
+ {
+ mutex_exit(&pState->Mtx);
+ vboxUsbSolarisPowerBusy(pState);
+ mutex_enter(&pState->Mtx);
+
+ int rc = -1;
+ if ( pState->pPower->fPowerWakeup
+ && pState->DevState != USB_DEV_DISCONNECTED)
+ {
+ mutex_exit(&pState->Mtx);
+ rc = pm_raise_power(pState->pDip, 0 /* component */, USB_DEV_OS_FULL_PWR);
+ if (rc != DDI_SUCCESS)
+ Log((DEVICE_NAME ": vboxUsbSolarisDestroyPower: Raising power failed! rc=%d\n", rc));
+
+ rc = usb_handle_remote_wakeup(pState->pDip, USB_REMOTE_WAKEUP_DISABLE);
+ if (rc != DDI_SUCCESS)
+ Log((DEVICE_NAME ": vboxUsbSolarisDestroyPower: Failed to disable remote wakeup\n"));
+ }
+ else
+ mutex_exit(&pState->Mtx);
+
+ rc = pm_lower_power(pState->pDip, 0 /* component */, USB_DEV_OS_PWR_OFF);
+ if (rc != DDI_SUCCESS)
+ Log((DEVICE_NAME ": vboxUsbSolarisDestroyPower: Lowering power failed! rc=%d\n", rc));
+
+ vboxUsbSolarisPowerIdle(pState);
+ mutex_enter(&pState->Mtx);
+ RTMemFree(pState->pPower);
+ pState->pPower = NULL;
+ }
+}
+
+
+/**
+ * Converts Solaris' USBA URB status to VBox's USB URB status.
+ *
+ * @param Status Solaris USBA USB URB status.
+ *
+ * @returns VBox USB URB status.
+ */
+LOCAL VUSBSTATUS vboxUsbSolarisGetUrbStatus(usb_cr_t Status)
+{
+ switch (Status)
+ {
+ case USB_CR_OK: return VUSBSTATUS_OK;
+ case USB_CR_CRC: return VUSBSTATUS_CRC;
+ case USB_CR_DEV_NOT_RESP: return VUSBSTATUS_DNR;
+ case USB_CR_DATA_UNDERRUN: return VUSBSTATUS_DATA_UNDERRUN;
+ case USB_CR_DATA_OVERRUN: return VUSBSTATUS_DATA_OVERRUN;
+ case USB_CR_STALL: return VUSBSTATUS_STALL;
+ /*
+ case USB_CR_BITSTUFFING:
+ case USB_CR_DATA_TOGGLE_MM:
+ case USB_CR_PID_CHECKFAILURE:
+ case USB_CR_UNEXP_PID:
+ case USB_CR_BUFFER_OVERRUN:
+ case USB_CR_BUFFER_UNDERRUN:
+ case USB_CR_TIMEOUT:
+ case USB_CR_NOT_ACCESSED:
+ case USB_CR_NO_RESOURCES:
+ case USB_CR_UNSPECIFIED_ERR:
+ case USB_CR_STOPPED_POLLING:
+ case USB_CR_PIPE_CLOSING:
+ case USB_CR_PIPE_RESET:
+ case USB_CR_NOT_SUPPORTED:
+ case USB_CR_FLUSHED:
+ case USB_CR_HC_HARDWARE_ERR:
+ */
+ default: return VUSBSTATUS_INVALID;
+ }
+}
+
+
+/**
+ * Converts Solaris' USBA error code to VBox's error code.
+ *
+ * @param UsbRc Solaris USBA error code.
+ *
+ * @returns VBox error code.
+ */
+static int vboxUsbSolarisToVBoxRC(int UsbRc)
+{
+ switch (UsbRc)
+ {
+ case USB_SUCCESS: return VINF_SUCCESS;
+ case USB_INVALID_ARGS: return VERR_INVALID_PARAMETER;
+ case USB_INVALID_PIPE: return VERR_BAD_PIPE;
+ case USB_INVALID_CONTEXT: return VERR_INVALID_CONTEXT;
+ case USB_BUSY: return VERR_PIPE_BUSY;
+ case USB_PIPE_ERROR: return VERR_PIPE_IO_ERROR;
+ /*
+ case USB_FAILURE:
+ case USB_NO_RESOURCES:
+ case USB_NO_BANDWIDTH:
+ case USB_NOT_SUPPORTED:
+ case USB_PIPE_ERROR:
+ case USB_NO_FRAME_NUMBER:
+ case USB_INVALID_START_FRAME:
+ case USB_HC_HARDWARE_ERROR:
+ case USB_INVALID_REQUEST:
+ case USB_INVALID_VERSION:
+ case USB_INVALID_PERM:
+ */
+ default: return VERR_GENERAL_FAILURE;
+ }
+}
+
+
+/**
+ * Converts Solaris' USBA device state to VBox's error code.
+ *
+ * @param uDeviceState The USB device state to convert.
+ *
+ * @returns VBox error code.
+ */
+static int vboxUsbSolarisDeviceState(uint8_t uDeviceState)
+{
+ switch (uDeviceState)
+ {
+ case USB_DEV_ONLINE: return VINF_SUCCESS;
+ case USB_DEV_SUSPENDED: return VERR_VUSB_DEVICE_IS_SUSPENDED;
+ case USB_DEV_DISCONNECTED:
+ case USB_DEV_PWRED_DOWN: return VERR_VUSB_DEVICE_NOT_ATTACHED;
+ default: return VERR_GENERAL_FAILURE;
+ }
+}
+
+
+/**
+ * Checks if the device is a USB device.
+ *
+ * @param pDip Pointer to this device info. structure.
+ *
+ * @returns If this is really a USB device returns true, otherwise false.
+ */
+LOCAL bool vboxUsbSolarisIsUSBDevice(dev_info_t *pDip)
+{
+ int rc = DDI_FAILURE;
+
+ /*
+ * Check device for "usb" compatible property, root hubs->device would likely mean parent has no "usb" property.
+ */
+ char **ppszCompatible = NULL;
+ uint_t cCompatible;
+ rc = ddi_prop_lookup_string_array(DDI_DEV_T_ANY, pDip, DDI_PROP_DONTPASS, "compatible", &ppszCompatible, &cCompatible);
+ if (RT_LIKELY(rc == DDI_PROP_SUCCESS))
+ {
+ while (cCompatible--)
+ {
+ Log((DEVICE_NAME ": vboxUsbSolarisIsUSBDevice: Compatible[%d]=%s\n", cCompatible, ppszCompatible[cCompatible]));
+ if (!strncmp(ppszCompatible[cCompatible], "usb", 3))
+ {
+ Log((DEVICE_NAME ": vboxUsbSolarisIsUSBDevice: Verified device as USB. pszCompatible=%s\n",
+ ppszCompatible[cCompatible]));
+ ddi_prop_free(ppszCompatible);
+ return true;
+ }
+ }
+
+ ddi_prop_free(ppszCompatible);
+ ppszCompatible = NULL;
+ }
+ else
+ Log((DEVICE_NAME ": vboxUsbSolarisIsUSBDevice: USB property lookup failed, rc=%d\n", rc));
+
+ /*
+ * Check parent for "usb" compatible property.
+ */
+ dev_info_t *pParentDip = ddi_get_parent(pDip);
+ if (pParentDip)
+ {
+ rc = ddi_prop_lookup_string_array(DDI_DEV_T_ANY, pParentDip, DDI_PROP_DONTPASS, "compatible", &ppszCompatible,
+ &cCompatible);
+ if (RT_LIKELY(rc == DDI_PROP_SUCCESS))
+ {
+ while (cCompatible--)
+ {
+ Log((DEVICE_NAME ": vboxUsbSolarisIsUSBDevice: Parent compatible[%d]=%s\n", cCompatible,
+ ppszCompatible[cCompatible]));
+ if (!strncmp(ppszCompatible[cCompatible], "usb", 3))
+ {
+ Log((DEVICE_NAME ": vboxUsbSolarisIsUSBDevice: Verified device as USB. parent pszCompatible=%s\n",
+ ppszCompatible[cCompatible]));
+ ddi_prop_free(ppszCompatible);
+ return true;
+ }
+ }
+
+ ddi_prop_free(ppszCompatible);
+ ppszCompatible = NULL;
+ }
+ else
+ Log((DEVICE_NAME ": vboxUsbSolarisIsUSBDevice: USB parent property lookup failed. rc=%d\n", rc));
+ }
+ else
+ Log((DEVICE_NAME ": vboxUsbSolarisIsUSBDevice: Failed to obtain parent device for property lookup\n"));
+
+ return false;
+}
+
+
+/**
+ * Submits a URB.
+ *
+ * @param pState The USB device instance.
+ * @param pUrbReq Pointer to the VBox USB URB.
+ * @param Mode The IOCtl mode.
+ *
+ * @returns VBox error code.
+ */
+LOCAL int vboxUsbSolarisSendUrb(vboxusb_state_t *pState, PVBOXUSBREQ_URB pUrbReq, int Mode)
+{
+ int iEpIndex = VBOXUSB_GET_EP_INDEX(pUrbReq->bEndpoint);
+ Assert(iEpIndex >= 0 && iEpIndex < RT_ELEMENTS(pState->aEps));
+ vboxusb_ep_t *pEp = &pState->aEps[iEpIndex];
+ AssertPtrReturn(pEp, VERR_INVALID_POINTER);
+ Assert(pUrbReq);
+
+#if 0
+ LogFunc((DEVICE_NAME ": vboxUsbSolarisSendUrb: pState=%p pUrbReq=%p bEndpoint=%#x[%d] enmDir=%#x enmType=%#x "
+ "cbData=%d pvData=%p\n", pState, pUrbReq, pUrbReq->bEndpoint, iEpIndex, pUrbReq->enmDir,
+ pUrbReq->enmType, pUrbReq->cbData, pUrbReq->pvData));
+#endif
+
+ if (RT_UNLIKELY(!pUrbReq->pvData))
+ {
+ LogRel((DEVICE_NAME ": vboxUsbSolarisSendUrb: Invalid request - No data\n"));
+ return VERR_INVALID_POINTER;
+ }
+
+ /*
+ * Allocate message block & copy userspace buffer for host to device Xfers and for
+ * Control Xfers (since input has Setup header that needs copying).
+ */
+ mblk_t *pMsg = NULL;
+ int rc = VINF_SUCCESS;
+ if ( pUrbReq->enmDir == VUSBDIRECTION_OUT
+ || pUrbReq->enmType == VUSBXFERTYPE_MSG)
+ {
+ pMsg = allocb(pUrbReq->cbData, BPRI_HI);
+ if (RT_UNLIKELY(!pMsg))
+ {
+ LogRel((DEVICE_NAME ": vboxUsbSolarisSendUrb: Failed to allocate %u bytes\n", pUrbReq->cbData));
+ return VERR_NO_MEMORY;
+ }
+
+ rc = ddi_copyin(pUrbReq->pvData, pMsg->b_wptr, pUrbReq->cbData, Mode);
+ if (RT_UNLIKELY(rc))
+ {
+ LogRel((DEVICE_NAME ": vboxUsbSolarisSendUrb: ddi_copyin failed! rc=%d\n", rc));
+ freemsg(pMsg);
+ return VERR_NO_MEMORY;
+ }
+
+ pMsg->b_wptr += pUrbReq->cbData;
+ }
+
+ mutex_enter(&pState->Mtx);
+ rc = vboxUsbSolarisDeviceState(pState->DevState);
+ if (!pState->fDefaultPipeOpen) /* Required for Isoc. IN Xfers which don't Xfer through the pipe after polling starts */
+ rc = VERR_VUSB_DEVICE_NOT_ATTACHED;
+ if (RT_SUCCESS(rc))
+ {
+ /*
+ * Open the pipe if needed.
+ */
+ rc = vboxUsbSolarisOpenPipe(pState, pEp);
+ if (RT_UNLIKELY(RT_FAILURE(rc)))
+ {
+ mutex_exit(&pState->Mtx);
+ freemsg(pMsg);
+ LogRel((DEVICE_NAME ": vboxUsbSolarisSendUrb: OpenPipe failed! pState=%p pUrbReq=%p bEndpoint=%#x enmDir=%#x "
+ "enmType=%#x cbData=%d pvData=%p rc=%d\n", pState, pUrbReq, pUrbReq->bEndpoint, pUrbReq->enmDir,
+ pUrbReq->enmType, pUrbReq->cbData, pUrbReq->pvData, rc));
+ return VERR_BAD_PIPE;
+ }
+
+ mutex_exit(&pState->Mtx);
+
+ vboxusb_urb_t *pUrb = NULL;
+ if ( pUrbReq->enmType == VUSBXFERTYPE_ISOC
+ && pUrbReq->enmDir == VUSBDIRECTION_IN)
+ pUrb = vboxUsbSolarisGetIsocInUrb(pState, pUrbReq);
+ else
+ pUrb = vboxUsbSolarisQueueUrb(pState, pUrbReq, pMsg);
+
+ if (RT_LIKELY(pUrb))
+ {
+ switch (pUrb->enmType)
+ {
+ case VUSBXFERTYPE_MSG:
+ {
+ rc = vboxUsbSolarisCtrlXfer(pState, pEp, pUrb);
+ break;
+ }
+
+ case VUSBXFERTYPE_BULK:
+ {
+ rc = vboxUsbSolarisBulkXfer(pState, pEp, pUrb);
+ break;
+ }
+
+ case VUSBXFERTYPE_INTR:
+ {
+ rc = vboxUsbSolarisIntrXfer(pState, pEp, pUrb);
+ break;
+ }
+
+ case VUSBXFERTYPE_ISOC:
+ {
+ rc = vboxUsbSolarisIsocXfer(pState, pEp, pUrb);
+ break;
+ }
+
+ default:
+ {
+ LogRelMax(5, (DEVICE_NAME ": vboxUsbSolarisSendUrb: URB type unsupported %d\n", pUrb->enmType));
+ rc = VERR_NOT_SUPPORTED;
+ break;
+ }
+ }
+
+ if (RT_FAILURE(rc))
+ {
+ mutex_enter(&pState->Mtx);
+ freemsg(pUrb->pMsg);
+ pUrb->pMsg = NULL;
+ pMsg = NULL;
+
+ if ( pUrb->enmType == VUSBXFERTYPE_ISOC
+ && pUrb->enmDir == VUSBDIRECTION_IN)
+ {
+ RTMemFree(pUrb);
+ pUrb = NULL;
+ }
+ else
+ {
+ /*
+ * Xfer failed, move URB back to the free list.
+ */
+ list_remove(&pState->hInflightUrbs, pUrb);
+ Assert(pState->cInflightUrbs > 0);
+ --pState->cInflightUrbs;
+
+ pUrb->enmState = VBOXUSB_URB_STATE_FREE;
+ Assert(!pUrb->pMsg);
+ list_insert_head(&pState->hFreeUrbs, pUrb);
+ ++pState->cFreeUrbs;
+ }
+ mutex_exit(&pState->Mtx);
+ }
+ }
+ else
+ {
+ LogRel((DEVICE_NAME ": vboxUsbSolarisSendUrb: Failed to queue URB\n"));
+ rc = VERR_NO_MEMORY;
+ freemsg(pMsg);
+ }
+ }
+ else
+ {
+ mutex_exit(&pState->Mtx);
+ freemsg(pMsg);
+ }
+
+ return rc;
+}
+
+
+/**
+ * Reaps a completed URB.
+ *
+ * @param pState The USB device instance.
+ * @param pUrbReq Pointer to the VBox USB URB.
+ * @param Mode The IOCtl mode.
+ *
+ * @returns VBox error code.
+ */
+LOCAL int vboxUsbSolarisReapUrb(vboxusb_state_t *pState, PVBOXUSBREQ_URB pUrbReq, int Mode)
+{
+ /* LogFunc((DEVICE_NAME ": vboxUsbSolarisReapUrb: pState=%p pUrbReq=%p\n", pState, pUrbReq)); */
+
+ AssertPtrReturn(pUrbReq, VERR_INVALID_POINTER);
+
+ int rc = VINF_SUCCESS;
+ mutex_enter(&pState->Mtx);
+ rc = vboxUsbSolarisDeviceState(pState->DevState);
+ if (!pState->fDefaultPipeOpen)
+ rc = VERR_VUSB_DEVICE_NOT_ATTACHED;
+ if (RT_SUCCESS(rc))
+ {
+ vboxusb_urb_t *pUrb = list_remove_head(&pState->hLandedUrbs);
+ if (pUrb)
+ {
+ Assert(pState->cLandedUrbs > 0);
+ --pState->cLandedUrbs;
+ }
+
+ /*
+ * It is safe to access pUrb->pMsg outside the state mutex because this is from the landed URB list
+ * and not the inflight URB list.
+ */
+ mutex_exit(&pState->Mtx);
+ if (pUrb)
+ {
+ /*
+ * Copy the URB which will then be copied to user-space.
+ */
+ pUrbReq->pvUrbR3 = pUrb->pvUrbR3;
+ pUrbReq->bEndpoint = pUrb->bEndpoint;
+ pUrbReq->enmType = pUrb->enmType;
+ pUrbReq->enmDir = pUrb->enmDir;
+ pUrbReq->enmStatus = pUrb->enmStatus;
+ pUrbReq->pvData = (void *)pUrb->pvDataR3;
+ pUrbReq->cbData = pUrb->cbDataR3;
+
+ if (RT_LIKELY(pUrb->pMsg))
+ {
+ /*
+ * Copy the message back into the user buffer.
+ */
+ if (RT_LIKELY(pUrb->pvDataR3 != NIL_RTR3PTR))
+ {
+ Assert(!pUrb->pMsg->b_cont); /* We really should have a single message block always. */
+ size_t cbData = RT_MIN(MBLKL(pUrb->pMsg), pUrb->cbDataR3);
+ pUrbReq->cbData = cbData;
+
+ if (RT_LIKELY(cbData))
+ {
+ rc = ddi_copyout(pUrb->pMsg->b_rptr, (void *)pUrbReq->pvData, cbData, Mode);
+ if (RT_UNLIKELY(rc))
+ {
+ LogRel((DEVICE_NAME ": vboxUsbSolarisReapUrb: ddi_copyout failed! rc=%d\n", rc));
+ pUrbReq->enmStatus = VUSBSTATUS_INVALID;
+ }
+ }
+
+ Log((DEVICE_NAME ": vboxUsbSolarisReapUrb: pvUrbR3=%p pvDataR3=%p cbData=%d\n", pUrbReq->pvUrbR3,
+ pUrbReq->pvData, pUrbReq->cbData));
+ }
+ else
+ {
+ pUrbReq->cbData = 0;
+ rc = VERR_INVALID_POINTER;
+ LogRel((DEVICE_NAME ": vboxUsbSolarisReapUrb: Missing pvDataR3!!\n"));
+ }
+
+ /*
+ * Free buffer allocated in vboxUsbSolarisSendUrb or vboxUsbSolaris[Ctrl|Bulk|Intr]Xfer().
+ */
+ freemsg(pUrb->pMsg);
+ pUrb->pMsg = NULL;
+ }
+ else
+ {
+ if ( pUrb->enmType == VUSBXFERTYPE_ISOC
+ && pUrb->enmDir == VUSBDIRECTION_IN)
+ {
+ pUrbReq->enmStatus = VUSBSTATUS_INVALID;
+ pUrbReq->cbData = 0;
+ }
+ }
+
+ /*
+ * Copy Isoc packet descriptors.
+ */
+ if (pUrb->enmType == VUSBXFERTYPE_ISOC)
+ {
+ AssertCompile(sizeof(pUrbReq->aIsocPkts) == sizeof(pUrb->aIsocPkts));
+ pUrbReq->cIsocPkts = pUrb->cIsocPkts;
+
+ for (unsigned i = 0; i < pUrb->cIsocPkts; i++)
+ {
+ pUrbReq->aIsocPkts[i].cbPkt = pUrb->aIsocPkts[i].cbPkt;
+ pUrbReq->aIsocPkts[i].cbActPkt = pUrb->aIsocPkts[i].cbActPkt;
+ pUrbReq->aIsocPkts[i].enmStatus = pUrb->aIsocPkts[i].enmStatus;
+ }
+
+ if (pUrb->enmDir == VUSBDIRECTION_IN)
+ {
+ RTMemFree(pUrb);
+ pUrb = NULL;
+ }
+ }
+
+ if (pUrb)
+ {
+ /*
+ * Add URB back to the free list.
+ */
+ Assert(!pUrb->pMsg);
+ pUrb->cbDataR3 = 0;
+ pUrb->pvDataR3 = NIL_RTR3PTR;
+ pUrb->enmState = VBOXUSB_URB_STATE_FREE;
+ mutex_enter(&pState->Mtx);
+ list_insert_head(&pState->hFreeUrbs, pUrb);
+ ++pState->cFreeUrbs;
+ mutex_exit(&pState->Mtx);
+ }
+ }
+ else
+ {
+ pUrbReq->pvUrbR3 = NULL;
+ pUrbReq->cbData = 0;
+ pUrbReq->pvData = NULL;
+ pUrbReq->enmStatus = VUSBSTATUS_INVALID;
+ }
+ }
+ else
+ mutex_exit(&pState->Mtx);
+
+ return rc;
+}
+
+
+/**
+ * Clears a pipe (CLEAR_FEATURE).
+ *
+ * @param pState The USB device instance.
+ * @param bEndpoint The Endpoint address.
+ *
+ * @returns VBox error code.
+ */
+LOCAL int vboxUsbSolarisClearEndPoint(vboxusb_state_t *pState, uint8_t bEndpoint)
+{
+ LogFunc((DEVICE_NAME ": vboxUsbSolarisClearEndPoint: pState=%p bEndpoint=%#x\n", pState, bEndpoint));
+
+ mutex_enter(&pState->Mtx);
+ int rc = vboxUsbSolarisDeviceState(pState->DevState);
+ if (RT_SUCCESS(rc))
+ {
+ int iEpIndex = VBOXUSB_GET_EP_INDEX(bEndpoint);
+ Assert(iEpIndex >= 0 && iEpIndex < RT_ELEMENTS(pState->aEps));
+ vboxusb_ep_t *pEp = &pState->aEps[iEpIndex];
+ if (RT_LIKELY(pEp))
+ {
+ /*
+ * Check if the endpoint is open to be cleared.
+ */
+ if (pEp->pPipe)
+ {
+ mutex_exit(&pState->Mtx);
+
+ /*
+ * Synchronous reset pipe.
+ */
+ usb_pipe_reset(pState->pDip, pEp->pPipe,
+ USB_FLAGS_SLEEP, /* Synchronous */
+ NULL, /* Completion callback */
+ NULL); /* Exception callback */
+
+ mutex_enter(&pState->Mtx);
+
+ Log((DEVICE_NAME ": vboxUsbSolarisClearEndPoint: bEndpoint=%#x[%d] returns %d\n", bEndpoint, iEpIndex, rc));
+
+ rc = VINF_SUCCESS;
+ }
+ else
+ {
+ Log((DEVICE_NAME ": vboxUsbSolarisClearEndPoint: Not opened to be cleared. Faking success. bEndpoint=%#x\n",
+ bEndpoint));
+ rc = VINF_SUCCESS;
+ }
+ }
+ else
+ {
+ LogRel((DEVICE_NAME ": vboxUsbSolarisClearEndPoint: Endpoint missing! bEndpoint=%#x[%d]\n", bEndpoint, iEpIndex));
+ rc = VERR_GENERAL_FAILURE;
+ }
+ }
+ else
+ Log((DEVICE_NAME ": vboxUsbSolarisClearEndPoint: Device not online, state=%d\n", pState->DevState));
+
+ mutex_exit(&pState->Mtx);
+ return rc;
+}
+
+
+/**
+ * Sets configuration (SET_CONFIGURATION)
+ *
+ * @param pState The USB device instance.
+ * @param bConfig The Configuration.
+ *
+ * @returns VBox error code.
+ */
+LOCAL int vboxUsbSolarisSetConfig(vboxusb_state_t *pState, uint8_t bConfig)
+{
+ LogFunc((DEVICE_NAME ": vboxUsbSolarisSetConfig: pState=%p bConfig=%u\n", pState, bConfig));
+
+ mutex_enter(&pState->Mtx);
+ int rc = vboxUsbSolarisDeviceState(pState->DevState);
+ if (RT_SUCCESS(rc))
+ {
+ vboxUsbSolarisCloseAllPipes(pState, false /* ControlPipe */);
+ int iCfgIndex = vboxUsbSolarisGetConfigIndex(pState, bConfig);
+
+ if ( iCfgIndex >= 0
+ && iCfgIndex < pState->pDevDesc->dev_n_cfg)
+ {
+ /*
+ * Switch Config synchronously.
+ */
+ mutex_exit(&pState->Mtx);
+ rc = usb_set_cfg(pState->pDip, (uint_t)iCfgIndex, USB_FLAGS_SLEEP, NULL /* callback */, NULL /* callback data */);
+ mutex_enter(&pState->Mtx);
+
+ if (rc == USB_SUCCESS)
+ {
+ int rc2 = vboxUsbSolarisInitEpsForCfg(pState);
+ AssertRC(rc2); NOREF(rc2);
+ rc = VINF_SUCCESS;
+ }
+ else
+ {
+ LogRel((DEVICE_NAME ": vboxUsbSolarisSetConfig: usb_set_cfg failed for iCfgIndex=%#x bConfig=%u rc=%d\n",
+ iCfgIndex, bConfig, rc));
+ rc = vboxUsbSolarisToVBoxRC(rc);
+ }
+ }
+ else
+ {
+ LogRel((DEVICE_NAME ": vboxUsbSolarisSetConfig: Invalid iCfgIndex=%d bConfig=%u\n", iCfgIndex, bConfig));
+ rc = VERR_OUT_OF_RANGE;
+ }
+ }
+
+ mutex_exit(&pState->Mtx);
+
+ return rc;
+}
+
+
+/**
+ * Gets configuration (GET_CONFIGURATION)
+ *
+ * @param pState The USB device instance.
+ * @param pbConfig Where to store the Configuration.
+ *
+ * @returns VBox error code.
+ */
+LOCAL int vboxUsbSolarisGetConfig(vboxusb_state_t *pState, uint8_t *pbConfig)
+{
+ LogFunc((DEVICE_NAME ": vboxUsbSolarisGetConfig: pState=%p pbConfig=%p\n", pState, pbConfig));
+ AssertPtrReturn(pbConfig, VERR_INVALID_POINTER);
+
+ /*
+ * Get Config synchronously.
+ */
+ uint_t bConfig;
+ int rc = usb_get_cfg(pState->pDip, &bConfig, USB_FLAGS_SLEEP);
+ if (RT_LIKELY(rc == USB_SUCCESS))
+ {
+ *pbConfig = bConfig;
+ rc = VINF_SUCCESS;
+ }
+ else
+ {
+ LogRel((DEVICE_NAME ": vboxUsbSolarisGetConfig: Failed, rc=%d\n", rc));
+ rc = vboxUsbSolarisToVBoxRC(rc);
+ }
+
+ Log((DEVICE_NAME ": vboxUsbSolarisGetConfig: Returns %d bConfig=%u\n", rc, *pbConfig));
+ return rc;
+}
+
+
+/**
+ * Sets interface (SET_INTERFACE) and alternate.
+ *
+ * @param pState The USB device instance.
+ * @param bIf The Interface.
+ * @param bAlt The Alternate setting.
+ *
+ * @returns VBox error code.
+ */
+LOCAL int vboxUsbSolarisSetInterface(vboxusb_state_t *pState, uint8_t bIf, uint8_t bAlt)
+{
+ LogFunc((DEVICE_NAME ": vboxUsbSolarisSetInterface: pState=%p bIf=%#x bAlt=%#x\n", pState, bIf, bAlt));
+
+ mutex_enter(&pState->Mtx);
+ int rc = vboxUsbSolarisDeviceState(pState->DevState);
+ if (RT_SUCCESS(rc))
+ {
+ /*
+ * Set Interface & Alt setting synchronously.
+ */
+ mutex_exit(&pState->Mtx);
+ rc = usb_set_alt_if(pState->pDip, bIf, bAlt, USB_FLAGS_SLEEP, NULL /* callback */, NULL /* callback data */);
+ mutex_enter(&pState->Mtx);
+
+ if (rc == USB_SUCCESS)
+ {
+ Log((DEVICE_NAME ": vboxUsbSolarisSetInterface: Success, bIf=%#x bAlt=%#x\n", bIf, bAlt, rc));
+ int rc2 = vboxUsbSolarisInitEpsForIfAlt(pState, bIf, bAlt);
+ AssertRC(rc2); NOREF(rc2);
+ rc = VINF_SUCCESS;
+ }
+ else
+ {
+ LogRel((DEVICE_NAME ": vboxUsbSolarisSetInterface: usb_set_alt_if failed for bIf=%#x bAlt=%#x rc=%d\n", bIf, bAlt, rc));
+ rc = vboxUsbSolarisToVBoxRC(rc);
+ }
+ }
+
+ mutex_exit(&pState->Mtx);
+
+ return rc;
+}
+
+
+/**
+ * Closes the USB device and optionally resets it.
+ *
+ * @param pState The USB device instance.
+ * @param enmReset The reset level.
+ *
+ * @returns VBox error code.
+ */
+LOCAL int vboxUsbSolarisCloseDevice(vboxusb_state_t *pState, VBOXUSB_RESET_LEVEL enmReset)
+{
+ LogFunc((DEVICE_NAME ": vboxUsbSolarisCloseDevice: pState=%p enmReset=%d\n", pState, enmReset));
+
+ mutex_enter(&pState->Mtx);
+ int rc = vboxUsbSolarisDeviceState(pState->DevState);
+
+ if (enmReset == VBOXUSB_RESET_LEVEL_CLOSE)
+ vboxUsbSolarisCloseAllPipes(pState, true /* ControlPipe */);
+ else
+ vboxUsbSolarisCloseAllPipes(pState, false /* ControlPipe */);
+
+ mutex_exit(&pState->Mtx);
+
+ if (RT_SUCCESS(rc))
+ {
+ switch (enmReset)
+ {
+ case VBOXUSB_RESET_LEVEL_REATTACH:
+ rc = usb_reset_device(pState->pDip, USB_RESET_LVL_REATTACH);
+ break;
+
+ case VBOXUSB_RESET_LEVEL_SOFT:
+ rc = usb_reset_device(pState->pDip, USB_RESET_LVL_DEFAULT);
+ break;
+
+ default:
+ rc = USB_SUCCESS;
+ break;
+ }
+
+ rc = vboxUsbSolarisToVBoxRC(rc);
+ }
+
+ Log((DEVICE_NAME ": vboxUsbSolarisCloseDevice: Returns %d\n", rc));
+ return rc;
+}
+
+
+/**
+ * Aborts pending requests and reset the pipe.
+ *
+ * @param pState The USB device instance.
+ * @param bEndpoint The Endpoint address.
+ *
+ * @returns VBox error code.
+ */
+LOCAL int vboxUsbSolarisAbortPipe(vboxusb_state_t *pState, uint8_t bEndpoint)
+{
+ LogFunc((DEVICE_NAME ": vboxUsbSolarisAbortPipe: pState=%p bEndpoint=%#x\n", pState, bEndpoint));
+
+ mutex_enter(&pState->Mtx);
+ int rc = vboxUsbSolarisDeviceState(pState->DevState);
+ if (RT_SUCCESS(rc))
+ {
+ int iEpIndex = VBOXUSB_GET_EP_INDEX(bEndpoint);
+ Assert(iEpIndex >= 0 && iEpIndex < RT_ELEMENTS(pState->aEps));
+ vboxusb_ep_t *pEp = &pState->aEps[iEpIndex];
+ if (RT_LIKELY(pEp))
+ {
+ if (pEp->pPipe)
+ {
+ /*
+ * Aborting requests not supported for the default control pipe.
+ */
+ if ((pEp->EpDesc.bEndpointAddress & USB_EP_NUM_MASK) == 0)
+ {
+ mutex_exit(&pState->Mtx);
+ LogRel((DEVICE_NAME ": vboxUsbSolarisAbortPipe: Cannot reset default control pipe\n"));
+ return VERR_NOT_SUPPORTED;
+ }
+
+ mutex_exit(&pState->Mtx);
+ usb_pipe_reset(pState->pDip, pEp->pPipe,
+ USB_FLAGS_SLEEP, /* Synchronous */
+ NULL, /* Completion callback */
+ NULL); /* Callback's parameter */
+
+ /*
+ * Allow pending async requests to complete.
+ */
+ /** @todo this is most likely not required. */
+ rc = usb_pipe_drain_reqs(pState->pDip, pEp->pPipe,
+ USB_FLAGS_SLEEP, /* Synchronous */
+ 5, /* Timeout (seconds) */
+ NULL, /* Completion callback */
+ NULL); /* Callback's parameter */
+
+ mutex_enter(&pState->Mtx);
+
+ Log((DEVICE_NAME ": vboxUsbSolarisAbortPipe: usb_pipe_drain_reqs returns %d\n", rc));
+ rc = vboxUsbSolarisToVBoxRC(rc);
+ }
+ else
+ {
+ LogRel((DEVICE_NAME ": vboxUsbSolarisAbortPipe: pipe not open. bEndpoint=%#x\n", bEndpoint));
+ rc = VERR_PIPE_IO_ERROR;
+ }
+ }
+ else
+ {
+ LogRel((DEVICE_NAME ": vboxUsbSolarisAbortPipe: Invalid pipe bEndpoint=%#x[%d]\n", bEndpoint, iEpIndex));
+ rc = VERR_INVALID_HANDLE;
+ }
+ }
+
+ mutex_exit(&pState->Mtx);
+
+ LogFunc((DEVICE_NAME ": vboxUsbSolarisAbortPipe: Returns %d\n", rc));
+ return rc;
+}
+
+
+/**
+ * Initializes an Endpoint.
+ *
+ * @param pState The USB device instance.
+ * @param pEpData The Endpoint data (NULL implies the default
+ * endpoint).
+ *
+ * @returns VBox error code.
+ */
+LOCAL int vboxUsbSolarisInitEp(vboxusb_state_t *pState, usb_ep_data_t *pEpData)
+{
+ LogFunc((DEVICE_NAME ": vboxUsbSolarisInitEp: pState=%p pEpData=%p", pState, pEpData));
+
+ /*
+ * Is this the default endpoint?
+ */
+ usb_ep_descr_t *pEpDesc = NULL;
+ vboxusb_ep_t *pEp = NULL;
+ int iEpIndex;
+ if (!pEpData)
+ {
+ iEpIndex = 0;
+ pEpDesc = &g_VBoxUSBSolarisDefaultEpDesc;
+ }
+ else
+ {
+ iEpIndex = VBOXUSB_GET_EP_INDEX(pEpData->ep_descr.bEndpointAddress);
+ pEpDesc = (usb_ep_descr_t *)((uint8_t *)pEpData + g_offUsbEpDataDescr);
+ }
+
+ Assert(iEpIndex >= 0 && iEpIndex < RT_ELEMENTS(pState->aEps));
+ pEp = &pState->aEps[iEpIndex];
+
+ /*
+ * Initialize the endpoint.
+ */
+ pEp->EpDesc = *pEpDesc;
+ if (!pEp->fInitialized)
+ {
+ pEp->pPipe = NULL;
+ bzero(&pEp->PipePolicy, sizeof(pEp->PipePolicy));
+ pEp->PipePolicy.pp_max_async_reqs = VBOXUSB_MAX_PIPE_ASYNC_REQS;
+ pEp->fIsocPolling = false;
+ list_create(&pEp->hIsocInUrbs, sizeof(vboxusb_urb_t), offsetof(vboxusb_urb_t, hListLink));
+ pEp->cIsocInUrbs = 0;
+ list_create(&pEp->hIsocInLandedReqs, sizeof(vboxusb_isoc_req_t), offsetof(vboxusb_isoc_req_t, hListLink));
+ pEp->cbIsocInLandedReqs = 0;
+ pEp->cbMaxIsocData = 0;
+ pEp->fInitialized = true;
+ }
+
+ Log((DEVICE_NAME ": vboxUsbSolarisInitEp: Success, %s[%2d] %s %s bEndpoint=%#x\n", !pEpData ? "Default " : "Endpoint",
+ iEpIndex, vboxUsbSolarisEpType(pEp), vboxUsbSolarisEpDir(pEp), pEp->EpDesc.bEndpointAddress));
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * Initializes Endpoints for the current configuration, all interfaces and
+ * alternate setting 0 for each interface.
+ *
+ * @param pState The USB device instance.
+ *
+ * @returns VBox status code.
+ */
+LOCAL int vboxUsbSolarisInitEpsForCfg(vboxusb_state_t *pState)
+{
+ uint_t uCfgIndex = usb_get_current_cfgidx(pState->pDip);
+ if (uCfgIndex >= pState->pDevDesc->dev_n_cfg)
+ {
+ LogRel((DEVICE_NAME ": vboxUsbSolarisInitEpsForCfg: Invalid current config index %u\n", uCfgIndex));
+ return VERR_OUT_OF_RANGE;
+ }
+
+ usb_cfg_data_t *pConfig = &pState->pDevDesc->dev_cfg[uCfgIndex];
+ uchar_t bConfig = pConfig->cfg_descr.bConfigurationValue;
+
+ LogFunc((DEVICE_NAME ": vboxUsbSolarisInitEpsForCfg: pState=%p bConfig=%u uCfgIndex=%u\n", pState, bConfig, uCfgIndex));
+
+ const uint_t cIfs = pConfig->cfg_n_if;
+ for (uchar_t uIf = 0; uIf < cIfs; uIf++)
+ {
+ usb_if_data_t *pIf = &pConfig->cfg_if[uIf];
+ const uint_t cAlts = pIf->if_n_alt;
+ for (uchar_t uAlt = 0; uAlt < cAlts; uAlt++)
+ {
+ usb_alt_if_data_t *pAlt = &pIf->if_alt[uAlt];
+ if (pAlt->altif_descr.bAlternateSetting == 0) /* Refer USB 2.0 spec 9.6.5 "Interface" */
+ {
+ const uint_t cEps = pAlt->altif_n_ep;
+ for (uchar_t uEp = 0; uEp < cEps; uEp++)
+ {
+ uint8_t *pbEpData = (uint8_t *)&pAlt->altif_ep[0];
+ usb_ep_data_t *pEpData = (usb_ep_data_t *)(pbEpData + uEp * g_cbUsbEpData);
+ int rc = vboxUsbSolarisInitEp(pState, pEpData);
+ if (RT_FAILURE(rc))
+ {
+ LogRel((DEVICE_NAME ": vboxUsbSolarisInitEpsForCfg: Failed to init endpoint! "
+ "bConfig=%u bIf=%#x bAlt=%#x\n", bConfig, pAlt->altif_descr.bInterfaceNumber,
+ pAlt->altif_descr.bAlternateSetting));
+ return rc;
+ }
+ }
+ break; /* move on to next interface. */
+ }
+ }
+ }
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * Initializes Endpoints for the given Interface & Alternate setting.
+ *
+ * @param pState The USB device instance.
+ * @param bIf The Interface.
+ * @param bAlt The Alterate.
+ *
+ * @returns VBox status code.
+ */
+LOCAL int vboxUsbSolarisInitEpsForIfAlt(vboxusb_state_t *pState, uint8_t bIf, uint8_t bAlt)
+{
+ LogFunc((DEVICE_NAME ": vboxUsbSolarisInitEpsForIfAlt: pState=%p bIf=%d uAlt=%d\n", pState, bIf, bAlt));
+
+ /* Doesn't hurt to be paranoid */
+ uint_t uCfgIndex = usb_get_current_cfgidx(pState->pDip);
+ if (uCfgIndex >= pState->pDevDesc->dev_n_cfg)
+ {
+ LogRel((DEVICE_NAME ": vboxUsbSolarisInitEpsForIfAlt: Invalid current config index %d\n", uCfgIndex));
+ return VERR_OUT_OF_RANGE;
+ }
+
+ usb_cfg_data_t *pConfig = &pState->pDevDesc->dev_cfg[uCfgIndex];
+ for (uchar_t uIf = 0; uIf < pConfig->cfg_n_if; uIf++)
+ {
+ usb_if_data_t *pInterface = &pConfig->cfg_if[uIf];
+ const uint_t cAlts = pInterface->if_n_alt;
+ for (uchar_t uAlt = 0; uAlt < cAlts; uAlt++)
+ {
+ usb_alt_if_data_t *pAlt = &pInterface->if_alt[uAlt];
+ if ( pAlt->altif_descr.bInterfaceNumber == bIf
+ && pAlt->altif_descr.bAlternateSetting == bAlt)
+ {
+ const uint_t cEps = pAlt->altif_n_ep;
+ for (uchar_t uEp = 0; uEp < cEps; uEp++)
+ {
+ uint8_t *pbEpData = (uint8_t *)&pAlt->altif_ep[0];
+ usb_ep_data_t *pEpData = (usb_ep_data_t *)(pbEpData + uEp * g_cbUsbEpData);
+ int rc = vboxUsbSolarisInitEp(pState, pEpData);
+ if (RT_FAILURE(rc))
+ {
+ uint8_t bCfgValue = pConfig->cfg_descr.bConfigurationValue;
+ LogRel((DEVICE_NAME ": vboxUsbSolarisInitEpsForIfAlt: Failed to init endpoint! "
+ "bCfgValue=%u bIf=%#x bAlt=%#x\n", bCfgValue, bIf, bAlt));
+ return rc;
+ }
+ }
+ return VINF_SUCCESS;
+ }
+ }
+ }
+ return VERR_NOT_FOUND;
+}
+
+
+/**
+ * Destroys all Endpoints.
+ *
+ * @param pState The USB device instance.
+ *
+ * @remarks Requires the state mutex to be held.
+ * Call only from Detach() or similar as callbacks
+ */
+LOCAL void vboxUsbSolarisDestroyAllEps(vboxusb_state_t *pState)
+{
+ LogFunc((DEVICE_NAME ": vboxUsbSolarisDestroyAllEps: pState=%p\n", pState));
+
+ Assert(mutex_owned(&pState->Mtx));
+ for (unsigned i = 0; i < VBOXUSB_MAX_ENDPOINTS; i++)
+ {
+ vboxusb_ep_t *pEp = &pState->aEps[i];
+ if (pEp->fInitialized)
+ vboxUsbSolarisDestroyEp(pState, pEp);
+ }
+}
+
+
+/**
+ * Destroys an Endpoint.
+ *
+ * @param pState The USB device instance.
+ * @param pEp The Endpoint.
+ *
+ * @remarks Requires the state mutex to be held.
+ */
+LOCAL void vboxUsbSolarisDestroyEp(vboxusb_state_t *pState, vboxusb_ep_t *pEp)
+{
+ LogFunc((DEVICE_NAME ": vboxUsbSolarisDestroyEp: pState=%p pEp=%p\n", pState, pEp));
+
+ Assert(pEp->fInitialized);
+ Assert(mutex_owned(&pState->Mtx));
+ vboxusb_urb_t *pUrb = list_remove_head(&pEp->hIsocInUrbs);
+ while (pUrb)
+ {
+ if (pUrb->pMsg)
+ freemsg(pUrb->pMsg);
+ RTMemFree(pUrb);
+ pUrb = list_remove_head(&pEp->hIsocInUrbs);
+ }
+ pEp->cIsocInUrbs = 0;
+ list_destroy(&pEp->hIsocInUrbs);
+
+ vboxusb_isoc_req_t *pIsocReq = list_remove_head(&pEp->hIsocInLandedReqs);
+ while (pIsocReq)
+ {
+ kmem_free(pIsocReq, sizeof(vboxusb_isoc_req_t));
+ pIsocReq = list_remove_head(&pEp->hIsocInLandedReqs);
+ }
+ pEp->cbIsocInLandedReqs = 0;
+ list_destroy(&pEp->hIsocInLandedReqs);
+
+ pEp->fInitialized = false;
+}
+
+
+/**
+ * Closes all non-default pipes and drains the default pipe.
+ *
+ * @param pState The USB device instance.
+ * @param fDefault Whether to close the default control pipe.
+ *
+ * @remarks Requires the device state mutex to be held.
+ */
+LOCAL void vboxUsbSolarisCloseAllPipes(vboxusb_state_t *pState, bool fDefault)
+{
+ LogFunc((DEVICE_NAME ": vboxUsbSolarisCloseAllPipes: pState=%p\n", pState));
+
+ for (int i = 1; i < VBOXUSB_MAX_ENDPOINTS; i++)
+ {
+ vboxusb_ep_t *pEp = &pState->aEps[i];
+ if ( pEp
+ && pEp->pPipe)
+ {
+ Log((DEVICE_NAME ": vboxUsbSolarisCloseAllPipes: Closing[%d]\n", i));
+ vboxUsbSolarisClosePipe(pState, pEp);
+ }
+ }
+
+ if (fDefault)
+ {
+ vboxusb_ep_t *pEp = &pState->aEps[0];
+ if ( pEp
+ && pEp->pPipe)
+ {
+ vboxUsbSolarisClosePipe(pState, pEp);
+ Log((DEVICE_NAME ": vboxUsbSolarisCloseAllPipes: Closed default pipe\n"));
+ }
+ }
+}
+
+
+/**
+ * Opens the pipe associated with an Endpoint.
+ *
+ * @param pState The USB device instance.
+ * @param pEp The Endpoint.
+ * @remarks Requires the device state mutex to be held.
+ *
+ * @returns VBox status code.
+ */
+LOCAL int vboxUsbSolarisOpenPipe(vboxusb_state_t *pState, vboxusb_ep_t *pEp)
+{
+ Assert(mutex_owned(&pState->Mtx));
+
+ /*
+ * Make sure the Endpoint isn't open already.
+ */
+ if (pEp->pPipe)
+ return VINF_SUCCESS;
+
+ /*
+ * Default Endpoint; already opened just copy the pipe handle.
+ */
+ if ((pEp->EpDesc.bEndpointAddress & USB_EP_NUM_MASK) == 0)
+ {
+ pEp->pPipe = pState->pDevDesc->dev_default_ph;
+ Log((DEVICE_NAME ": vboxUsbSolarisOpenPipe: Default pipe opened\n"));
+ return VINF_SUCCESS;
+ }
+
+ /*
+ * Open the non-default pipe for the Endpoint.
+ */
+ mutex_exit(&pState->Mtx);
+ int rc = usb_pipe_open(pState->pDip, &pEp->EpDesc, &pEp->PipePolicy, USB_FLAGS_NOSLEEP, &pEp->pPipe);
+ mutex_enter(&pState->Mtx);
+ if (rc == USB_SUCCESS)
+ {
+ LogFunc((DEVICE_NAME ": vboxUsbSolarisOpenPipe: Opened pipe, pState=%p pEp=%p\n", pState, pEp));
+ usb_pipe_set_private(pEp->pPipe, (usb_opaque_t)pEp);
+
+ /*
+ * Determine input buffer size for Isoc. IN transfers.
+ */
+ if ( VBOXUSB_XFER_TYPE(pEp) == VUSBXFERTYPE_ISOC
+ && VBOXUSB_XFER_DIR(pEp) == VUSB_DIR_TO_HOST)
+ {
+ /*
+ * wMaxPacketSize bits 10..0 specifies maximum packet size which can hold 1024 bytes.
+ * If bits 12..11 is non-zero, cbMax will be more than 1024 and thus the Endpoint is a
+ * high-bandwidth Endpoint.
+ */
+ uint16_t cbMax = VBOXUSB_PKT_SIZE(pEp->EpDesc.wMaxPacketSize);
+ if (cbMax <= 1024)
+ {
+ /* Buffer 1 second for highspeed and 8 seconds for fullspeed Endpoints. */
+ pEp->cbMaxIsocData = 1000 * cbMax * 8;
+ }
+ else
+ {
+ /* Buffer about 400 milliseconds of data for highspeed high-bandwidth endpoints. */
+ pEp->cbMaxIsocData = 400 * cbMax * 8;
+ }
+ Log((DEVICE_NAME ": vboxUsbSolarisOpenPipe: bEndpoint=%#x cbMaxIsocData=%u\n", pEp->EpDesc.bEndpointAddress,
+ pEp->cbMaxIsocData));
+ }
+
+ rc = VINF_SUCCESS;
+ }
+ else
+ {
+ LogRel((DEVICE_NAME ": vboxUsbSolarisOpenPipe: Failed! rc=%d pState=%p pEp=%p\n", rc, pState, pEp));
+ rc = VERR_BAD_PIPE;
+ }
+
+ return rc;
+}
+
+
+/**
+ * Closes the pipe associated with an Endpoint.
+ *
+ * @param pState The USB device instance.
+ * @param pEp The Endpoint.
+ *
+ * @remarks Requires the device state mutex to be held.
+ */
+LOCAL void vboxUsbSolarisClosePipe(vboxusb_state_t *pState, vboxusb_ep_t *pEp)
+{
+ LogFunc((DEVICE_NAME ": vboxUsbSolarisClosePipe: pState=%p pEp=%p\n", pState, pEp));
+ AssertPtr(pEp);
+
+ if (pEp->pPipe)
+ {
+ /*
+ * Default pipe: allow completion of pending requests.
+ */
+ if (pEp->pPipe == pState->pDevDesc->dev_default_ph)
+ {
+ mutex_exit(&pState->Mtx);
+ usb_pipe_drain_reqs(pState->pDip, pEp->pPipe, 0, USB_FLAGS_SLEEP, NULL /* callback */, NULL /* callback arg. */);
+ mutex_enter(&pState->Mtx);
+ Log((DEVICE_NAME ": vboxUsbSolarisClosePipe: Closed default pipe\n"));
+ pState->fDefaultPipeOpen = false;
+ }
+ else
+ {
+ /*
+ * Stop Isoc. IN polling if required.
+ */
+ if (pEp->fIsocPolling)
+ {
+ pEp->fIsocPolling = false;
+ mutex_exit(&pState->Mtx);
+ usb_pipe_stop_isoc_polling(pEp->pPipe, USB_FLAGS_NOSLEEP);
+ mutex_enter(&pState->Mtx);
+ }
+
+ /*
+ * Non-default pipe: close it.
+ */
+ Log((DEVICE_NAME ": vboxUsbSolarisClosePipe: Pipe bmAttributes=%#x bEndpoint=%#x\n", pEp->EpDesc.bmAttributes,
+ pEp->EpDesc.bEndpointAddress));
+ mutex_exit(&pState->Mtx);
+ usb_pipe_close(pState->pDip, pEp->pPipe, USB_FLAGS_SLEEP, NULL /* callback */, NULL /* callback arg. */);
+ mutex_enter(&pState->Mtx);
+ }
+
+ /*
+ * Free the Endpoint data message block and reset pipe handle.
+ */
+ pEp->pPipe = NULL;
+
+ Log((DEVICE_NAME ": vboxUsbSolarisClosePipe: Success, bEndpoint=%#x\n", pEp->EpDesc.bEndpointAddress));
+ }
+
+ Assert(pEp->pPipe == NULL);
+}
+
+
+/**
+ * Finds the Configuration index for the passed in Configuration value.
+ *
+ * @param pState The USB device instance.
+ * @param bConfig The Configuration.
+ *
+ * @returns The configuration index if found, otherwise -1.
+ */
+LOCAL int vboxUsbSolarisGetConfigIndex(vboxusb_state_t *pState, uint_t bConfig)
+{
+ for (int CfgIndex = 0; CfgIndex < pState->pDevDesc->dev_n_cfg; CfgIndex++)
+ {
+ usb_cfg_data_t *pConfig = &pState->pDevDesc->dev_cfg[CfgIndex];
+ if (pConfig->cfg_descr.bConfigurationValue == bConfig)
+ return CfgIndex;
+ }
+
+ return -1;
+}
+
+
+/**
+ * Allocates and initializes an Isoc. In URB from the ring-3 equivalent.
+ *
+ * @param pState The USB device instance.
+ * @param pUrbReq Opaque pointer to the complete request.
+ *
+ * @returns The allocated Isoc. In URB to be used.
+ */
+LOCAL vboxusb_urb_t *vboxUsbSolarisGetIsocInUrb(vboxusb_state_t *pState, PVBOXUSBREQ_URB pUrbReq)
+{
+ /*
+ * Isoc. In URBs are not queued into the Inflight list like every other URBs.
+ * For now we allocate each URB which gets queued into the respective Endpoint during Xfer.
+ */
+ vboxusb_urb_t *pUrb = RTMemAllocZ(sizeof(vboxusb_urb_t));
+ if (RT_LIKELY(pUrb))
+ {
+ pUrb->enmState = VBOXUSB_URB_STATE_INFLIGHT;
+ pUrb->pState = pState;
+
+ if (RT_LIKELY(pUrbReq))
+ {
+ pUrb->pvUrbR3 = pUrbReq->pvUrbR3;
+ pUrb->bEndpoint = pUrbReq->bEndpoint;
+ pUrb->enmType = pUrbReq->enmType;
+ pUrb->enmDir = pUrbReq->enmDir;
+ pUrb->enmStatus = pUrbReq->enmStatus;
+ pUrb->cbDataR3 = pUrbReq->cbData;
+ pUrb->pvDataR3 = (RTR3PTR)pUrbReq->pvData;
+ pUrb->cIsocPkts = pUrbReq->cIsocPkts;
+
+ for (unsigned i = 0; i < pUrbReq->cIsocPkts; i++)
+ pUrb->aIsocPkts[i].cbPkt = pUrbReq->aIsocPkts[i].cbPkt;
+
+ pUrb->pMsg = NULL;
+ }
+ }
+ else
+ LogRel((DEVICE_NAME ": vboxUsbSolarisGetIsocInUrb: Failed to alloc %d bytes\n", sizeof(vboxusb_urb_t)));
+ return pUrb;
+}
+
+
+/**
+ * Queues a URB reusing previously allocated URBs as required.
+ *
+ * @param pState The USB device instance.
+ * @param pUrbReq Opaque pointer to the complete request.
+ * @param pMsg Pointer to the allocated request data.
+ *
+ * @returns The allocated URB to be used, or NULL upon failure.
+ */
+LOCAL vboxusb_urb_t *vboxUsbSolarisQueueUrb(vboxusb_state_t *pState, PVBOXUSBREQ_URB pUrbReq, mblk_t *pMsg)
+{
+ Assert(pUrbReq);
+ LogFunc((DEVICE_NAME ": vboxUsbSolarisQueueUrb: pState=%p pUrbReq=%p\n", pState, pUrbReq));
+
+ mutex_enter(&pState->Mtx);
+
+ /*
+ * Grab a URB from the free list.
+ */
+ vboxusb_urb_t *pUrb = list_remove_head(&pState->hFreeUrbs);
+ if (pUrb)
+ {
+ Assert(pUrb->enmState == VBOXUSB_URB_STATE_FREE);
+ Assert(!pUrb->pMsg);
+ Assert(pState->cFreeUrbs > 0);
+ --pState->cFreeUrbs;
+ }
+ else
+ {
+ /*
+ * We can't discard "old" URBs. For instance, INTR IN URBs that don't complete as
+ * they don't have a timeout can essentially take arbitrarily long to complete depending
+ * on the device and it's not safe to discard them in case they -do- complete. However,
+ * we also have to reasonably assume a device doesn't have too many pending URBs always.
+ *
+ * Thus we just use a large queue and simply refuse further transfers. This is not
+ * a situation which normally ever happens as usually there are at most than 4 or 5 URBs
+ * in-flight until we reap them.
+ */
+ uint32_t const cTotalUrbs = pState->cInflightUrbs + pState->cFreeUrbs + pState->cLandedUrbs;
+ if (cTotalUrbs >= VBOXUSB_URB_QUEUE_SIZE)
+ {
+ mutex_exit(&pState->Mtx);
+ LogRelMax(5, (DEVICE_NAME ": vboxUsbSolarisQueueUrb: Max queue size %u reached, refusing further transfers",
+ cTotalUrbs));
+ return NULL;
+ }
+
+ /*
+ * Allocate a new URB as we have no free URBs.
+ */
+ mutex_exit(&pState->Mtx);
+ pUrb = RTMemAllocZ(sizeof(vboxusb_urb_t));
+ if (RT_UNLIKELY(!pUrb))
+ {
+ LogRel((DEVICE_NAME ": vboxUsbSolarisQueueUrb: Failed to alloc %d bytes\n", sizeof(vboxusb_urb_t)));
+ return NULL;
+ }
+ mutex_enter(&pState->Mtx);
+ }
+
+ /*
+ * Add the URB to the inflight list.
+ */
+ list_insert_tail(&pState->hInflightUrbs, pUrb);
+ ++pState->cInflightUrbs;
+
+ Assert(!pUrb->pMsg);
+ pUrb->pMsg = pMsg;
+ pUrb->pState = pState;
+ pUrb->enmState = VBOXUSB_URB_STATE_INFLIGHT;
+ pUrb->pvUrbR3 = pUrbReq->pvUrbR3;
+ pUrb->bEndpoint = pUrbReq->bEndpoint;
+ pUrb->enmType = pUrbReq->enmType;
+ pUrb->enmDir = pUrbReq->enmDir;
+ pUrb->enmStatus = pUrbReq->enmStatus;
+ pUrb->fShortOk = pUrbReq->fShortOk;
+ pUrb->pvDataR3 = (RTR3PTR)pUrbReq->pvData;
+ pUrb->cbDataR3 = pUrbReq->cbData;
+ pUrb->cIsocPkts = pUrbReq->cIsocPkts;
+ if (pUrbReq->enmType == VUSBXFERTYPE_ISOC)
+ {
+ for (unsigned i = 0; i < pUrbReq->cIsocPkts; i++)
+ pUrb->aIsocPkts[i].cbPkt = pUrbReq->aIsocPkts[i].cbPkt;
+ }
+
+ mutex_exit(&pState->Mtx);
+ return pUrb;
+}
+
+
+/**
+ * Dequeues a completed URB into the landed list and informs user-land.
+ *
+ * @param pUrb The URB to move.
+ * @param URBStatus The Solaris URB completion code.
+ *
+ * @remarks All pipes could be closed at this point (e.g. Device disconnected during inflight URBs)
+ */
+LOCAL void vboxUsbSolarisDeQueueUrb(vboxusb_urb_t *pUrb, int URBStatus)
+{
+ LogFunc((DEVICE_NAME ": vboxUsbSolarisDeQueue: pUrb=%p\n", pUrb));
+ AssertPtrReturnVoid(pUrb);
+
+ pUrb->enmStatus = vboxUsbSolarisGetUrbStatus(URBStatus);
+ if (pUrb->enmStatus != VUSBSTATUS_OK)
+ Log((DEVICE_NAME ": vboxUsbSolarisDeQueueUrb: URB failed! URBStatus=%d bEndpoint=%#x\n", URBStatus, pUrb->bEndpoint));
+
+ vboxusb_state_t *pState = pUrb->pState;
+ if (RT_LIKELY(pState))
+ {
+ mutex_enter(&pState->Mtx);
+ pUrb->enmState = VBOXUSB_URB_STATE_LANDED;
+
+ /*
+ * Remove it from the inflight list & move it to the landed list.
+ */
+ list_remove(&pState->hInflightUrbs, pUrb);
+ Assert(pState->cInflightUrbs > 0);
+ --pState->cInflightUrbs;
+
+ list_insert_tail(&pState->hLandedUrbs, pUrb);
+ ++pState->cLandedUrbs;
+
+ vboxUsbSolarisNotifyComplete(pUrb->pState);
+ mutex_exit(&pState->Mtx);
+ return;
+ }
+
+ /* Well, let's at least not leak memory... */
+ freemsg(pUrb->pMsg);
+ pUrb->pMsg = NULL;
+ pUrb->enmStatus = VUSBSTATUS_INVALID;
+
+ LogRel((DEVICE_NAME ": vboxUsbSolarisDeQueue: State Gone\n"));
+}
+
+
+/**
+ * Concatenates a chain message block into a single message block if possible.
+ *
+ * @param pUrb The URB to move.
+ */
+LOCAL void vboxUsbSolarisConcatMsg(vboxusb_urb_t *pUrb)
+{
+ /*
+ * Concatenate the whole message rather than doing a chained copy while reaping.
+ */
+ if ( pUrb->pMsg
+ && pUrb->pMsg->b_cont)
+ {
+ mblk_t *pFullMsg = msgpullup(pUrb->pMsg, -1 /* all data */);
+ if (RT_LIKELY(pFullMsg))
+ {
+ freemsg(pUrb->pMsg);
+ pUrb->pMsg = pFullMsg;
+ }
+ else
+ LogRel((DEVICE_NAME ": vboxUsbSolarisConcatMsg: Failed. Expect glitches due to truncated data!\n"));
+ }
+}
+
+
+/**
+ * Wakes up a user process signalling URB completion.
+ *
+ * @param pState The USB device instance.
+ * @remarks Requires the device state mutex to be held.
+ */
+LOCAL void vboxUsbSolarisNotifyComplete(vboxusb_state_t *pState)
+{
+ if (pState->fPollPending)
+ {
+ pollhead_t *pPollHead = &pState->PollHead;
+ pState->fPollPending = false;
+ mutex_exit(&pState->Mtx);
+ pollwakeup(pPollHead, POLLIN);
+ mutex_enter(&pState->Mtx);
+ }
+}
+
+
+/**
+ * Wakes up a user process signalling a device unplug events.
+ *
+ * @param pState The USB device instance.
+ * @remarks Requires the device state mutex to be held.
+ */
+LOCAL void vboxUsbSolarisNotifyUnplug(vboxusb_state_t *pState)
+{
+ if (pState->fPollPending)
+ {
+ pollhead_t *pPollHead = &pState->PollHead;
+ pState->fPollPending = false;
+ mutex_exit(&pState->Mtx);
+ pollwakeup(pPollHead, POLLHUP);
+ mutex_enter(&pState->Mtx);
+ }
+}
+
+
+/**
+ * Performs a Control Xfer.
+ *
+ * @param pState The USB device instance.
+ * @param pEp The Endpoint for the Xfer.
+ * @param pUrb The VBox USB URB.
+ *
+ * @returns VBox status code.
+ */
+LOCAL int vboxUsbSolarisCtrlXfer(vboxusb_state_t *pState, vboxusb_ep_t *pEp, vboxusb_urb_t *pUrb)
+{
+ LogFunc((DEVICE_NAME ": vboxUsbSolarisCtrlXfer: pState=%p pEp=%p pUrb=%p enmDir=%d cbData=%d\n", pState, pEp, pUrb,
+ pUrb->enmDir, pUrb->cbDataR3));
+
+ AssertPtrReturn(pUrb->pMsg, VERR_INVALID_PARAMETER);
+ const size_t cbData = pUrb->cbDataR3 > VBOXUSB_CTRL_XFER_SIZE ? pUrb->cbDataR3 - VBOXUSB_CTRL_XFER_SIZE : 0;
+
+ /*
+ * Allocate a wrapper request.
+ */
+ usb_ctrl_req_t *pReq = usb_alloc_ctrl_req(pState->pDip, cbData, USB_FLAGS_SLEEP);
+ if (RT_LIKELY(pReq))
+ {
+ uchar_t *pSetupData = pUrb->pMsg->b_rptr;
+
+ /*
+ * Initialize the Ctrl Xfer Header.
+ */
+ pReq->ctrl_bmRequestType = pSetupData[0];
+ pReq->ctrl_bRequest = pSetupData[1];
+ pReq->ctrl_wValue = (pSetupData[3] << VBOXUSB_CTRL_XFER_SIZE) | pSetupData[2];
+ pReq->ctrl_wIndex = (pSetupData[5] << VBOXUSB_CTRL_XFER_SIZE) | pSetupData[4];
+ pReq->ctrl_wLength = (pSetupData[7] << VBOXUSB_CTRL_XFER_SIZE) | pSetupData[6];
+
+ if ( pUrb->enmDir == VUSBDIRECTION_OUT
+ && cbData)
+ {
+ bcopy(pSetupData + VBOXUSB_CTRL_XFER_SIZE, pReq->ctrl_data->b_wptr, cbData);
+ pReq->ctrl_data->b_wptr += cbData;
+ }
+
+ freemsg(pUrb->pMsg);
+ pUrb->pMsg = NULL;
+
+ /*
+ * Initialize callbacks and timeouts.
+ */
+ pReq->ctrl_cb = vboxUsbSolarisCtrlXferCompleted;
+ pReq->ctrl_exc_cb = vboxUsbSolarisCtrlXferCompleted;
+ pReq->ctrl_timeout = VBOXUSB_CTRL_XFER_TIMEOUT;
+ pReq->ctrl_attributes = USB_ATTRS_AUTOCLEARING | USB_ATTRS_SHORT_XFER_OK;
+ pReq->ctrl_client_private = (usb_opaque_t)pUrb;
+
+ /*
+ * Submit the request.
+ */
+ int rc = usb_pipe_ctrl_xfer(pEp->pPipe, pReq, USB_FLAGS_NOSLEEP);
+ if (RT_LIKELY(rc == USB_SUCCESS))
+ return VINF_SUCCESS;
+
+ LogRel((DEVICE_NAME ": vboxUsbSolarisCtrlXfer: Request failed! bEndpoint=%#x rc=%d\n", pUrb->bEndpoint, rc));
+
+ usb_free_ctrl_req(pReq);
+ return VERR_PIPE_IO_ERROR;
+ }
+
+ LogRel((DEVICE_NAME ": vboxUsbSolarisCtrlXfer: Failed to alloc request for %u bytes\n", cbData));
+ return VERR_NO_MEMORY;
+}
+
+
+/**
+ * Completion/Exception callback for Control Xfers.
+ *
+ * @param pPipe The Ctrl pipe handle.
+ * @param pReq The Ctrl request.
+ */
+LOCAL void vboxUsbSolarisCtrlXferCompleted(usb_pipe_handle_t pPipe, usb_ctrl_req_t *pReq)
+{
+ LogFunc((DEVICE_NAME ": vboxUsbSolarisCtrlXferCompleted: pPipe=%p pReq=%p\n", pPipe, pReq));
+ Assert(pReq);
+ Assert(!(pReq->ctrl_cb_flags & USB_CB_INTR_CONTEXT));
+
+ vboxusb_urb_t *pUrb = (vboxusb_urb_t *)pReq->ctrl_client_private;
+ if (RT_LIKELY(pUrb))
+ {
+ /*
+ * Funky stuff: We need to reconstruct the header for control transfers.
+ * Let us chain along the data and concatenate the entire message.
+ */
+ mblk_t *pSetupMsg = allocb(sizeof(VUSBSETUP), BPRI_MED);
+ if (RT_LIKELY(pSetupMsg))
+ {
+ VUSBSETUP SetupData;
+ SetupData.bmRequestType = pReq->ctrl_bmRequestType;
+ SetupData.bRequest = pReq->ctrl_bRequest;
+ SetupData.wValue = pReq->ctrl_wValue;
+ SetupData.wIndex = pReq->ctrl_wIndex;
+ SetupData.wLength = pReq->ctrl_wLength;
+
+ bcopy(&SetupData, pSetupMsg->b_wptr, sizeof(VUSBSETUP));
+ pSetupMsg->b_wptr += sizeof(VUSBSETUP);
+
+ /*
+ * Should be safe to update pMsg here without the state mutex as typically nobody else
+ * touches this URB in the inflight list.
+ *
+ * The reason we choose to use vboxUsbSolarisConcatMsg here is that we don't assume the
+ * message returned by Solaris is one contiguous chunk in 'pMsg->b_rptr'.
+ */
+ Assert(!pUrb->pMsg);
+ pUrb->pMsg = pSetupMsg;
+ pUrb->pMsg->b_cont = pReq->ctrl_data;
+ pReq->ctrl_data = NULL;
+ vboxUsbSolarisConcatMsg(pUrb);
+ }
+ else
+ LogRel((DEVICE_NAME ": vboxUsbSolarisCtrlXferCompleted: Failed to alloc %u bytes for header\n", sizeof(VUSBSETUP)));
+
+ /*
+ * Update the URB and move to landed list for reaping.
+ */
+ vboxUsbSolarisDeQueueUrb(pUrb, pReq->ctrl_completion_reason);
+ }
+ else
+ LogRel((DEVICE_NAME ": vboxUsbSolarisCtrlXferCompleted: Extreme error! missing private data\n"));
+
+ usb_free_ctrl_req(pReq);
+}
+
+
+/**
+ * Performs a Bulk Xfer.
+ *
+ * @param pState The USB device instance.
+ * @param pEp The Endpoint for the Xfer.
+ * @param pUrb The VBox USB URB.
+ *
+ * @returns VBox status code.
+ * @remarks Any errors, the caller should free pUrb->pMsg.
+ */
+LOCAL int vboxUsbSolarisBulkXfer(vboxusb_state_t *pState, vboxusb_ep_t *pEp, vboxusb_urb_t *pUrb)
+{
+ LogFunc((DEVICE_NAME ": vboxUsbSolarisBulkXfer: pState=%p pEp=%p pUrb=%p enmDir=%d cbData=%d\n", pState, pEp, pUrb,
+ pUrb->enmDir, pUrb->cbDataR3));
+
+ /*
+ * Allocate a wrapper request.
+ */
+ size_t const cbAlloc = pUrb->enmDir == VUSBDIRECTION_IN ? pUrb->cbDataR3 : 0;
+ usb_bulk_req_t *pReq = usb_alloc_bulk_req(pState->pDip, cbAlloc, USB_FLAGS_SLEEP);
+ if (RT_LIKELY(pReq))
+ {
+ /*
+ * Initialize Bulk Xfer, callbacks and timeouts.
+ */
+ usb_req_attrs_t fAttributes = USB_ATTRS_AUTOCLEARING;
+ if (pUrb->enmDir == VUSBDIRECTION_OUT)
+ {
+ pReq->bulk_data = pUrb->pMsg;
+ pUrb->pMsg = NULL;
+ }
+ else if ( pUrb->enmDir == VUSBDIRECTION_IN
+ && pUrb->fShortOk)
+ {
+ fAttributes |= USB_ATTRS_SHORT_XFER_OK;
+ }
+
+ Assert(!pUrb->pMsg);
+ pReq->bulk_len = pUrb->cbDataR3;
+ pReq->bulk_cb = vboxUsbSolarisBulkXferCompleted;
+ pReq->bulk_exc_cb = vboxUsbSolarisBulkXferCompleted;
+ pReq->bulk_timeout = 0;
+ pReq->bulk_attributes = fAttributes;
+ pReq->bulk_client_private = (usb_opaque_t)pUrb;
+
+ /* Don't obtain state lock here, we're just reading unchanging data... */
+ if (RT_UNLIKELY(pUrb->cbDataR3 > pState->cbMaxBulkXfer))
+ {
+ LogRel((DEVICE_NAME ": vboxUsbSolarisBulkXfer: Requesting %d bytes when only %d bytes supported by device\n",
+ pUrb->cbDataR3, pState->cbMaxBulkXfer));
+ }
+
+ /*
+ * Submit the request.
+ */
+ int rc = usb_pipe_bulk_xfer(pEp->pPipe, pReq, USB_FLAGS_NOSLEEP);
+ if (RT_LIKELY(rc == USB_SUCCESS))
+ return VINF_SUCCESS;
+
+ LogRel((DEVICE_NAME ": vboxUsbSolarisBulkXfer: Request failed! Ep=%#x rc=%d cbData=%u\n", pUrb->bEndpoint, rc,
+ pReq->bulk_len));
+
+ usb_free_bulk_req(pReq);
+ return VERR_PIPE_IO_ERROR;
+ }
+
+ LogRel((DEVICE_NAME ": vboxUsbSolarisBulkXfer: Failed to alloc bulk request\n"));
+ return VERR_NO_MEMORY;
+}
+
+
+/**
+ * Completion/Exception callback for Bulk Xfers.
+ *
+ * @param pPipe The Bulk pipe handle.
+ * @param pReq The Bulk request.
+ */
+LOCAL void vboxUsbSolarisBulkXferCompleted(usb_pipe_handle_t pPipe, usb_bulk_req_t *pReq)
+{
+ LogFunc((DEVICE_NAME ": vboxUsbSolarisBulkXferCompleted: pPipe=%p pReq=%p\n", pPipe, pReq));
+
+ Assert(pReq);
+ Assert(!(pReq->bulk_cb_flags & USB_CB_INTR_CONTEXT));
+
+ vboxusb_ep_t *pEp = (vboxusb_ep_t *)usb_pipe_get_private(pPipe);
+ if (RT_LIKELY(pEp))
+ {
+ vboxusb_urb_t *pUrb = (vboxusb_urb_t *)pReq->bulk_client_private;
+ if (RT_LIKELY(pUrb))
+ {
+ Assert(!pUrb->pMsg);
+ if ( pUrb->enmDir == VUSBDIRECTION_IN
+ && pReq->bulk_data)
+ {
+ pUrb->pMsg = pReq->bulk_data;
+ pReq->bulk_data = NULL;
+ vboxUsbSolarisConcatMsg(pUrb);
+ }
+
+ /*
+ * Update the URB and move to tail for reaping.
+ */
+ vboxUsbSolarisDeQueueUrb(pUrb, pReq->bulk_completion_reason);
+ }
+ else
+ LogRel((DEVICE_NAME ": vboxUsbSolarisBulkXferCompleted: Extreme error! private request data missing!\n"));
+ }
+ else
+ Log((DEVICE_NAME ": vboxUsbSolarisBulkXferCompleted: Pipe Gone!\n"));
+
+ usb_free_bulk_req(pReq);
+}
+
+
+/**
+ * Performs an Interrupt Xfer.
+ *
+ * @param pState The USB device instance.
+ * @param pEp The Endpoint for the Xfer.
+ * @param pUrb The VBox USB URB.
+ *
+ * @returns VBox status code.
+ * @remarks Any errors, the caller should free pUrb->pMsg.
+ */
+LOCAL int vboxUsbSolarisIntrXfer(vboxusb_state_t *pState, vboxusb_ep_t *pEp, vboxusb_urb_t *pUrb)
+{
+ LogFunc((DEVICE_NAME ": vboxUsbSolarisIntrXfer: pState=%p pEp=%p pUrb=%p enmDir=%d cbData=%d\n", pState, pEp, pUrb,
+ pUrb->enmDir, pUrb->cbDataR3));
+
+ usb_intr_req_t *pReq = usb_alloc_intr_req(pState->pDip, 0 /* length */, USB_FLAGS_SLEEP);
+ if (RT_LIKELY(pReq))
+ {
+ /*
+ * Initialize Intr Xfer, callbacks & timeouts.
+ */
+ usb_req_attrs_t fAttributes = USB_ATTRS_AUTOCLEARING;
+ if (pUrb->enmDir == VUSBDIRECTION_OUT)
+ {
+ pReq->intr_data = pUrb->pMsg;
+ pUrb->pMsg = NULL;
+ }
+ else
+ {
+ Assert(pUrb->enmDir == VUSBDIRECTION_IN);
+ fAttributes |= USB_ATTRS_ONE_XFER;
+ if (pUrb->fShortOk)
+ fAttributes |= USB_ATTRS_SHORT_XFER_OK;
+ }
+
+ Assert(!pUrb->pMsg);
+ pReq->intr_len = pUrb->cbDataR3; /* Not pEp->EpDesc.wMaxPacketSize */
+ pReq->intr_cb = vboxUsbSolarisIntrXferCompleted;
+ pReq->intr_exc_cb = vboxUsbSolarisIntrXferCompleted;
+ pReq->intr_timeout = 0;
+ pReq->intr_attributes = fAttributes;
+ pReq->intr_client_private = (usb_opaque_t)pUrb;
+
+ /*
+ * Submit the request.
+ */
+ int rc = usb_pipe_intr_xfer(pEp->pPipe, pReq, USB_FLAGS_NOSLEEP);
+ if (RT_LIKELY(rc == USB_SUCCESS))
+ return VINF_SUCCESS;
+
+ LogRel((DEVICE_NAME ": vboxUsbSolarisIntrXfer: usb_pipe_intr_xfer failed! rc=%d bEndpoint=%#x\n", rc, pUrb->bEndpoint));
+
+ usb_free_intr_req(pReq);
+ return VERR_PIPE_IO_ERROR;
+ }
+
+ LogRel((DEVICE_NAME ": vboxUsbSolarisIntrXfer: Failed to alloc intr request\n"));
+ return VERR_NO_MEMORY;
+}
+
+
+/**
+ * Completion/Exception callback for Intr Xfers.
+ *
+ * @param pPipe The Intr pipe handle.
+ * @param pReq The Intr request.
+ */
+LOCAL void vboxUsbSolarisIntrXferCompleted(usb_pipe_handle_t pPipe, usb_intr_req_t *pReq)
+{
+ LogFunc((DEVICE_NAME ": vboxUsbSolarisIntrXferCompleted: pPipe=%p pReq=%p\n", pPipe, pReq));
+
+ Assert(pReq);
+ Assert(!(pReq->intr_cb_flags & USB_CB_INTR_CONTEXT));
+
+ vboxusb_urb_t *pUrb = (vboxusb_urb_t *)pReq->intr_client_private;
+ if (RT_LIKELY(pUrb))
+ {
+ if ( pUrb->enmDir == VUSBDIRECTION_IN
+ && pReq->intr_data)
+ {
+ pUrb->pMsg = pReq->intr_data;
+ pReq->intr_data = NULL;
+ vboxUsbSolarisConcatMsg(pUrb);
+ }
+
+ /*
+ * Update the URB and move to landed list for reaping.
+ */
+ vboxUsbSolarisDeQueueUrb(pUrb, pReq->intr_completion_reason);
+ }
+ else
+ LogRel((DEVICE_NAME ": vboxUsbSolarisIntrXferCompleted: Extreme error! private request data missing\n"));
+
+ usb_free_intr_req(pReq);
+}
+
+
+/**
+ * Performs an Isochronous Xfer.
+ *
+ * @param pState The USB device instance.
+ * @param pEp The Endpoint for the Xfer.
+ * @param pUrb The VBox USB URB.
+ *
+ * @returns VBox status code.
+ * @remarks Any errors, the caller should free pUrb->pMsg.
+ */
+LOCAL int vboxUsbSolarisIsocXfer(vboxusb_state_t *pState, vboxusb_ep_t *pEp, vboxusb_urb_t *pUrb)
+{
+ /* LogFunc((DEVICE_NAME ": vboxUsbSolarisIsocXfer: pState=%p pEp=%p pUrb=%p\n", pState, pEp, pUrb)); */
+
+ /*
+ * For Isoc. IN transfers we perform one request and USBA polls the device continuously
+ * and supplies our Xfer callback with input data. We cannot perform one-shot Isoc. In transfers.
+ */
+ size_t cbData = (pUrb->enmDir == VUSBDIRECTION_IN ? pUrb->cIsocPkts * pUrb->aIsocPkts[0].cbPkt : 0);
+ if (pUrb->enmDir == VUSBDIRECTION_IN)
+ {
+ Log((DEVICE_NAME ": vboxUsbSolarisIsocXfer: Isoc. IN - Queueing\n"));
+
+ mutex_enter(&pState->Mtx);
+ if (pEp->fIsocPolling)
+ {
+ /*
+ * Queue a maximum of cbMaxIsocData bytes, else fail.
+ */
+ if (pEp->cbIsocInLandedReqs + cbData > pEp->cbMaxIsocData)
+ {
+ mutex_exit(&pState->Mtx);
+ Log((DEVICE_NAME ": vboxUsbSolarisIsocXfer: Max Isoc. data %d bytes queued\n", pEp->cbMaxIsocData));
+ return VERR_TOO_MUCH_DATA;
+ }
+
+ list_insert_tail(&pEp->hIsocInUrbs, pUrb);
+ ++pEp->cIsocInUrbs;
+
+ mutex_exit(&pState->Mtx);
+ return VINF_SUCCESS;
+ }
+ mutex_exit(&pState->Mtx);
+ }
+
+ int rc = VINF_SUCCESS;
+ usb_isoc_req_t *pReq = usb_alloc_isoc_req(pState->pDip, pUrb->cIsocPkts, cbData, USB_FLAGS_NOSLEEP);
+ Log((DEVICE_NAME ": vboxUsbSolarisIsocXfer: enmDir=%#x cIsocPkts=%d aIsocPkts[0]=%d cbDataR3=%d\n", pUrb->enmDir,
+ pUrb->cIsocPkts, pUrb->aIsocPkts[0].cbPkt, pUrb->cbDataR3));
+ if (RT_LIKELY(pReq))
+ {
+ /*
+ * Initialize Isoc Xfer, callbacks & timeouts.
+ */
+ for (unsigned i = 0; i < pUrb->cIsocPkts; i++)
+ pReq->isoc_pkt_descr[i].isoc_pkt_length = pUrb->aIsocPkts[i].cbPkt;
+
+ if (pUrb->enmDir == VUSBDIRECTION_OUT)
+ {
+ pReq->isoc_data = pUrb->pMsg;
+ pReq->isoc_attributes = USB_ATTRS_AUTOCLEARING | USB_ATTRS_ISOC_XFER_ASAP;
+ pReq->isoc_cb = vboxUsbSolarisIsocOutXferCompleted;
+ pReq->isoc_exc_cb = vboxUsbSolarisIsocOutXferCompleted;
+ pReq->isoc_client_private = (usb_opaque_t)pUrb;
+ }
+ else
+ {
+ pReq->isoc_attributes = USB_ATTRS_AUTOCLEARING | USB_ATTRS_ISOC_XFER_ASAP | USB_ATTRS_SHORT_XFER_OK;
+ pReq->isoc_cb = vboxUsbSolarisIsocInXferCompleted;
+ pReq->isoc_exc_cb = vboxUsbSolarisIsocInXferError;
+ pReq->isoc_client_private = (usb_opaque_t)pState;
+ }
+ pReq->isoc_pkts_count = pUrb->cIsocPkts;
+ pReq->isoc_pkts_length = 0; /* auto compute */
+
+ /*
+ * Submit the request.
+ */
+ rc = usb_pipe_isoc_xfer(pEp->pPipe, pReq, USB_FLAGS_NOSLEEP);
+ if (RT_LIKELY(rc == USB_SUCCESS))
+ {
+ if (pUrb->enmDir == VUSBDIRECTION_IN)
+ {
+ /*
+ * Add the first Isoc. IN URB to the queue as well.
+ */
+ mutex_enter(&pState->Mtx);
+ list_insert_tail(&pEp->hIsocInUrbs, pUrb);
+ ++pEp->cIsocInUrbs;
+ pEp->fIsocPolling = true;
+ mutex_exit(&pState->Mtx);
+ }
+
+ return VINF_SUCCESS;
+ }
+ else
+ {
+ LogRel((DEVICE_NAME ": vboxUsbSolarisIsocXfer: usb_pipe_isoc_xfer failed! rc=%d\n", rc));
+ rc = VERR_PIPE_IO_ERROR;
+
+ if (pUrb->enmDir == VUSBDIRECTION_IN)
+ {
+ mutex_enter(&pState->Mtx);
+ vboxusb_urb_t *pIsocFailedUrb = list_remove_tail(&pEp->hIsocInUrbs);
+ if (pIsocFailedUrb)
+ {
+ RTMemFree(pIsocFailedUrb);
+ --pEp->cIsocInUrbs;
+ }
+ pEp->fIsocPolling = false;
+ mutex_exit(&pState->Mtx);
+ }
+ }
+
+ if (pUrb->enmDir == VUSBDIRECTION_OUT)
+ {
+ freemsg(pUrb->pMsg);
+ pUrb->pMsg = NULL;
+ }
+
+ usb_free_isoc_req(pReq);
+ }
+ else
+ {
+ LogRel((DEVICE_NAME ": vboxUsbSolarisIsocXfer: Failed to alloc isoc req for %d packets\n", pUrb->cIsocPkts));
+ rc = VERR_NO_MEMORY;
+ }
+
+ return rc;
+}
+
+
+/**
+ * Completion/Exception callback for Isoc IN Xfers.
+ *
+ * @param pPipe The Intr pipe handle.
+ * @param pReq The Intr request.
+ *
+ * @remarks Completion callback executes in interrupt context!
+ */
+LOCAL void vboxUsbSolarisIsocInXferCompleted(usb_pipe_handle_t pPipe, usb_isoc_req_t *pReq)
+{
+ /* LogFunc((DEVICE_NAME ": vboxUsbSolarisIsocInXferCompleted: pPipe=%p pReq=%p\n", pPipe, pReq)); */
+
+ vboxusb_state_t *pState = (vboxusb_state_t *)pReq->isoc_client_private;
+ if (RT_LIKELY(pState))
+ {
+ vboxusb_ep_t *pEp = (vboxusb_ep_t *)usb_pipe_get_private(pPipe);
+ if ( pEp
+ && pEp->pPipe)
+ {
+#if 0
+ /*
+ * Stop polling if all packets failed.
+ */
+ if (pReq->isoc_error_count == pReq->isoc_pkts_count)
+ {
+ Log((DEVICE_NAME ": vboxUsbSolarisIsocInXferCompleted: Stopping polling! Too many errors\n"));
+ mutex_exit(&pState->Mtx);
+ usb_pipe_stop_isoc_polling(pPipe, USB_FLAGS_NOSLEEP);
+ mutex_enter(&pState->Mtx);
+ pEp->fIsocPolling = false;
+ }
+#endif
+
+ /** @todo Query and verify this at runtime. */
+ AssertCompile(sizeof(VUSBISOC_PKT_DESC) == sizeof(usb_isoc_pkt_descr_t));
+ if (RT_LIKELY(pReq->isoc_data))
+ {
+ Log((DEVICE_NAME ": vboxUsbSolarisIsocInXferCompleted: cIsocInUrbs=%d cbIsocInLandedReqs=%d\n", pEp->cIsocInUrbs,
+ pEp->cbIsocInLandedReqs));
+
+ mutex_enter(&pState->Mtx);
+
+ /*
+ * If there are waiting URBs, satisfy the oldest one.
+ */
+ if ( pEp->cIsocInUrbs > 0
+ && pEp->cbIsocInLandedReqs == 0)
+ {
+ vboxusb_urb_t *pUrb = list_remove_head(&pEp->hIsocInUrbs);
+ if (RT_LIKELY(pUrb))
+ {
+ --pEp->cIsocInUrbs;
+ mutex_exit(&pState->Mtx);
+
+ for (unsigned i = 0; i < pReq->isoc_pkts_count; i++)
+ {
+ pUrb->aIsocPkts[i].cbActPkt = pReq->isoc_pkt_descr[i].isoc_pkt_actual_length;
+ pUrb->aIsocPkts[i].enmStatus = vboxUsbSolarisGetUrbStatus(pReq->isoc_pkt_descr[i].isoc_pkt_status);
+ }
+
+ pUrb->pMsg = pReq->isoc_data;
+ pReq->isoc_data = NULL;
+
+ /*
+ * Move to landed list
+ */
+ mutex_enter(&pState->Mtx);
+ list_insert_tail(&pState->hLandedUrbs, pUrb);
+ ++pState->cLandedUrbs;
+ vboxUsbSolarisNotifyComplete(pState);
+ }
+ else
+ {
+ /* Huh!? cIsocInUrbs is wrong then! Should never happen unless we decide to decrement cIsocInUrbs in
+ Reap time */
+ pEp->cIsocInUrbs = 0;
+ LogRel((DEVICE_NAME ": vboxUsbSolarisIsocInXferCompleted: Extreme error! Isoc. counter borked!\n"));
+ }
+
+ mutex_exit(&pState->Mtx);
+ usb_free_isoc_req(pReq);
+ return;
+ }
+
+ mutex_exit(&pState->Mtx);
+ }
+ else
+ LogRel((DEVICE_NAME ": vboxUsbSolarisIsocInXferCompleted: Data missing\n"));
+ }
+ else
+ LogRel((DEVICE_NAME ": vboxUsbSolarisIsocInXferCompleted: Pipe Gone\n"));
+ }
+ else
+ Log((DEVICE_NAME ": vboxUsbSolarisIsocInXferCompleted: State Gone\n"));
+
+ usb_free_isoc_req(pReq);
+}
+
+
+/**
+ * Exception callback for Isoc IN Xfers.
+ *
+ * @param pPipe The Intr pipe handle.
+ * @param pReq The Intr request.
+ * @remarks Completion callback executes in interrupt context!
+ */
+LOCAL void vboxUsbSolarisIsocInXferError(usb_pipe_handle_t pPipe, usb_isoc_req_t *pReq)
+{
+ LogFunc((DEVICE_NAME ": vboxUsbSolarisIsocInXferError: pPipe=%p pReq=%p\n", pPipe, pReq));
+
+ vboxusb_state_t *pState = (vboxusb_state_t *)pReq->isoc_client_private;
+ if (RT_UNLIKELY(!pState))
+ {
+ Log((DEVICE_NAME ": vboxUsbSolarisIsocInXferError: State Gone\n"));
+ usb_free_isoc_req(pReq);
+ return;
+ }
+
+ mutex_enter(&pState->Mtx);
+ vboxusb_ep_t *pEp = (vboxusb_ep_t *)usb_pipe_get_private(pPipe);
+ if (RT_UNLIKELY(!pEp))
+ {
+ Log((DEVICE_NAME ": vboxUsbSolarisIsocInXferError: Pipe Gone\n"));
+ mutex_exit(&pState->Mtx);
+ usb_free_isoc_req(pReq);
+ return;
+ }
+
+ switch(pReq->isoc_completion_reason)
+ {
+ case USB_CR_NO_RESOURCES:
+ {
+ /*
+ * Resubmit the request in case the original request did not complete due to
+ * immediately unavailable requests
+ */
+ mutex_exit(&pState->Mtx);
+ usb_pipe_isoc_xfer(pPipe, pReq, USB_FLAGS_NOSLEEP);
+ Log((DEVICE_NAME ": vboxUsbSolarisIsocInXferError: Resubmitted Isoc. IN request due to unavailable resources\n"));
+ return;
+ }
+
+ case USB_CR_PIPE_CLOSING:
+ case USB_CR_STOPPED_POLLING:
+ case USB_CR_PIPE_RESET:
+ {
+ pEp->fIsocPolling = false;
+ usb_free_isoc_req(pReq);
+ break;
+ }
+
+ default:
+ {
+ Log((DEVICE_NAME ": vboxUsbSolarisIsocInXferError: Stopping Isoc. IN polling due to rc=%d\n",
+ pReq->isoc_completion_reason));
+ pEp->fIsocPolling = false;
+ mutex_exit(&pState->Mtx);
+ usb_pipe_stop_isoc_polling(pPipe, USB_FLAGS_NOSLEEP);
+ usb_free_isoc_req(pReq);
+ mutex_enter(&pState->Mtx);
+ break;
+ }
+ }
+
+ /*
+ * Dequeue i.e. delete the last queued Isoc In. URB. as failed.
+ */
+ vboxusb_urb_t *pUrb = list_remove_tail(&pEp->hIsocInUrbs);
+ if (pUrb)
+ {
+ --pEp->cIsocInUrbs;
+ Log((DEVICE_NAME ": vboxUsbSolarisIsocInXferError: Deleting last queued URB as it failed\n"));
+ freemsg(pUrb->pMsg);
+ RTMemFree(pUrb);
+ vboxUsbSolarisNotifyComplete(pState);
+ }
+
+ mutex_exit(&pState->Mtx);
+}
+
+
+/**
+ * Completion/Exception callback for Isoc OUT Xfers.
+ *
+ * @param pPipe The Intr pipe handle.
+ * @param pReq The Intr request.
+ * @remarks Completion callback executes in interrupt context!
+ */
+LOCAL void vboxUsbSolarisIsocOutXferCompleted(usb_pipe_handle_t pPipe, usb_isoc_req_t *pReq)
+{
+ LogFunc((DEVICE_NAME ": vboxUsbSolarisIsocOutXferCompleted: pPipe=%p pReq=%p\n", pPipe, pReq));
+
+ vboxusb_ep_t *pEp = (vboxusb_ep_t *)usb_pipe_get_private(pPipe);
+ if (RT_LIKELY(pEp))
+ {
+ vboxusb_urb_t *pUrb = (vboxusb_urb_t *)pReq->isoc_client_private;
+ if (RT_LIKELY(pUrb))
+ {
+ size_t cbActPkt = 0;
+ for (int i = 0; i < pReq->isoc_pkts_count; i++)
+ {
+ cbActPkt += pReq->isoc_pkt_descr[i].isoc_pkt_actual_length;
+ pUrb->aIsocPkts[i].cbActPkt = pReq->isoc_pkt_descr[i].isoc_pkt_actual_length;
+ pUrb->aIsocPkts[i].enmStatus = vboxUsbSolarisGetUrbStatus(pReq->isoc_pkt_descr[i].isoc_pkt_status);
+ }
+
+ Log((DEVICE_NAME ": vboxUsbSolarisIsocOutXferCompleted: cIsocPkts=%d cbData=%d cbActPkt=%d\n", pUrb->cIsocPkts,
+ pUrb->cbDataR3, cbActPkt));
+
+ if (pReq->isoc_completion_reason == USB_CR_OK)
+ {
+ if (RT_UNLIKELY(pUrb->pMsg != pReq->isoc_data)) /* Paranoia */
+ {
+ freemsg(pUrb->pMsg);
+ pUrb->pMsg = pReq->isoc_data;
+ }
+ }
+ pReq->isoc_data = NULL;
+
+ pUrb->cIsocPkts = pReq->isoc_pkts_count;
+ pUrb->cbDataR3 = cbActPkt;
+
+ /*
+ * Update the URB and move to landed list for reaping.
+ */
+ vboxUsbSolarisDeQueueUrb(pUrb, pReq->isoc_completion_reason);
+ }
+ else
+ Log((DEVICE_NAME ": vboxUsbSolarisIsocOutXferCompleted: Missing private data!?! Dropping OUT pUrb\n"));
+ }
+ else
+ Log((DEVICE_NAME ": vboxUsbSolarisIsocOutXferCompleted: Pipe Gone\n"));
+
+ usb_free_isoc_req(pReq);
+}
+
+
+/**
+ * Callback when the device gets disconnected.
+ *
+ * @param pDip The module structure instance.
+ *
+ * @returns Solaris USB error code.
+ */
+LOCAL int vboxUsbSolarisDeviceDisconnected(dev_info_t *pDip)
+{
+ LogFunc((DEVICE_NAME ": vboxUsbSolarisDeviceDisconnected: pDip=%p\n", pDip));
+
+ int instance = ddi_get_instance(pDip);
+ vboxusb_state_t *pState = ddi_get_soft_state(g_pVBoxUSBSolarisState, instance);
+
+ if (RT_LIKELY(pState))
+ {
+ /*
+ * Serialize access: exclusive access to the state.
+ */
+ usb_serialize_access(pState->StateMulti, USB_WAIT, 0);
+ mutex_enter(&pState->Mtx);
+
+ pState->DevState = USB_DEV_DISCONNECTED;
+
+ vboxUsbSolarisCloseAllPipes(pState, true /* ControlPipe */);
+ vboxUsbSolarisNotifyUnplug(pState);
+
+ mutex_exit(&pState->Mtx);
+ usb_release_access(pState->StateMulti);
+
+ return USB_SUCCESS;
+ }
+
+ LogRel((DEVICE_NAME ": vboxUsbSolarisDeviceDisconnected: Failed to get device state!\n"));
+ return USB_FAILURE;
+}
+
+
+/**
+ * Callback when the device gets reconnected.
+ *
+ * @param pDip The module structure instance.
+ *
+ * @returns Solaris USB error code.
+ */
+LOCAL int vboxUsbSolarisDeviceReconnected(dev_info_t *pDip)
+{
+ LogFunc((DEVICE_NAME ": vboxUsbSolarisDeviceReconnected: pDip=%p\n", pDip));
+
+ int instance = ddi_get_instance(pDip);
+ vboxusb_state_t *pState = ddi_get_soft_state(g_pVBoxUSBSolarisState, instance);
+
+ if (RT_LIKELY(pState))
+ {
+ vboxUsbSolarisDeviceRestore(pState);
+ return USB_SUCCESS;
+ }
+
+ LogRel((DEVICE_NAME ": vboxUsbSolarisDeviceReconnected: Failed to get device state!\n"));
+ return USB_FAILURE;
+}
+
+
+/**
+ * Restores device state after a reconnect or resume.
+ *
+ * @param pState The USB device instance.
+ */
+LOCAL void vboxUsbSolarisDeviceRestore(vboxusb_state_t *pState)
+{
+ LogFunc((DEVICE_NAME ": vboxUsbSolarisDeviceRestore: pState=%p\n", pState));
+ AssertPtrReturnVoid(pState);
+
+ /*
+ * Raise device power.
+ */
+ vboxUsbSolarisPowerBusy(pState);
+ int rc = pm_raise_power(pState->pDip, 0 /* component */, USB_DEV_OS_FULL_PWR);
+
+ /*
+ * Check if the same device is resumed/reconnected.
+ */
+ rc = usb_check_same_device(pState->pDip,
+ NULL, /* log handle */
+ USB_LOG_L2, /* log level */
+ -1, /* log mask */
+ USB_CHK_ALL, /* check level */
+ NULL); /* device string */
+
+ if (rc != USB_SUCCESS)
+ {
+ mutex_enter(&pState->Mtx);
+ pState->DevState = USB_DEV_DISCONNECTED;
+ mutex_exit(&pState->Mtx);
+
+ /* Do we need to inform userland here? */
+ vboxUsbSolarisPowerIdle(pState);
+ Log((DEVICE_NAME ": vboxUsbSolarisDeviceRestore: Not the same device\n"));
+ return;
+ }
+
+ /*
+ * Serialize access to not race with other PM functions.
+ */
+ usb_serialize_access(pState->StateMulti, USB_WAIT, 0);
+
+ mutex_enter(&pState->Mtx);
+ if (pState->DevState == USB_DEV_DISCONNECTED)
+ pState->DevState = USB_DEV_ONLINE;
+ else if (pState->DevState == USB_DEV_SUSPENDED)
+ pState->DevState = USB_DEV_ONLINE;
+
+ mutex_exit(&pState->Mtx);
+ usb_release_access(pState->StateMulti);
+
+ vboxUsbSolarisPowerIdle(pState);
+}
+
+
+/**
+ * Restores device state after a reconnect or resume.
+ *
+ * @param pState The USB device instance.
+ *
+ * @returns VBox status code.
+ */
+LOCAL int vboxUsbSolarisDeviceSuspend(vboxusb_state_t *pState)
+{
+ LogFunc((DEVICE_NAME ": vboxUsbSolarisDeviceSuspend: pState=%p\n", pState));
+
+ int rc = VERR_VUSB_DEVICE_IS_SUSPENDED;
+ mutex_enter(&pState->Mtx);
+
+ switch (pState->DevState)
+ {
+ case USB_DEV_SUSPENDED:
+ {
+ LogRel((DEVICE_NAME ": vboxUsbSolarisDeviceSuspend: Invalid device state %d\n", pState->DevState));
+ break;
+ }
+
+ case USB_DEV_ONLINE:
+ case USB_DEV_DISCONNECTED:
+ case USB_DEV_PWRED_DOWN:
+ {
+ int PreviousState = pState->DevState;
+ pState->DevState = USB_DEV_DISCONNECTED;
+
+ /** @todo this doesn't make sense when for e.g. an INTR IN URB with infinite
+ * timeout is pending on the device. Fix suspend logic later. */
+ /*
+ * Drain pending URBs.
+ */
+ for (int i = 0; i < VBOXUSB_DRAIN_TIME; i++)
+ {
+ if (pState->cInflightUrbs < 1)
+ break;
+
+ mutex_exit(&pState->Mtx);
+ delay(drv_usectohz(100000));
+ mutex_enter(&pState->Mtx);
+ }
+
+ /*
+ * Deny suspend if we still have pending URBs.
+ */
+ if (pState->cInflightUrbs > 0)
+ {
+ pState->DevState = PreviousState;
+ LogRel((DEVICE_NAME ": Cannot suspend %s %s (Ident=%s), %d inflight URBs\n", pState->szMfg, pState->szProduct,
+ pState->ClientInfo.szDeviceIdent, pState->cInflightUrbs));
+
+ mutex_exit(&pState->Mtx);
+ return VERR_RESOURCE_BUSY;
+ }
+
+ pState->cInflightUrbs = 0;
+
+ /*
+ * Serialize access to not race with Open/Detach/Close and
+ * Close all pipes including the default pipe.
+ */
+ mutex_exit(&pState->Mtx);
+ usb_serialize_access(pState->StateMulti, USB_WAIT, 0);
+ mutex_enter(&pState->Mtx);
+
+ vboxUsbSolarisCloseAllPipes(pState, true /* default pipe */);
+ vboxUsbSolarisNotifyUnplug(pState);
+
+ mutex_exit(&pState->Mtx);
+ usb_release_access(pState->StateMulti);
+
+ LogRel((DEVICE_NAME ": Suspended %s %s (Ident=%s)\n", pState->szMfg, pState->szProduct,
+ pState->ClientInfo.szDeviceIdent));
+ return VINF_SUCCESS;
+ }
+ }
+
+ mutex_exit(&pState->Mtx);
+ Log((DEVICE_NAME ": vboxUsbSolarisDeviceSuspend: Returns %d\n", rc));
+ return rc;
+}
+
+
+/**
+ * Restores device state after a reconnect or resume.
+ *
+ * @param pState The USB device instance.
+ */
+LOCAL void vboxUsbSolarisDeviceResume(vboxusb_state_t *pState)
+{
+ LogFunc((DEVICE_NAME ": vboxUsbSolarisDeviceResume: pState=%p\n", pState));
+ return vboxUsbSolarisDeviceRestore(pState);
+}
+
+
+/**
+ * Flags the PM component as busy so the system will not manage it's power.
+ *
+ * @param pState The USB device instance.
+ */
+LOCAL void vboxUsbSolarisPowerBusy(vboxusb_state_t *pState)
+{
+ LogFunc((DEVICE_NAME ": vboxUsbSolarisPowerBusy: pState=%p\n", pState));
+ AssertPtrReturnVoid(pState);
+
+ mutex_enter(&pState->Mtx);
+ if (pState->pPower)
+ {
+ pState->pPower->PowerBusy++;
+ mutex_exit(&pState->Mtx);
+
+ int rc = pm_busy_component(pState->pDip, 0 /* component */);
+ if (rc != DDI_SUCCESS)
+ {
+ Log((DEVICE_NAME ": vboxUsbSolarisPowerBusy: Busy component failed! rc=%d\n", rc));
+ mutex_enter(&pState->Mtx);
+ pState->pPower->PowerBusy--;
+ mutex_exit(&pState->Mtx);
+ }
+ }
+ else
+ mutex_exit(&pState->Mtx);
+}
+
+
+/**
+ * Flags the PM component as idle so its power managed by the system.
+ *
+ * @param pState The USB device instance.
+ */
+LOCAL void vboxUsbSolarisPowerIdle(vboxusb_state_t *pState)
+{
+ LogFunc((DEVICE_NAME ": vboxUsbSolarisPowerIdle: pState=%p\n", pState));
+ AssertPtrReturnVoid(pState);
+
+ if (pState->pPower)
+ {
+ int rc = pm_idle_component(pState->pDip, 0 /* component */);
+ if (rc == DDI_SUCCESS)
+ {
+ mutex_enter(&pState->Mtx);
+ Assert(pState->pPower->PowerBusy > 0);
+ pState->pPower->PowerBusy--;
+ mutex_exit(&pState->Mtx);
+ }
+ else
+ Log((DEVICE_NAME ": vboxUsbSolarisPowerIdle: Idle component failed! rc=%d\n", rc));
+ }
+}
+
diff --git a/src/VBox/HostDrivers/VBoxUSB/solaris/VBoxUSBMon-solaris.c b/src/VBox/HostDrivers/VBoxUSB/solaris/VBoxUSBMon-solaris.c
new file mode 100644
index 00000000..27e3868c
--- /dev/null
+++ b/src/VBox/HostDrivers/VBoxUSB/solaris/VBoxUSBMon-solaris.c
@@ -0,0 +1,1064 @@
+/* $Id: VBoxUSBMon-solaris.c $ */
+/** @file
+ * VirtualBox USB Monitor Driver, Solaris Hosts.
+ */
+
+/*
+ * Copyright (C) 2008-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#define LOG_GROUP LOG_GROUP_USB_DRV
+#include "VBoxUSBFilterMgr.h"
+#include <VBox/usblib-solaris.h>
+#include <VBox/version.h>
+#include <VBox/log.h>
+#include <VBox/cdefs.h>
+#include <VBox/types.h>
+#include <VBox/version.h>
+#include <iprt/assert.h>
+#include <iprt/err.h>
+#include <iprt/initterm.h>
+#include <iprt/mem.h>
+#include <iprt/process.h>
+#include <iprt/path.h>
+#include <iprt/semaphore.h>
+#include <iprt/string.h>
+
+#define USBDRV_MAJOR_VER 2
+#define USBDRV_MINOR_VER 0
+#include <sys/usb/usba.h>
+#include <sys/conf.h>
+#include <sys/modctl.h>
+#include <sys/mutex.h>
+#include <sys/stat.h>
+#include <sys/ddi.h>
+#include <sys/sunddi.h>
+#include <sys/sunndi.h>
+#include <sys/open.h>
+#include <sys/cmn_err.h>
+
+
+/*********************************************************************************************************************************
+* Defined Constants And Macros *
+*********************************************************************************************************************************/
+/** The module name. */
+#define DEVICE_NAME "vboxusbmon"
+/** The module description as seen in 'modinfo'. */
+#define DEVICE_DESC_DRV "VirtualBox USBMon"
+
+
+/*********************************************************************************************************************************
+* Internal Functions *
+*********************************************************************************************************************************/
+static int VBoxUSBMonSolarisOpen(dev_t *pDev, int fFlag, int fType, cred_t *pCred);
+static int VBoxUSBMonSolarisClose(dev_t Dev, int fFlag, int fType, cred_t *pCred);
+static int VBoxUSBMonSolarisRead(dev_t Dev, struct uio *pUio, cred_t *pCred);
+static int VBoxUSBMonSolarisWrite(dev_t Dev, struct uio *pUio, cred_t *pCred);
+static int VBoxUSBMonSolarisIOCtl(dev_t Dev, int Cmd, intptr_t pArg, int Mode, cred_t *pCred, int *pVal);
+static int VBoxUSBMonSolarisGetInfo(dev_info_t *pDip, ddi_info_cmd_t enmCmd, void *pArg, void **ppResult);
+static int VBoxUSBMonSolarisAttach(dev_info_t *pDip, ddi_attach_cmd_t enmCmd);
+static int VBoxUSBMonSolarisDetach(dev_info_t *pDip, ddi_detach_cmd_t enmCmd);
+
+
+/*********************************************************************************************************************************
+* Structures and Typedefs *
+*********************************************************************************************************************************/
+/**
+ * cb_ops: for drivers that support char/block entry points
+ */
+static struct cb_ops g_VBoxUSBMonSolarisCbOps =
+{
+ VBoxUSBMonSolarisOpen,
+ VBoxUSBMonSolarisClose,
+ nodev, /* b strategy */
+ nodev, /* b dump */
+ nodev, /* b print */
+ VBoxUSBMonSolarisRead,
+ VBoxUSBMonSolarisWrite,
+ VBoxUSBMonSolarisIOCtl,
+ nodev, /* c devmap */
+ nodev, /* c mmap */
+ nodev, /* c segmap */
+ nochpoll, /* c poll */
+ ddi_prop_op, /* property ops */
+ NULL, /* streamtab */
+ D_NEW | D_MP, /* compat. flag */
+ CB_REV /* revision */
+};
+
+/**
+ * dev_ops: for driver device operations
+ */
+static struct dev_ops g_VBoxUSBMonSolarisDevOps =
+{
+ DEVO_REV, /* driver build revision */
+ 0, /* ref count */
+ VBoxUSBMonSolarisGetInfo,
+ nulldev, /* identify */
+ nulldev, /* probe */
+ VBoxUSBMonSolarisAttach,
+ VBoxUSBMonSolarisDetach,
+ nodev, /* reset */
+ &g_VBoxUSBMonSolarisCbOps,
+ (struct bus_ops *)0,
+ nodev, /* power */
+ ddi_quiesce_not_needed
+};
+
+/**
+ * modldrv: export driver specifics to the kernel
+ */
+static struct modldrv g_VBoxUSBMonSolarisModule =
+{
+ &mod_driverops, /* extern from kernel */
+ DEVICE_DESC_DRV " " VBOX_VERSION_STRING "r" RT_XSTR(VBOX_SVN_REV),
+ &g_VBoxUSBMonSolarisDevOps
+};
+
+/**
+ * modlinkage: export install/remove/info to the kernel
+ */
+static struct modlinkage g_VBoxUSBMonSolarisModLinkage =
+{
+ MODREV_1,
+ &g_VBoxUSBMonSolarisModule,
+ NULL,
+};
+
+/**
+ * Client driver info.
+ */
+typedef struct vboxusbmon_client_t
+{
+ dev_info_t *pDip; /* Client device info. pointer */
+ VBOXUSB_CLIENT_INFO Info; /* Client registration data. */
+ struct vboxusbmon_client_t *pNext; /* Pointer to next client */
+} vboxusbmon_client_t;
+
+/**
+ * Device state info.
+ */
+typedef struct
+{
+ RTPROCESS Process; /* The process (id) of the session */
+} vboxusbmon_state_t;
+
+
+/*********************************************************************************************************************************
+* Global Variables *
+*********************************************************************************************************************************/
+/** Global Device handle we only support one instance. */
+static dev_info_t *g_pDip = NULL;
+/** Global Mutex. */
+static kmutex_t g_VBoxUSBMonSolarisMtx;
+/** Global list of client drivers registered with us. */
+vboxusbmon_client_t *g_pVBoxUSBMonSolarisClients = NULL;
+/** Opaque pointer to list of soft states. */
+static void *g_pVBoxUSBMonSolarisState;
+
+
+/*********************************************************************************************************************************
+* Internal Functions *
+*********************************************************************************************************************************/
+static int vboxUSBMonSolarisProcessIOCtl(int iFunction, void *pvState, void *pvData, size_t cbData, size_t *pcbReturnedData);
+static int vboxUSBMonSolarisResetDevice(char *pszDevicePath, bool fReattach);
+
+
+/*********************************************************************************************************************************
+* Monitor Global Hooks *
+*********************************************************************************************************************************/
+static int vboxUSBMonSolarisClientInfo(vboxusbmon_state_t *pState, PVBOXUSB_CLIENT_INFO pClientInfo);
+int VBoxUSBMonSolarisRegisterClient(dev_info_t *pClientDip, PVBOXUSB_CLIENT_INFO pClientInfo);
+int VBoxUSBMonSolarisUnregisterClient(dev_info_t *pClientDip);
+int VBoxUSBMonSolarisElectDriver(usb_dev_descr_t *pDevDesc, usb_dev_str_t *pDevStrings, char *pszDevicePath, int Bus, int Port,
+ char **ppszDrv, void *pvReserved);
+
+
+/**
+ * Kernel entry points
+ */
+int _init(void)
+{
+ int rc;
+
+ LogFunc((DEVICE_NAME ": _init\n"));
+
+ g_pDip = NULL;
+
+ /*
+ * Prevent module autounloading.
+ */
+ modctl_t *pModCtl = mod_getctl(&g_VBoxUSBMonSolarisModLinkage);
+ if (pModCtl)
+ pModCtl->mod_loadflags |= MOD_NOAUTOUNLOAD;
+ else
+ LogRel((DEVICE_NAME ": _init: Failed to disable autounloading!\n"));
+
+ /*
+ * Initialize IPRT R0 driver, which internally calls OS-specific r0 init.
+ */
+ rc = RTR0Init(0);
+ if (RT_SUCCESS(rc))
+ {
+ /*
+ * Initialize global mutex.
+ */
+ mutex_init(&g_VBoxUSBMonSolarisMtx, NULL, MUTEX_DRIVER, NULL);
+ rc = VBoxUSBFilterInit();
+ if (RT_SUCCESS(rc))
+ {
+ rc = ddi_soft_state_init(&g_pVBoxUSBMonSolarisState, sizeof(vboxusbmon_state_t), 1);
+ if (!rc)
+ {
+ rc = mod_install(&g_VBoxUSBMonSolarisModLinkage);
+ if (!rc)
+ return rc;
+
+ LogRel((DEVICE_NAME ": _init: mod_install failed! rc=%d\n", rc));
+ ddi_soft_state_fini(&g_pVBoxUSBMonSolarisState);
+ }
+ else
+ LogRel((DEVICE_NAME ": _init: ddi_soft_state_init failed! rc=%d\n", rc));
+ }
+ else
+ LogRel((DEVICE_NAME ": _init: VBoxUSBFilterInit failed! rc=%d\n", rc));
+
+ mutex_destroy(&g_VBoxUSBMonSolarisMtx);
+ RTR0Term();
+ }
+ else
+ LogRel((DEVICE_NAME ": _init: RTR0Init failed! rc=%d\n", rc));
+
+ return -1;
+}
+
+
+int _fini(void)
+{
+ int rc;
+
+ LogFunc((DEVICE_NAME ": _fini\n"));
+
+ rc = mod_remove(&g_VBoxUSBMonSolarisModLinkage);
+ if (!rc)
+ {
+ ddi_soft_state_fini(&g_pVBoxUSBMonSolarisState);
+ VBoxUSBFilterTerm();
+ mutex_destroy(&g_VBoxUSBMonSolarisMtx);
+
+ RTR0Term();
+ }
+ return rc;
+}
+
+
+int _info(struct modinfo *pModInfo)
+{
+ LogFunc((DEVICE_NAME ": _info\n"));
+
+ return mod_info(&g_VBoxUSBMonSolarisModLinkage, 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)
+ *
+ * @returns corresponding solaris error code.
+ */
+static int VBoxUSBMonSolarisAttach(dev_info_t *pDip, ddi_attach_cmd_t enmCmd)
+{
+ LogFunc((DEVICE_NAME ": VBoxUSBMonSolarisAttach: pDip=%p enmCmd=%d\n", pDip, enmCmd));
+ switch (enmCmd)
+ {
+ case DDI_ATTACH:
+ {
+ if (RT_UNLIKELY(g_pDip))
+ {
+ LogRel((DEVICE_NAME ": VBoxUSBMonSolarisAttach: Global instance already initialized\n"));
+ return DDI_FAILURE;
+ }
+
+ g_pDip = pDip;
+ int rc = ddi_create_priv_minor_node(pDip, DEVICE_NAME, S_IFCHR, 0 /* instance */, DDI_PSEUDO, 0 /* flags */,
+ "none", "none", 0660);
+ if (rc == DDI_SUCCESS)
+ {
+ rc = usb_register_dev_driver(g_pDip, VBoxUSBMonSolarisElectDriver);
+ if (rc == DDI_SUCCESS)
+ {
+ ddi_report_dev(pDip);
+ return DDI_SUCCESS;
+ }
+
+ LogRel((DEVICE_NAME ": VBoxUSBMonSolarisAttach: Failed to register driver election callback! rc=%d\n", rc));
+ }
+ else
+ LogRel((DEVICE_NAME ": VBoxUSBMonSolarisAttach: ddi_create_minor_node failed! rc=%d\n", rc));
+ return DDI_FAILURE;
+ }
+
+ case DDI_RESUME:
+ {
+ /* We don't have to bother about power management. */
+ 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)
+ *
+ * @returns corresponding solaris error code.
+ */
+static int VBoxUSBMonSolarisDetach(dev_info_t *pDip, ddi_detach_cmd_t enmCmd)
+{
+ LogFunc((DEVICE_NAME ": VBoxUSBMonSolarisDetach\n"));
+
+ switch (enmCmd)
+ {
+ case DDI_DETACH:
+ {
+ /*
+ * Free all registered clients' info.
+ */
+ mutex_enter(&g_VBoxUSBMonSolarisMtx);
+ vboxusbmon_client_t *pCur = g_pVBoxUSBMonSolarisClients;
+ while (pCur)
+ {
+ vboxusbmon_client_t *pNext = pCur->pNext;
+ RTMemFree(pCur);
+ pCur = pNext;
+ }
+ mutex_exit(&g_VBoxUSBMonSolarisMtx);
+
+ usb_unregister_dev_driver(g_pDip);
+
+ ddi_remove_minor_node(pDip, NULL);
+ g_pDip = NULL;
+ return DDI_SUCCESS;
+ }
+
+ case DDI_SUSPEND:
+ {
+ /* We don't have to bother about power management. */
+ return DDI_SUCCESS;
+ }
+
+ default:
+ return DDI_FAILURE;
+ }
+}
+
+
+/**
+ * Info entry point, called by solaris kernel for obtaining driver info.
+ *
+ * @param pDip The module structure instance (do not use).
+ * @param enmCmd Information request type.
+ * @param pvArg Type specific argument.
+ * @param ppvResult Where to store the requested info.
+ *
+ * @returns corresponding solaris error code.
+ */
+static int VBoxUSBMonSolarisGetInfo(dev_info_t *pDip, ddi_info_cmd_t enmCmd, void *pvArg, void **ppvResult)
+{
+ int rc = DDI_SUCCESS;
+
+ LogFunc((DEVICE_NAME ": VBoxUSBMonSolarisGetInfo\n"));
+
+ switch (enmCmd)
+ {
+ case DDI_INFO_DEVT2DEVINFO:
+ {
+ *ppvResult = (void *)g_pDip;
+ if (!*ppvResult)
+ rc = DDI_FAILURE;
+ break;
+ }
+
+ case DDI_INFO_DEVT2INSTANCE:
+ {
+ /* There can only be a single-instance of this driver and thus its instance number is 0. */
+ *ppvResult = (void *)0;
+ break;
+ }
+
+ default:
+ rc = DDI_FAILURE;
+ break;
+ }
+ return rc;
+}
+
+
+static int VBoxUSBMonSolarisOpen(dev_t *pDev, int fFlag, int fType, cred_t *pCred)
+{
+ vboxusbmon_state_t *pState = NULL;
+ unsigned iOpenInstance;
+
+ LogFunc((DEVICE_NAME ": VBoxUSBMonSolarisOpen\n"));
+
+ /*
+ * Verify we are being opened as a character device.
+ */
+ if (fType != OTYP_CHR)
+ return EINVAL;
+
+ /*
+ * Verify that we're called after attach.
+ */
+ if (!g_pDip)
+ {
+ LogRel((DEVICE_NAME ": VBoxUSBMonSolarisOpen: Invalid state for opening\n"));
+ return ENXIO;
+ }
+
+ for (iOpenInstance = 0; iOpenInstance < 4096; iOpenInstance++)
+ {
+ if ( !ddi_get_soft_state(g_pVBoxUSBMonSolarisState, iOpenInstance) /* faster */
+ && ddi_soft_state_zalloc(g_pVBoxUSBMonSolarisState, iOpenInstance) == DDI_SUCCESS)
+ {
+ pState = ddi_get_soft_state(g_pVBoxUSBMonSolarisState, iOpenInstance);
+ break;
+ }
+ }
+ if (!pState)
+ {
+ LogRel((DEVICE_NAME ": VBoxUSBMonSolarisOpen: Too many open instances"));
+ return ENXIO;
+ }
+
+ pState->Process = RTProcSelf();
+ *pDev = makedevice(getmajor(*pDev), iOpenInstance);
+
+ NOREF(fFlag);
+ NOREF(pCred);
+
+ return 0;
+}
+
+
+static int VBoxUSBMonSolarisClose(dev_t Dev, int fFlag, int fType, cred_t *pCred)
+{
+ vboxusbmon_state_t *pState = NULL;
+ LogFunc((DEVICE_NAME ": VBoxUSBMonSolarisClose\n"));
+
+ pState = ddi_get_soft_state(g_pVBoxUSBMonSolarisState, getminor(Dev));
+ if (!pState)
+ {
+ LogRel((DEVICE_NAME ": VBoxUSBMonSolarisClose: Failed to get state\n"));
+ return EFAULT;
+ }
+
+ /*
+ * Remove all filters for this client process.
+ */
+ VBoxUSBFilterRemoveOwner(pState->Process);
+
+ ddi_soft_state_free(g_pVBoxUSBMonSolarisState, getminor(Dev));
+ pState = NULL;
+
+ NOREF(fFlag);
+ NOREF(fType);
+ NOREF(pCred);
+
+ return 0;
+}
+
+
+static int VBoxUSBMonSolarisRead(dev_t Dev, struct uio *pUio, cred_t *pCred)
+{
+ LogFunc((DEVICE_NAME ": VBoxUSBMonSolarisRead\n"));
+ return 0;
+}
+
+
+static int VBoxUSBMonSolarisWrite(dev_t Dev, struct uio *pUio, cred_t *pCred)
+{
+ LogFunc((DEVICE_NAME ": VBoxUSBMonSolarisWrite\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(Code) (((Code) >> 16) & IOCPARM_MASK)
+#endif
+
+static int VBoxUSBMonSolarisIOCtl(dev_t Dev, int Cmd, intptr_t pArg, int Mode, cred_t *pCred, int *pVal)
+{
+ LogFunc((DEVICE_NAME ": VBoxUSBMonSolarisIOCtl: Dev=%d Cmd=%d pArg=%p Mode=%d\n", Dev, Cmd, pArg));
+
+ /*
+ * Get the session from the soft state item.
+ */
+ vboxusbmon_state_t *pState = ddi_get_soft_state(g_pVBoxUSBMonSolarisState, getminor(Dev));
+ if (!pState)
+ {
+ LogRel((DEVICE_NAME ": VBoxUSBMonSolarisIOCtl: No state data for minor instance %d\n", getminor(Dev)));
+ return EINVAL;
+ }
+
+ /*
+ * Read the request wrapper. Though We don't really need wrapper struct. now
+ * it's room for the future as Solaris isn't generous regarding the size.
+ */
+ VBOXUSBREQ ReqWrap;
+ if (IOCPARM_LEN(Cmd) != sizeof(ReqWrap))
+ {
+ LogRel((DEVICE_NAME ": VBoxUSBMonSolarisIOCtl: bad request %#x size=%d expected=%d\n", Cmd, IOCPARM_LEN(Cmd),
+ sizeof(ReqWrap)));
+ return ENOTTY;
+ }
+
+ int rc = ddi_copyin((void *)pArg, &ReqWrap, sizeof(ReqWrap), Mode);
+ if (RT_UNLIKELY(rc))
+ {
+ LogRel((DEVICE_NAME ": VBoxUSBMonSolarisIOCtl: ddi_copyin failed to read header pArg=%p Cmd=%d. rc=%d\n", pArg, Cmd, rc));
+ return EINVAL;
+ }
+
+ if (ReqWrap.u32Magic != VBOXUSBMON_MAGIC)
+ {
+ LogRel((DEVICE_NAME ": VBoxUSBMonSolarisIOCtl: Bad magic %#x; pArg=%p Cmd=%d\n", ReqWrap.u32Magic, pArg, Cmd));
+ return EINVAL;
+ }
+ if (RT_UNLIKELY( ReqWrap.cbData == 0
+ || ReqWrap.cbData > _1M*16))
+ {
+ LogRel((DEVICE_NAME ": VBoxUSBMonSolarisIOCtl: Bad size %#x; pArg=%p Cmd=%d\n", ReqWrap.cbData, pArg, Cmd));
+ return EINVAL;
+ }
+
+ /*
+ * Read the request.
+ */
+ void *pvBuf = RTMemTmpAlloc(ReqWrap.cbData);
+ if (RT_UNLIKELY(!pvBuf))
+ {
+ LogRel((DEVICE_NAME ": VBoxUSBMonSolarisIOCtl: RTMemTmpAlloc failed to alloc %d bytes\n", ReqWrap.cbData));
+ return ENOMEM;
+ }
+
+ rc = ddi_copyin((void *)(uintptr_t)ReqWrap.pvDataR3, pvBuf, ReqWrap.cbData, Mode);
+ if (RT_UNLIKELY(rc))
+ {
+ RTMemTmpFree(pvBuf);
+ LogRel((DEVICE_NAME ": VBoxUSBMonSolarisIOCtl: ddi_copyin failed; pvBuf=%p pArg=%p Cmd=%d. rc=%d\n", pvBuf, pArg, Cmd,
+ rc));
+ return EFAULT;
+ }
+ if (RT_UNLIKELY( ReqWrap.cbData != 0
+ && !RT_VALID_PTR(pvBuf)))
+ {
+ RTMemTmpFree(pvBuf);
+ LogRel((DEVICE_NAME ": VBoxUSBMonSolarisIOCtl: pvBuf Invalid pointer %p\n", pvBuf));
+ return EINVAL;
+ }
+ Log((DEVICE_NAME ": VBoxUSBMonSolarisIOCtl: pid=%d\n", (int)RTProcSelf()));
+
+ /*
+ * Process the IOCtl.
+ */
+ size_t cbDataReturned = 0;
+ rc = vboxUSBMonSolarisProcessIOCtl(Cmd, pState, pvBuf, ReqWrap.cbData, &cbDataReturned);
+ ReqWrap.rc = rc;
+ rc = 0;
+
+ if (RT_UNLIKELY(cbDataReturned > ReqWrap.cbData))
+ {
+ LogRel((DEVICE_NAME ": VBoxUSBMonSolarisIOCtl: Too much output data %d expected %d\n", cbDataReturned, ReqWrap.cbData));
+ cbDataReturned = ReqWrap.cbData;
+ }
+
+ ReqWrap.cbData = cbDataReturned;
+
+ /*
+ * Copy the request back to user space.
+ */
+ rc = ddi_copyout(&ReqWrap, (void *)pArg, sizeof(ReqWrap), Mode);
+ if (RT_LIKELY(!rc))
+ {
+ /*
+ * Copy the payload (if any) back to user space.
+ */
+ if (cbDataReturned > 0)
+ {
+ rc = ddi_copyout(pvBuf, (void *)(uintptr_t)ReqWrap.pvDataR3, cbDataReturned, Mode);
+ if (RT_UNLIKELY(rc))
+ {
+ LogRel((DEVICE_NAME ": VBoxUSBMonSolarisIOCtl: ddi_copyout failed; pvBuf=%p pArg=%p Cmd=%d. rc=%d\n", pvBuf,
+ pArg, Cmd, rc));
+ rc = EFAULT;
+ }
+ }
+ }
+ else
+ {
+ LogRel((DEVICE_NAME ": VBoxUSBMonSolarisIOCtl: ddi_copyout(1) failed pArg=%p Cmd=%d\n", pArg, Cmd));
+ rc = EFAULT;
+ }
+
+ *pVal = rc;
+ RTMemTmpFree(pvBuf);
+ return rc;
+}
+
+
+/**
+ * IOCtl processor for user to kernel and kernel to kernel communication.
+ *
+ * @returns VBox status code.
+ *
+ * @param iFunction The requested function.
+ * @param pvState Opaque pointer to driver state used for getting
+ * ring-3 process (Id).
+ * @param pvData The input/output data buffer. Can be NULL
+ * depending on the function.
+ * @param cbData The max size of the data buffer.
+ * @param pcbReturnedData Where to store the amount of returned data. Can
+ * be NULL.
+ */
+static int vboxUSBMonSolarisProcessIOCtl(int iFunction, void *pvState, void *pvData, size_t cbData, size_t *pcbReturnedData)
+{
+ LogFunc((DEVICE_NAME ": vboxUSBMonSolarisProcessIOCtl: iFunction=%d pvBuf=%p cbBuf=%zu\n", iFunction, pvData, cbData));
+
+ AssertPtrReturn(pvState, VERR_INVALID_POINTER);
+ vboxusbmon_state_t *pState = (vboxusbmon_state_t *)pvState;
+ int rc;
+
+#define CHECKRET_MIN_SIZE(mnemonic, cbMin) \
+ do { \
+ if (RT_UNLIKELY(cbData < (cbMin))) \
+ { \
+ LogRel(("vboxUSBSolarisProcessIOCtl: " mnemonic ": cbData=%#zx (%zu) min is %#zx (%zu)\n", \
+ cbData, cbData, (size_t)(cbMin), (size_t)(cbMin))); \
+ return VERR_BUFFER_OVERFLOW; \
+ } \
+ if (RT_UNLIKELY((cbMin) != 0 && !RT_VALID_PTR(pvData))) \
+ { \
+ LogRel(("vboxUSBSolarisProcessIOCtl: " mnemonic ": Invalid pointer %p\n", pvData)); \
+ return VERR_INVALID_POINTER; \
+ } \
+ } while (0)
+
+ switch (iFunction)
+ {
+ case VBOXUSBMON_IOCTL_ADD_FILTER:
+ {
+ CHECKRET_MIN_SIZE("ADD_FILTER", sizeof(VBOXUSBREQ_ADD_FILTER));
+
+ VBOXUSBREQ_ADD_FILTER *pReq = (VBOXUSBREQ_ADD_FILTER *)pvData;
+ PUSBFILTER pFilter = (PUSBFILTER)&pReq->Filter;
+
+ Log(("vboxUSBMonSolarisProcessIOCtl: idVendor=%#x idProduct=%#x bcdDevice=%#x bDeviceClass=%#x "
+ "bDeviceSubClass=%#x bDeviceProtocol=%#x bBus=%#x bPort=%#x\n",
+ USBFilterGetNum(pFilter, USBFILTERIDX_VENDOR_ID),
+ USBFilterGetNum(pFilter, USBFILTERIDX_PRODUCT_ID),
+ USBFilterGetNum(pFilter, USBFILTERIDX_DEVICE_REV),
+ USBFilterGetNum(pFilter, USBFILTERIDX_DEVICE_CLASS),
+ USBFilterGetNum(pFilter, USBFILTERIDX_DEVICE_SUB_CLASS),
+ USBFilterGetNum(pFilter, USBFILTERIDX_DEVICE_PROTOCOL),
+ USBFilterGetNum(pFilter, USBFILTERIDX_BUS),
+ USBFilterGetNum(pFilter, USBFILTERIDX_PORT)));
+ Log(("vboxUSBMonSolarisProcessIOCtl: Manufacturer=%s Product=%s Serial=%s\n",
+ USBFilterGetString(pFilter, USBFILTERIDX_MANUFACTURER_STR) ? USBFilterGetString(pFilter, USBFILTERIDX_MANUFACTURER_STR) : "<null>",
+ USBFilterGetString(pFilter, USBFILTERIDX_PRODUCT_STR) ? USBFilterGetString(pFilter, USBFILTERIDX_PRODUCT_STR) : "<null>",
+ USBFilterGetString(pFilter, USBFILTERIDX_SERIAL_NUMBER_STR) ? USBFilterGetString(pFilter, USBFILTERIDX_SERIAL_NUMBER_STR) : "<null>"));
+
+ rc = USBFilterSetMustBePresent(pFilter, USBFILTERIDX_BUS, false /* fMustBePresent */); AssertRC(rc);
+
+ rc = VBoxUSBFilterAdd(pFilter, pState->Process, &pReq->uId);
+ *pcbReturnedData = cbData;
+ Log((DEVICE_NAME ": vboxUSBMonSolarisProcessIOCtl: ADD_FILTER (Process:%d) returned %d\n", pState->Process, rc));
+ break;
+ }
+
+ case VBOXUSBMON_IOCTL_REMOVE_FILTER:
+ {
+ CHECKRET_MIN_SIZE("REMOVE_FILTER", sizeof(VBOXUSBREQ_REMOVE_FILTER));
+
+ VBOXUSBREQ_REMOVE_FILTER *pReq = (VBOXUSBREQ_REMOVE_FILTER *)pvData;
+ rc = VBoxUSBFilterRemove(pState->Process, (uintptr_t)pReq->uId);
+ *pcbReturnedData = 0;
+ Log((DEVICE_NAME ": vboxUSBMonSolarisProcessIOCtl: REMOVE_FILTER (Process:%d) returned %d\n", pState->Process, rc));
+ break;
+ }
+
+ case VBOXUSBMON_IOCTL_RESET_DEVICE:
+ {
+ CHECKRET_MIN_SIZE("RESET_DEVICE", sizeof(VBOXUSBREQ_RESET_DEVICE));
+
+ VBOXUSBREQ_RESET_DEVICE *pReq = (VBOXUSBREQ_RESET_DEVICE *)pvData;
+ rc = vboxUSBMonSolarisResetDevice(pReq->szDevicePath, pReq->fReattach);
+ *pcbReturnedData = 0;
+ Log((DEVICE_NAME ": vboxUSBMonSolarisProcessIOCtl: RESET_DEVICE (Process:%d) returned %d\n", pState->Process, rc));
+ break;
+ }
+
+ case VBOXUSBMON_IOCTL_CLIENT_INFO:
+ {
+ CHECKRET_MIN_SIZE("CLIENT_INFO", sizeof(VBOXUSBREQ_CLIENT_INFO));
+
+ VBOXUSBREQ_CLIENT_INFO *pReq = (VBOXUSBREQ_CLIENT_INFO *)pvData;
+ rc = vboxUSBMonSolarisClientInfo(pState, pReq);
+ *pcbReturnedData = cbData;
+ Log((DEVICE_NAME ": vboxUSBMonSolarisProcessIOCtl: CLIENT_INFO (Process:%d) returned %d\n", pState->Process, rc));
+ break;
+ }
+
+ case VBOXUSBMON_IOCTL_GET_VERSION:
+ {
+ CHECKRET_MIN_SIZE("GET_VERSION", sizeof(VBOXUSBREQ_GET_VERSION));
+
+ PVBOXUSBREQ_GET_VERSION pGetVersionReq = (PVBOXUSBREQ_GET_VERSION)pvData;
+ pGetVersionReq->u32Major = VBOXUSBMON_VERSION_MAJOR;
+ pGetVersionReq->u32Minor = VBOXUSBMON_VERSION_MINOR;
+ *pcbReturnedData = sizeof(VBOXUSBREQ_GET_VERSION);
+ rc = VINF_SUCCESS;
+ Log((DEVICE_NAME ": vboxUSBMonSolarisProcessIOCtl: GET_VERSION returned %d\n", rc));
+ break;
+ }
+
+ default:
+ {
+ LogRel((DEVICE_NAME ": vboxUSBMonSolarisProcessIOCtl: Unknown request (Process:%d) %#x\n", pState->Process,
+ iFunction));
+ *pcbReturnedData = 0;
+ rc = VERR_NOT_SUPPORTED;
+ break;
+ }
+ }
+ return rc;
+}
+
+
+static int vboxUSBMonSolarisResetDevice(char *pszDevicePath, bool fReattach)
+{
+ int rc = VERR_GENERAL_FAILURE;
+
+ LogFunc((DEVICE_NAME ": vboxUSBMonSolarisResetDevice: pszDevicePath=%s fReattach=%d\n", pszDevicePath, fReattach));
+
+ /*
+ * Try grabbing the dev_info_t.
+ */
+ dev_info_t *pDeviceInfo = e_ddi_hold_devi_by_path(pszDevicePath, 0);
+ if (pDeviceInfo)
+ {
+ ddi_release_devi(pDeviceInfo);
+
+ /*
+ * Grab the root device node from the parent hub for resetting.
+ */
+ dev_info_t *pTmpDeviceInfo = NULL;
+ for (;;)
+ {
+ pTmpDeviceInfo = ddi_get_parent(pDeviceInfo);
+ if (!pTmpDeviceInfo)
+ {
+ LogRel((DEVICE_NAME ":vboxUSBMonSolarisResetDevice: Failed to get parent device info for %s\n", pszDevicePath));
+ return VERR_GENERAL_FAILURE;
+ }
+
+ if (ddi_prop_exists(DDI_DEV_T_ANY, pTmpDeviceInfo, DDI_PROP_DONTPASS, "usb-port-count")) /* parent hub */
+ break;
+
+ pDeviceInfo = pTmpDeviceInfo;
+ }
+
+ /*
+ * Try re-enumerating the device.
+ */
+ rc = usb_reset_device(pDeviceInfo, fReattach ? USB_RESET_LVL_REATTACH : USB_RESET_LVL_DEFAULT);
+ Log((DEVICE_NAME ": vboxUSBMonSolarisResetDevice: usb_reset_device for %s level=%s rc=%d\n", pszDevicePath,
+ fReattach ? "ReAttach" : "Default", rc));
+
+ switch (rc)
+ {
+ case USB_SUCCESS: rc = VINF_SUCCESS; break;
+ case USB_INVALID_PERM: rc = VERR_PERMISSION_DENIED; break;
+ case USB_INVALID_ARGS: rc = VERR_INVALID_PARAMETER; break;
+ case USB_BUSY: rc = VERR_RESOURCE_BUSY; break;
+ case USB_INVALID_CONTEXT: rc = VERR_INVALID_CONTEXT; break;
+ case USB_FAILURE: rc = VERR_GENERAL_FAILURE; break;
+
+ default: rc = VERR_UNRESOLVED_ERROR; break;
+ }
+ }
+ else
+ {
+ rc = VERR_INVALID_HANDLE;
+ LogRel((DEVICE_NAME ": vboxUSBMonSolarisResetDevice: Cannot obtain device info for %s\n", pszDevicePath));
+ }
+
+ return rc;
+}
+
+
+/**
+ * Query client driver information. This also has a side-effect that it informs
+ * the client driver which upcoming VM process should be allowed to open it.
+ *
+ * @returns VBox status code.
+ * @param pState Pointer to the device state.
+ * @param pClientInfo Pointer to the client info. object.
+ */
+static int vboxUSBMonSolarisClientInfo(vboxusbmon_state_t *pState, PVBOXUSB_CLIENT_INFO pClientInfo)
+{
+ LogFunc((DEVICE_NAME ": vboxUSBMonSolarisClientInfo: pState=%p pClientInfo=%p\n", pState, pClientInfo));
+
+ AssertPtrReturn(pState, VERR_INVALID_POINTER);
+ AssertPtrReturn(pClientInfo, VERR_INVALID_POINTER);
+
+ mutex_enter(&g_VBoxUSBMonSolarisMtx);
+ vboxusbmon_client_t *pCur = g_pVBoxUSBMonSolarisClients;
+ vboxusbmon_client_t *pPrev = NULL;
+ while (pCur)
+ {
+ if (strncmp(pClientInfo->szDeviceIdent, pCur->Info.szDeviceIdent, sizeof(pCur->Info.szDeviceIdent) - 1) == 0)
+ {
+ pClientInfo->Instance = pCur->Info.Instance;
+ RTStrPrintf(pClientInfo->szClientPath, sizeof(pClientInfo->szClientPath), "%s", pCur->Info.szClientPath);
+
+ /*
+ * Inform the client driver that this is the client process that is going to open it. We can predict the future!
+ */
+ int rc;
+ if (pCur->Info.pfnSetConsumerCredentials)
+ {
+ rc = pCur->Info.pfnSetConsumerCredentials(pState->Process, pCur->Info.Instance, NULL /* pvReserved */);
+ if (RT_FAILURE(rc))
+ LogRel((DEVICE_NAME ": vboxUSBMonSolarisClientInfo: pfnSetConsumerCredentials failed! rc=%d\n", rc));
+ }
+ else
+ rc = VERR_INVALID_FUNCTION;
+
+ mutex_exit(&g_VBoxUSBMonSolarisMtx);
+
+ Log((DEVICE_NAME ": vboxUSBMonSolarisClientInfo: Found %s, rc=%d\n", pClientInfo->szDeviceIdent, rc));
+ return rc;
+ }
+ pPrev = pCur;
+ pCur = pCur->pNext;
+ }
+
+ mutex_exit(&g_VBoxUSBMonSolarisMtx);
+
+ LogRel((DEVICE_NAME ": vboxUSBMonSolarisClientInfo: Failed to find client %s\n", pClientInfo->szDeviceIdent));
+ return VERR_NOT_FOUND;
+}
+
+
+/**
+ * Registers client driver.
+ *
+ * @returns VBox status code.
+ */
+int VBoxUSBMonSolarisRegisterClient(dev_info_t *pClientDip, PVBOXUSB_CLIENT_INFO pClientInfo)
+{
+ LogFunc((DEVICE_NAME ": VBoxUSBMonSolarisRegisterClient: pClientDip=%p pClientInfo=%p\n", pClientDip, pClientInfo));
+ AssertPtrReturn(pClientInfo, VERR_INVALID_PARAMETER);
+
+ if (RT_LIKELY(g_pDip))
+ {
+ vboxusbmon_client_t *pClient = RTMemAllocZ(sizeof(vboxusbmon_client_t));
+ if (RT_LIKELY(pClient))
+ {
+ pClient->Info.Instance = pClientInfo->Instance;
+ strncpy(pClient->Info.szClientPath, pClientInfo->szClientPath, sizeof(pClient->Info.szClientPath));
+ strncpy(pClient->Info.szDeviceIdent, pClientInfo->szDeviceIdent, sizeof(pClient->Info.szDeviceIdent));
+ pClient->Info.pfnSetConsumerCredentials = pClientInfo->pfnSetConsumerCredentials;
+ pClient->pDip = pClientDip;
+
+ mutex_enter(&g_VBoxUSBMonSolarisMtx);
+ pClient->pNext = g_pVBoxUSBMonSolarisClients;
+ g_pVBoxUSBMonSolarisClients = pClient;
+ mutex_exit(&g_VBoxUSBMonSolarisMtx);
+
+ Log((DEVICE_NAME ": Client registered (ClientPath=%s Ident=%s)\n", pClient->Info.szClientPath,
+ pClient->Info.szDeviceIdent));
+ return VINF_SUCCESS;
+ }
+ return VERR_NO_MEMORY;
+ }
+ return VERR_INVALID_STATE;
+}
+
+
+/**
+ * Deregisters client driver.
+ *
+ * @returns VBox status code.
+ */
+int VBoxUSBMonSolarisUnregisterClient(dev_info_t *pClientDip)
+{
+ LogFunc((DEVICE_NAME ": VBoxUSBMonSolarisUnregisterClient: pClientDip=%p\n", pClientDip));
+ AssertReturn(pClientDip, VERR_INVALID_PARAMETER);
+
+ if (RT_LIKELY(g_pDip))
+ {
+ mutex_enter(&g_VBoxUSBMonSolarisMtx);
+
+ vboxusbmon_client_t *pCur = g_pVBoxUSBMonSolarisClients;
+ vboxusbmon_client_t *pPrev = NULL;
+ while (pCur)
+ {
+ if (pCur->pDip == pClientDip)
+ {
+ if (pPrev)
+ pPrev->pNext = pCur->pNext;
+ else
+ g_pVBoxUSBMonSolarisClients = pCur->pNext;
+
+ mutex_exit(&g_VBoxUSBMonSolarisMtx);
+
+ Log((DEVICE_NAME ": Client unregistered (ClientPath=%s Ident=%s)\n", pCur->Info.szClientPath,
+ pCur->Info.szDeviceIdent));
+ RTMemFree(pCur);
+ return VINF_SUCCESS;
+ }
+ pPrev = pCur;
+ pCur = pCur->pNext;
+ }
+
+ mutex_exit(&g_VBoxUSBMonSolarisMtx);
+
+ LogRel((DEVICE_NAME ": VBoxUSBMonSolarisUnregisterClient: Failed to find registered client %p\n", pClientDip));
+ return VERR_NOT_FOUND;
+ }
+ return VERR_INVALID_STATE;
+}
+
+
+/**
+ * USBA driver election callback.
+ *
+ * @returns USB_SUCCESS if we want to capture the device, USB_FAILURE otherwise.
+ * @param pDevDesc The parsed device descriptor (does not include subconfigs).
+ * @param pDevStrings Device strings: Manufacturer, Product, Serial Number.
+ * @param pszDevicePath The physical path of the device being attached.
+ * @param Bus The Bus number on which the device is on.
+ * @param Port The Port number on the bus.
+ * @param ppszDrv The name of the driver we wish to capture the device with.
+ * @param pvReserved Reserved for future use.
+ */
+int VBoxUSBMonSolarisElectDriver(usb_dev_descr_t *pDevDesc, usb_dev_str_t *pDevStrings, char *pszDevicePath, int Bus, int Port,
+ char **ppszDrv, void *pvReserved)
+{
+ LogFunc((DEVICE_NAME ": VBoxUSBMonSolarisElectDriver: pDevDesc=%p pDevStrings=%p pszDevicePath=%s Bus=%d Port=%d\n", pDevDesc,
+ pDevStrings, pszDevicePath, Bus, Port));
+
+ AssertPtrReturn(pDevDesc, USB_FAILURE);
+ AssertPtrReturn(pDevStrings, USB_FAILURE);
+
+ /*
+ * Create a filter from the device being attached.
+ */
+ USBFILTER Filter;
+ USBFilterInit(&Filter, USBFILTERTYPE_CAPTURE);
+ USBFilterSetNumExact(&Filter, USBFILTERIDX_VENDOR_ID, pDevDesc->idVendor, true);
+ USBFilterSetNumExact(&Filter, USBFILTERIDX_PRODUCT_ID, pDevDesc->idProduct, true);
+ USBFilterSetNumExact(&Filter, USBFILTERIDX_DEVICE_REV, pDevDesc->bcdDevice, true);
+ USBFilterSetNumExact(&Filter, USBFILTERIDX_DEVICE_CLASS, pDevDesc->bDeviceClass, true);
+ USBFilterSetNumExact(&Filter, USBFILTERIDX_DEVICE_SUB_CLASS, pDevDesc->bDeviceSubClass, true);
+ USBFilterSetNumExact(&Filter, USBFILTERIDX_DEVICE_PROTOCOL, pDevDesc->bDeviceProtocol, true);
+ USBFilterSetNumExact(&Filter, USBFILTERIDX_BUS, 0x0 /* Bus */, true); /* Use 0x0 as userland initFilterFromDevice function in Main: see comment on "SetMustBePresent" below */
+ USBFilterSetNumExact(&Filter, USBFILTERIDX_PORT, Port, true);
+ USBFilterSetStringExact(&Filter, USBFILTERIDX_MANUFACTURER_STR, pDevStrings->usb_mfg ? pDevStrings->usb_mfg : "",
+ true /*fMustBePresent*/, true /*fPurge*/);
+ USBFilterSetStringExact(&Filter, USBFILTERIDX_PRODUCT_STR, pDevStrings->usb_product ? pDevStrings->usb_product : "",
+ true /*fMustBePresent*/, true /*fPurge*/);
+ USBFilterSetStringExact(&Filter, USBFILTERIDX_SERIAL_NUMBER_STR, pDevStrings->usb_serialno ? pDevStrings->usb_serialno : "",
+ true /*fMustBePresent*/, true /*fPurge*/);
+
+ /* This doesn't work like it should (USBFilterMatch fails on matching field (6) i.e. Bus despite this. Investigate later. */
+ USBFilterSetMustBePresent(&Filter, USBFILTERIDX_BUS, false /* fMustBePresent */);
+
+ Log((DEVICE_NAME ": VBoxUSBMonSolarisElectDriver: idVendor=%#x idProduct=%#x bcdDevice=%#x bDeviceClass=%#x "
+ "bDeviceSubClass=%#x bDeviceProtocol=%#x bBus=%#x bPort=%#x\n",
+ USBFilterGetNum(&Filter, USBFILTERIDX_VENDOR_ID),
+ USBFilterGetNum(&Filter, USBFILTERIDX_PRODUCT_ID),
+ USBFilterGetNum(&Filter, USBFILTERIDX_DEVICE_REV),
+ USBFilterGetNum(&Filter, USBFILTERIDX_DEVICE_CLASS),
+ USBFilterGetNum(&Filter, USBFILTERIDX_DEVICE_SUB_CLASS),
+ USBFilterGetNum(&Filter, USBFILTERIDX_DEVICE_PROTOCOL),
+ USBFilterGetNum(&Filter, USBFILTERIDX_BUS),
+ USBFilterGetNum(&Filter, USBFILTERIDX_PORT)));
+ Log((DEVICE_NAME ": VBoxUSBMonSolarisElectDriver: Manufacturer=%s Product=%s Serial=%s\n",
+ USBFilterGetString(&Filter, USBFILTERIDX_MANUFACTURER_STR) ? USBFilterGetString(&Filter, USBFILTERIDX_MANUFACTURER_STR) : "<null>",
+ USBFilterGetString(&Filter, USBFILTERIDX_PRODUCT_STR) ? USBFilterGetString(&Filter, USBFILTERIDX_PRODUCT_STR) : "<null>",
+ USBFilterGetString(&Filter, USBFILTERIDX_SERIAL_NUMBER_STR) ? USBFilterGetString(&Filter, USBFILTERIDX_SERIAL_NUMBER_STR) : "<null>"));
+
+ /*
+ * Run through user filters and try to see if it has a match.
+ */
+ uintptr_t uId = 0;
+ RTPROCESS Owner = VBoxUSBFilterMatch(&Filter, &uId);
+ USBFilterDelete(&Filter);
+ if (Owner == NIL_RTPROCESS)
+ {
+ Log((DEVICE_NAME ": VBoxUSBMonSolarisElectDriver: No matching filters, device %#x:%#x uninteresting\n",
+ pDevDesc->idVendor, pDevDesc->idProduct));
+ return USB_FAILURE;
+ }
+
+ *ppszDrv = ddi_strdup(VBOXUSB_DRIVER_NAME, KM_SLEEP);
+#if 0
+ LogRel((DEVICE_NAME ": Capturing %s %s %#x:%#x:%s Bus=%d Port=%d\n",
+ pDevStrings->usb_mfg ? pDevStrings->usb_mfg : "<Unknown Manufacturer>",
+ pDevStrings->usb_product ? pDevStrings->usb_product : "<Unnamed USB device>",
+ pDevDesc->idVendor, pDevDesc->idProduct, pszDevicePath, Bus, Port));
+#else
+ /* Until IPRT R0 logging is fixed. See @bugref{6657#c7} */
+ cmn_err(CE_CONT, "Capturing %s %s 0x%x:0x%x:%s Bus=%d Port=%d\n",
+ pDevStrings->usb_mfg ? pDevStrings->usb_mfg : "<Unknown Manufacturer>",
+ pDevStrings->usb_product ? pDevStrings->usb_product : "<Unnamed USB device>",
+ pDevDesc->idVendor, pDevDesc->idProduct, pszDevicePath, Bus, Port);
+#endif
+ return USB_SUCCESS;
+}
+
diff --git a/src/VBox/HostDrivers/VBoxUSB/solaris/include/usbai_private.h b/src/VBox/HostDrivers/VBoxUSB/solaris/include/usbai_private.h
new file mode 100644
index 00000000..2e61d804
--- /dev/null
+++ b/src/VBox/HostDrivers/VBoxUSB/solaris/include/usbai_private.h
@@ -0,0 +1,161 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2009-2010 Oracle Corporation. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#ifndef _SYS_USB_USBA_USBAI_PRIVATE_H
+#define _SYS_USB_USBA_USBAI_PRIVATE_H
+
+/*
+ * Unstable interfaces not part of USBAI but used by Solaris client drivers.
+ * These interfaces may not be present in future releases and are highly
+ * unstable.
+ *
+ * Status key:
+ * C = Remove from Sun client drivers before removing from this file
+ * D = May be needed by legacy (DDK) drivers.
+ */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * convenience function for getting default config index
+ * as saved in usba_device structure
+ *
+ * Status: C
+ */
+uint_t usb_get_current_cfgidx(dev_info_t *);
+
+/*
+ * Usb logging, debug and console message handling.
+ */
+typedef struct usb_log_handle *usb_log_handle_t;
+
+#define USB_LOG_L0 0 /* warnings, console & syslog buffer */
+#define USB_LOG_L1 1 /* errors, syslog buffer */
+#define USB_LOG_L2 2 /* recoverable errors, debug only */
+#define USB_LOG_L3 3 /* interesting data, debug only */
+#define USB_LOG_L4 4 /* tracing, debug only */
+
+#define USB_CHK_BASIC 0 /* Empty mask. Basics always done. */
+#define USB_CHK_SERIAL 0x00000001 /* Compare device serial numbers. */
+#define USB_CHK_CFG 0x00000002 /* Compare raw config clouds. */
+#define USB_CHK_VIDPID 0x00000004 /* Compare product and vendor ID. */
+#define USB_CHK_ALL 0xFFFFFFFF /* Perform maximum checking. */
+
+int usb_check_same_device(dev_info_t *dip,
+ usb_log_handle_t log_handle,
+ int log_level,
+ int log_mask,
+ uint_t check_mask,
+ char *device_string);
+
+/*
+ * **************************************************************************
+ * Serialization functions remaining Contracted Consolidation Private
+ * **************************************************************************
+ */
+
+/* This whole section: status: C and D. */
+
+/*
+ * opaque serialization handle.
+ * Used by all usb_serialization routines.
+ *
+ * This handle is opaque to the client driver.
+ */
+typedef struct usb_serialization *usb_serialization_t;
+
+/*
+ * usb_init_serialization
+ * setup for serialization
+ *
+ * ARGUMENTS:
+ * s_dip - devinfo pointer
+ * flag - USB_INIT_SER_CHECK_SAME_THREAD
+ * when set, usb_release_access() will verify that the same
+ * thread releases access. If not, a console warning will
+ * be issued but access will be released anyways.
+ *
+ * RETURNS:
+ * usb_serialization handle
+ *
+ */
+usb_serialization_t usb_init_serialization(dev_info_t *s_dip,
+ uint_t flag);
+
+#define USB_INIT_SER_CHECK_SAME_THREAD 1
+
+/* fini for serialization */
+void usb_fini_serialization(usb_serialization_t usb_serp);
+
+/*
+ * Various ways of calling usb_serialize_access. These correspond to
+ * their cv_*wait* function counterparts for usb_serialize_access.
+ */
+#define USB_WAIT 0
+#define USB_WAIT_SIG 1
+#define USB_TIMEDWAIT 2
+#define USB_TIMEDWAIT_SIG 3
+
+/*
+ * usb_serialize_access:
+ * acquire serialized access
+ *
+ * ARGUMENTS:
+ * usb_serp - usb_serialization handle
+ * how_to_wait - Which cv_*wait* function to wait for condition.
+ * USB_WAIT: use cv_wait
+ * USB_WAIT_SIG: use cv_wait_sig
+ * USB_TIMEDWAIT: use cv_timedwait
+ * USB_TIMEDWAIT_SIG: use cv_timedwait_sig
+ * delta_timeout - Time in ms from current time to timeout. Checked
+ * only if USB_TIMEDWAIT or USB_TIMEDWAIT_SIG
+ * specified in how_to_wait.
+ * RETURNS:
+ * Same as values returned by cv_*wait* functions,
+ * except for when how_to_wait == USB_WAIT, where 0 is always returned.
+ * For calls where a timeout or signal could be expected, use this value
+ * to tell whether a kill(2) signal or timeout occurred.
+ */
+int usb_serialize_access(usb_serialization_t usb_serp,
+ uint_t how_to_wait,
+ uint_t delta_timeout);
+
+/*
+ * usb_release_access:
+ * release serialized access
+ *
+ * ARGUMENTS:
+ * usb_serp - usb_serialization handle
+ */
+void usb_release_access(usb_serialization_t usb_serp);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _SYS_USB_USBA_USBAI_PRIVATE_H */
+
diff --git a/src/VBox/HostDrivers/VBoxUSB/solaris/vboxusb.conf b/src/VBox/HostDrivers/VBoxUSB/solaris/vboxusb.conf
new file mode 100644
index 00000000..17993e73
--- /dev/null
+++ b/src/VBox/HostDrivers/VBoxUSB/solaris/vboxusb.conf
@@ -0,0 +1,37 @@
+#
+# Solaris Host USB Client Driver Configuration
+#
+
+#
+# Copyright (C) 2010-2023 Oracle and/or its affiliates.
+#
+# This file is part of VirtualBox base platform packages, as
+# available from https://www.virtualbox.org.
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation, in version 3 of the
+# License.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, see <https://www.gnu.org/licenses>.
+#
+# The contents of this file may alternatively be used under the terms
+# of the Common Development and Distribution License Version 1.0
+# (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+# in the VirtualBox distribution, in which case the provisions of the
+# CDDL are applicable instead of those of the GPL.
+#
+# You may elect to license modified versions of this file under the
+# terms and conditions of either the GPL or the CDDL or both.
+#
+# SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+#
+
+name="vboxusb" parent="pseudo";
+
diff --git a/src/VBox/HostDrivers/VBoxUSB/solaris/vboxusbmon.conf b/src/VBox/HostDrivers/VBoxUSB/solaris/vboxusbmon.conf
new file mode 100644
index 00000000..98a3b5e1
--- /dev/null
+++ b/src/VBox/HostDrivers/VBoxUSB/solaris/vboxusbmon.conf
@@ -0,0 +1,37 @@
+#
+# Solaris Host USB Monitor Driver Configuration
+#
+
+#
+# Copyright (C) 2010-2023 Oracle and/or its affiliates.
+#
+# This file is part of VirtualBox base platform packages, as
+# available from https://www.virtualbox.org.
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation, in version 3 of the
+# License.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, see <https://www.gnu.org/licenses>.
+#
+# The contents of this file may alternatively be used under the terms
+# of the Common Development and Distribution License Version 1.0
+# (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+# in the VirtualBox distribution, in which case the provisions of the
+# CDDL are applicable instead of those of the GPL.
+#
+# You may elect to license modified versions of this file under the
+# terms and conditions of either the GPL or the CDDL or both.
+#
+# SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+#
+
+name="vboxusbmon" parent="pseudo" instance=0;
+
diff --git a/src/VBox/HostDrivers/VBoxUSB/testcase/Makefile.kup b/src/VBox/HostDrivers/VBoxUSB/testcase/Makefile.kup
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/src/VBox/HostDrivers/VBoxUSB/testcase/Makefile.kup
diff --git a/src/VBox/HostDrivers/VBoxUSB/testcase/tstUSBFilter.cpp b/src/VBox/HostDrivers/VBoxUSB/testcase/tstUSBFilter.cpp
new file mode 100644
index 00000000..123682a9
--- /dev/null
+++ b/src/VBox/HostDrivers/VBoxUSB/testcase/tstUSBFilter.cpp
@@ -0,0 +1,391 @@
+/** $Id: tstUSBFilter.cpp $ */
+/** @file
+ * VirtualBox USB filter abstraction - testcase.
+ */
+
+/*
+ * Copyright (C) 2007-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include <VBox/usbfilter.h>
+#include <iprt/errcore.h>
+
+#include <iprt/string.h>
+#include <iprt/stream.h>
+#include <iprt/assert.h>
+#include <iprt/initterm.h>
+
+
+/*********************************************************************************************************************************
+* Defined Constants And Macros *
+*********************************************************************************************************************************/
+#define TESTCASE "tstUSBFilter"
+
+#define TST_CHECK_RC(expr) do { \
+ int rc = expr; \
+ if (RT_FAILURE(rc)) \
+ { \
+ RTPrintf( TESTCASE "(%d): %Rrc - %s\n", __LINE__, rc, #expr); \
+ cErrors++; \
+ } \
+ } while (0)
+
+#define TST_CHECK_EXPR(expr) do { \
+ int rc = (intptr_t)(expr); \
+ if (!rc) \
+ { \
+ RTPrintf( TESTCASE "(%d): %s -> %d\n", __LINE__, #expr, rc); \
+ cErrors++; \
+ } \
+} while (0)
+
+
+/*********************************************************************************************************************************
+* Global Variables *
+*********************************************************************************************************************************/
+static const char g_szString64[64+1] =
+{
+ "abcdefghijklmnopqrstuvwxyz012345"
+ "abcdefghijklmnopqrstuvwxyz012345"
+};
+
+static const char g_szString128[128+1] =
+{
+ "abcdefghijklmnopqrstuvwxyz012345abcdefghijklmnopqrstuvwxyz012345"
+ "abcdefghijklmnopqrstuvwxyz012345abcdefghijklmnopqrstuvwxyz012345"
+};
+
+static const char g_szString256[256+1] =
+{
+ "abcdefghijklmnopqrstuvwxyz012345abcdefghijklmnopqrstuvwxyz012345"
+ "abcdefghijklmnopqrstuvwxyz012345abcdefghijklmnopqrstuvwxyz012345"
+ "abcdefghijklmnopqrstuvwxyz012345abcdefghijklmnopqrstuvwxyz012345"
+ "abcdefghijklmnopqrstuvwxyz012345abcdefghijklmnopqrstuvwxyz012345"
+};
+
+
+int main()
+{
+ unsigned cErrors = 0;
+ RTR3InitExeNoArguments(0);
+
+ /*
+ * Basic property setting and simple matching.
+ */
+ USBFILTER Flt1;
+ USBFilterInit(&Flt1, USBFILTERTYPE_CAPTURE);
+ /* numbers */
+ TST_CHECK_RC(USBFilterSetNumExact(&Flt1, USBFILTERIDX_VENDOR_ID, 0x1111, true));
+ TST_CHECK_RC(USBFilterSetNumExact(&Flt1, USBFILTERIDX_PRODUCT_ID, 0x2222, true));
+ TST_CHECK_RC(USBFilterSetNumExact(&Flt1, USBFILTERIDX_DEVICE, 0, true));
+ TST_CHECK_RC(USBFilterSetNumExact(&Flt1, USBFILTERIDX_DEVICE_CLASS, 0, true));
+ TST_CHECK_RC(USBFilterSetNumExact(&Flt1, USBFILTERIDX_DEVICE_SUB_CLASS, 0, true));
+ TST_CHECK_RC(USBFilterSetNumExact(&Flt1, USBFILTERIDX_DEVICE_PROTOCOL, 0xff, true));
+ TST_CHECK_RC(USBFilterSetNumExact(&Flt1, USBFILTERIDX_BUS, 1, true));
+ TST_CHECK_RC(USBFilterSetIgnore(&Flt1, USBFILTERIDX_BUS));
+ TST_CHECK_RC(USBFilterSetPresentOnly(&Flt1, USBFILTERIDX_BUS));
+ TST_CHECK_RC(USBFilterSetNumExact(&Flt1, USBFILTERIDX_BUS, 1, true));
+ TST_CHECK_RC(USBFilterSetNumExact(&Flt1, USBFILTERIDX_BUS, 1, false));
+ TST_CHECK_RC(USBFilterSetNumExact(&Flt1, USBFILTERIDX_PORT, 1, true));
+ TST_CHECK_RC(USBFilterSetNumExact(&Flt1, USBFILTERIDX_PORT, 1, false));
+ TST_CHECK_RC(USBFilterSetIgnore(&Flt1, USBFILTERIDX_PORT));
+ /* strings */
+ TST_CHECK_RC(USBFilterSetStringExact(&Flt1, USBFILTERIDX_MANUFACTURER_STR, "foobar", true, false ));
+ TST_CHECK_RC(USBFilterSetStringExact(&Flt1, USBFILTERIDX_MANUFACTURER_STR, "foobar", true, false ));
+ TST_CHECK_RC(USBFilterSetStringExact(&Flt1, USBFILTERIDX_MANUFACTURER_STR, g_szString64, true, false ));
+ TST_CHECK_RC(USBFilterSetStringExact(&Flt1, USBFILTERIDX_MANUFACTURER_STR, g_szString64, true, false ));
+ TST_CHECK_RC(USBFilterSetStringExact(&Flt1, USBFILTERIDX_MANUFACTURER_STR, g_szString64, true, false ));
+ TST_CHECK_RC(USBFilterSetStringExact(&Flt1, USBFILTERIDX_MANUFACTURER_STR, g_szString64, true, false ));
+ TST_CHECK_RC(USBFilterSetStringExact(&Flt1, USBFILTERIDX_MANUFACTURER_STR, g_szString64, true, false ));
+ TST_CHECK_RC(USBFilterSetStringExact(&Flt1, USBFILTERIDX_MANUFACTURER_STR, g_szString128, true, false ));
+ TST_CHECK_RC(USBFilterSetStringExact(&Flt1, USBFILTERIDX_MANUFACTURER_STR, g_szString128, true, false ));
+ TST_CHECK_RC(USBFilterSetStringExact(&Flt1, USBFILTERIDX_MANUFACTURER_STR, g_szString128, true, false ));
+ TST_CHECK_RC(USBFilterSetStringExact(&Flt1, USBFILTERIDX_MANUFACTURER_STR, g_szString128, true, false ));
+ TST_CHECK_RC(USBFilterSetStringExact(&Flt1, USBFILTERIDX_MANUFACTURER_STR, g_szString128, true, false ));
+ TST_CHECK_RC(USBFilterSetStringExact(&Flt1, USBFILTERIDX_MANUFACTURER_STR, g_szString64, true, false ));
+ TST_CHECK_RC(USBFilterSetStringExact(&Flt1, USBFILTERIDX_PRODUCT_STR, "barbar", true, false ));
+ TST_CHECK_RC(USBFilterSetStringExact(&Flt1, USBFILTERIDX_PRODUCT_STR, g_szString64, true, false ));
+ TST_CHECK_RC(USBFilterSetStringExact(&Flt1, USBFILTERIDX_PRODUCT_STR, g_szString64, true, false ));
+ TST_CHECK_RC(USBFilterSetStringExact(&Flt1, USBFILTERIDX_SERIAL_NUMBER_STR, g_szString64, true, false ));
+ TST_CHECK_RC(USBFilterSetStringExact(&Flt1, USBFILTERIDX_SERIAL_NUMBER_STR, g_szString64, true, false ));
+ TST_CHECK_RC(USBFilterSetStringExact(&Flt1, USBFILTERIDX_SERIAL_NUMBER_STR, g_szString64, true, false ));
+ TST_CHECK_RC(USBFilterSetStringExact(&Flt1, USBFILTERIDX_SERIAL_NUMBER_STR, g_szString64, true, false ));
+ TST_CHECK_RC(USBFilterSetStringExact(&Flt1, USBFILTERIDX_SERIAL_NUMBER_STR, g_szString64, true, false ));
+ TST_CHECK_RC(USBFilterSetStringExact(&Flt1, USBFILTERIDX_MANUFACTURER_STR, "vendor", true, false ));
+ TST_CHECK_RC(USBFilterSetStringExact(&Flt1, USBFILTERIDX_PRODUCT_STR, "product", true, false ));
+ TST_CHECK_RC(USBFilterSetStringExact(&Flt1, USBFILTERIDX_SERIAL_NUMBER_STR, "serial", true, false ));
+
+ /* cloning */
+ USBFILTER Dev;
+ USBFilterClone(&Dev, &Flt1);
+
+ TST_CHECK_EXPR(USBFilterIsIdentical(&Dev, &Flt1));
+ TST_CHECK_EXPR(USBFilterMatch(&Dev, &Flt1));
+
+ USBFilterDelete(&Flt1);
+ USBFilterDelete(&Dev);
+
+ /* make a sample device */
+ USBFilterInit(&Dev, USBFILTERTYPE_CAPTURE);
+ TST_CHECK_RC(USBFilterSetNumExact(&Dev, USBFILTERIDX_VENDOR_ID, 0x1111, true));
+ TST_CHECK_RC(USBFilterSetNumExact(&Dev, USBFILTERIDX_PRODUCT_ID, 0x2222, true));
+ TST_CHECK_RC(USBFilterSetNumExact(&Dev, USBFILTERIDX_DEVICE, 0, true));
+ TST_CHECK_RC(USBFilterSetNumExact(&Dev, USBFILTERIDX_DEVICE_CLASS, 0, true));
+ TST_CHECK_RC(USBFilterSetNumExact(&Dev, USBFILTERIDX_DEVICE_SUB_CLASS, 0, true));
+ TST_CHECK_RC(USBFilterSetNumExact(&Dev, USBFILTERIDX_DEVICE_PROTOCOL, 0xff, true));
+ TST_CHECK_RC(USBFilterSetNumExact(&Dev, USBFILTERIDX_BUS, 1, true));
+ TST_CHECK_RC(USBFilterSetNumExact(&Dev, USBFILTERIDX_PORT, 2, true));
+ TST_CHECK_RC(USBFilterSetStringExact(&Dev, USBFILTERIDX_MANUFACTURER_STR, "vendor", true, false ));
+ TST_CHECK_RC(USBFilterSetStringExact(&Dev, USBFILTERIDX_PRODUCT_STR, "product", true, false ));
+ TST_CHECK_RC(USBFilterSetStringExact(&Dev, USBFILTERIDX_SERIAL_NUMBER_STR, "serial", true, false ));
+
+ /* do some basic matching tests */
+ USBFilterInit(&Flt1, USBFILTERTYPE_CAPTURE);
+ TST_CHECK_EXPR(!USBFilterHasAnySubstatialCriteria(&Flt1));
+ TST_CHECK_EXPR(USBFilterMatch(&Flt1, &Dev) /* 100% ignore filter */);
+
+ TST_CHECK_RC(USBFilterSetNumExact(&Flt1, USBFILTERIDX_PORT, 3, true));
+ TST_CHECK_EXPR(!USBFilterMatch(&Flt1, &Dev));
+ TST_CHECK_RC(USBFilterSetNumExact(&Flt1, USBFILTERIDX_PORT, 2, true));
+ TST_CHECK_EXPR(USBFilterMatch(&Flt1, &Dev));
+
+ TST_CHECK_RC(USBFilterSetNumExact(&Flt1, USBFILTERIDX_BUS, 2, true));
+ TST_CHECK_EXPR(!USBFilterMatch(&Flt1, &Dev));
+ TST_CHECK_RC(USBFilterSetNumExact(&Flt1, USBFILTERIDX_BUS, 1, true));
+ TST_CHECK_EXPR(USBFilterMatch(&Flt1, &Dev));
+
+ TST_CHECK_RC(USBFilterSetStringExact(&Flt1, USBFILTERIDX_PRODUCT_STR, "no match", true, false ));
+ TST_CHECK_EXPR(!USBFilterMatch(&Flt1, &Dev));
+ TST_CHECK_RC(USBFilterSetStringExact(&Flt1, USBFILTERIDX_PRODUCT_STR, "product", true, false ));
+ TST_CHECK_EXPR(USBFilterMatch(&Flt1, &Dev));
+
+ /* string patterns */
+ TST_CHECK_RC(USBFilterSetStringPattern(&Flt1, USBFILTERIDX_PRODUCT_STR, "p*", true));
+ TST_CHECK_EXPR(USBFilterMatch(&Flt1, &Dev));
+ TST_CHECK_RC(USBFilterSetStringPattern(&Flt1, USBFILTERIDX_PRODUCT_STR, "*product", true));
+ TST_CHECK_EXPR(USBFilterMatch(&Flt1, &Dev));
+ TST_CHECK_RC(USBFilterSetStringPattern(&Flt1, USBFILTERIDX_PRODUCT_STR, "product*", true));
+ TST_CHECK_EXPR(USBFilterMatch(&Flt1, &Dev));
+ TST_CHECK_RC(USBFilterSetStringPattern(&Flt1, USBFILTERIDX_PRODUCT_STR, "pro*t", true));
+ TST_CHECK_EXPR(USBFilterMatch(&Flt1, &Dev));
+ TST_CHECK_RC(USBFilterSetStringPattern(&Flt1, USBFILTERIDX_PRODUCT_STR, "pro*uct", true));
+ TST_CHECK_EXPR(USBFilterMatch(&Flt1, &Dev));
+ TST_CHECK_RC(USBFilterSetStringPattern(&Flt1, USBFILTERIDX_PRODUCT_STR, "pro*uct", true));
+ TST_CHECK_EXPR(USBFilterMatch(&Flt1, &Dev));
+ TST_CHECK_RC(USBFilterSetStringPattern(&Flt1, USBFILTERIDX_PRODUCT_STR, "pro*duct", true));
+ TST_CHECK_EXPR(USBFilterMatch(&Flt1, &Dev));
+ TST_CHECK_RC(USBFilterSetStringPattern(&Flt1, USBFILTERIDX_PRODUCT_STR, "pro*x", true));
+ TST_CHECK_EXPR(!USBFilterMatch(&Flt1, &Dev));
+ TST_CHECK_RC(USBFilterSetStringPattern(&Flt1, USBFILTERIDX_PRODUCT_STR, "*product*", true));
+ TST_CHECK_EXPR(USBFilterMatch(&Flt1, &Dev));
+ TST_CHECK_RC(USBFilterSetStringPattern(&Flt1, USBFILTERIDX_PRODUCT_STR, "*oduct*", true));
+ TST_CHECK_EXPR(USBFilterMatch(&Flt1, &Dev));
+ TST_CHECK_RC(USBFilterSetStringPattern(&Flt1, USBFILTERIDX_PRODUCT_STR, "*produc*", true));
+ TST_CHECK_EXPR(USBFilterMatch(&Flt1, &Dev));
+
+ TST_CHECK_RC(USBFilterSetStringPattern(&Flt1, USBFILTERIDX_PRODUCT_STR, "?r??u*?t", true));
+ TST_CHECK_EXPR(USBFilterMatch(&Flt1, &Dev));
+ TST_CHECK_RC(USBFilterSetStringPattern(&Flt1, USBFILTERIDX_PRODUCT_STR, "?r??u*?*?*?***??t", true));
+ TST_CHECK_EXPR(USBFilterMatch(&Flt1, &Dev));
+ TST_CHECK_RC(USBFilterSetStringPattern(&Flt1, USBFILTERIDX_PRODUCT_STR, "?r??u*?*?*?***??", true));
+ TST_CHECK_EXPR(USBFilterMatch(&Flt1, &Dev));
+ TST_CHECK_RC(USBFilterSetStringPattern(&Flt1, USBFILTERIDX_PRODUCT_STR, "p*d*t", true));
+ TST_CHECK_EXPR(USBFilterMatch(&Flt1, &Dev));
+ TST_CHECK_RC(USBFilterSetStringPattern(&Flt1, USBFILTERIDX_PRODUCT_STR, "p*x*t", true));
+ TST_CHECK_EXPR(!USBFilterMatch(&Flt1, &Dev));
+
+ TST_CHECK_RC(USBFilterSetIgnore(&Flt1, USBFILTERIDX_PRODUCT_STR));
+ TST_CHECK_EXPR(USBFilterMatch(&Flt1, &Dev));
+
+ /* numeric patterns */
+ TST_CHECK_RC(USBFilterSetNumExpression(&Flt1, USBFILTERIDX_VENDOR_ID, "0x1111", true));
+ TST_CHECK_EXPR(USBFilterMatch(&Flt1, &Dev));
+ TST_CHECK_RC(USBFilterSetNumExpression(&Flt1, USBFILTERIDX_VENDOR_ID, "0X1111", true));
+ TST_CHECK_EXPR(USBFilterMatch(&Flt1, &Dev));
+ TST_CHECK_RC(USBFilterSetNumExpression(&Flt1, USBFILTERIDX_VENDOR_ID, "4369", true));
+ TST_CHECK_EXPR(USBFilterMatch(&Flt1, &Dev));
+ TST_CHECK_RC(USBFilterSetNumExpression(&Flt1, USBFILTERIDX_VENDOR_ID, "010421", true));
+ TST_CHECK_EXPR(USBFilterMatch(&Flt1, &Dev));
+
+ TST_CHECK_RC(USBFilterSetNumExpression(&Flt1, USBFILTERIDX_VENDOR_ID, "0x1111-0x1111", true));
+ TST_CHECK_EXPR(USBFilterMatch(&Flt1, &Dev));
+ TST_CHECK_RC(USBFilterSetNumExpression(&Flt1, USBFILTERIDX_VENDOR_ID, "4369-4369", true));
+ TST_CHECK_EXPR(USBFilterMatch(&Flt1, &Dev));
+ TST_CHECK_RC(USBFilterSetNumExpression(&Flt1, USBFILTERIDX_VENDOR_ID, "010421-010421", true));
+ TST_CHECK_EXPR(USBFilterMatch(&Flt1, &Dev));
+
+ TST_CHECK_RC(USBFilterSetNumExpression(&Flt1, USBFILTERIDX_VENDOR_ID, "0x1110-0x1112", true));
+ TST_CHECK_EXPR(USBFilterMatch(&Flt1, &Dev));
+ TST_CHECK_RC(USBFilterSetNumExpression(&Flt1, USBFILTERIDX_VENDOR_ID, "4360-4370", true));
+ TST_CHECK_EXPR(USBFilterMatch(&Flt1, &Dev));
+ TST_CHECK_RC(USBFilterSetNumExpression(&Flt1, USBFILTERIDX_VENDOR_ID, "010420-010422", true));
+ TST_CHECK_EXPR(USBFilterMatch(&Flt1, &Dev));
+
+ TST_CHECK_RC(USBFilterSetNumExpression(&Flt1, USBFILTERIDX_VENDOR_ID, "0x1112-0x1110", true));
+ TST_CHECK_EXPR(USBFilterMatch(&Flt1, &Dev));
+
+ TST_CHECK_RC(USBFilterSetNumExpression(&Flt1, USBFILTERIDX_VENDOR_ID, "0x0-0x1f", true));
+ TST_CHECK_EXPR(!USBFilterMatch(&Flt1, &Dev));
+ TST_CHECK_RC(USBFilterSetNumExpression(&Flt1, USBFILTERIDX_VENDOR_ID, "0-19", true));
+ TST_CHECK_EXPR(!USBFilterMatch(&Flt1, &Dev));
+ TST_CHECK_RC(USBFilterSetNumExpression(&Flt1, USBFILTERIDX_VENDOR_ID, "0-017", true));
+ TST_CHECK_EXPR(!USBFilterMatch(&Flt1, &Dev));
+
+ TST_CHECK_RC(USBFilterSetNumExpression(&Flt1, USBFILTERIDX_VENDOR_ID, "0x0-0xffff", true));
+ TST_CHECK_EXPR(USBFilterMatch(&Flt1, &Dev));
+ TST_CHECK_RC(USBFilterSetNumExpression(&Flt1, USBFILTERIDX_VENDOR_ID, "0-65535", true));
+ TST_CHECK_EXPR(USBFilterMatch(&Flt1, &Dev));
+ TST_CHECK_RC(USBFilterSetNumExpression(&Flt1, USBFILTERIDX_VENDOR_ID, "0-177777", true));
+ TST_CHECK_EXPR(USBFilterMatch(&Flt1, &Dev));
+
+ TST_CHECK_RC(USBFilterSetNumExpression(&Flt1, USBFILTERIDX_VENDOR_ID, "0x0-0XABCD", true));
+ TST_CHECK_EXPR(USBFilterMatch(&Flt1, &Dev));
+ TST_CHECK_RC(USBFilterSetNumExpression(&Flt1, USBFILTERIDX_VENDOR_ID, "0x0EF-0XABCD", true));
+ TST_CHECK_EXPR(USBFilterMatch(&Flt1, &Dev));
+ TST_CHECK_RC(USBFilterSetNumExpression(&Flt1, USBFILTERIDX_VENDOR_ID, "0X0ef-0Xabcd", true));
+ TST_CHECK_EXPR(USBFilterMatch(&Flt1, &Dev));
+
+ TST_CHECK_RC(USBFilterSetNumExpression(&Flt1, USBFILTERIDX_VENDOR_ID, "42|1|0x1111", true));
+ TST_CHECK_EXPR(USBFilterMatch(&Flt1, &Dev));
+ TST_CHECK_RC(USBFilterSetNumExpression(&Flt1, USBFILTERIDX_VENDOR_ID, "42|0x1111|1", true));
+ TST_CHECK_EXPR(USBFilterMatch(&Flt1, &Dev));
+ TST_CHECK_RC(USBFilterSetNumExpression(&Flt1, USBFILTERIDX_VENDOR_ID, "0x1111|42|1", true));
+ TST_CHECK_EXPR(USBFilterMatch(&Flt1, &Dev));
+ TST_CHECK_RC(USBFilterSetNumExpression(&Flt1, USBFILTERIDX_VENDOR_ID, "0x1112|42|1", true));
+ TST_CHECK_EXPR(!USBFilterMatch(&Flt1, &Dev));
+
+ /* numeric patterns - interval filters */
+ TST_CHECK_RC(USBFilterSetNumExpression(&Flt1, USBFILTERIDX_VENDOR_ID, "int:0x0-0xffff", true));
+ TST_CHECK_EXPR(USBFilterMatch(&Flt1, &Dev));
+ TST_CHECK_RC(USBFilterSetNumExpression(&Flt1, USBFILTERIDX_VENDOR_ID, "int: 0x0 - 0xffff ", true));
+ TST_CHECK_EXPR(USBFilterMatch(&Flt1, &Dev));
+ TST_CHECK_RC(USBFilterSetNumExpression(&Flt1, USBFILTERIDX_PRODUCT_ID, "int:0x0028-", true));
+ TST_CHECK_EXPR(USBFilterMatch(&Flt1, &Dev));
+ TST_CHECK_RC(USBFilterSetNumExpression(&Flt1, USBFILTERIDX_DEVICE_REV, "int:-0x0045", true));
+ TST_CHECK_EXPR(USBFilterMatch(&Flt1, &Dev));
+ TST_CHECK_RC(USBFilterSetNumExpression(&Flt1, USBFILTERIDX_PORT, "int:1,4", true));
+ TST_CHECK_EXPR(USBFilterMatch(&Flt1, &Dev));
+ TST_CHECK_RC(USBFilterSetNumExpression(&Flt1, USBFILTERIDX_PORT, "int:( 1, 3 )", true));
+ TST_CHECK_EXPR(USBFilterMatch(&Flt1, &Dev));
+
+ TST_CHECK_RC(USBFilterSetNumExpression(&Flt1, USBFILTERIDX_VENDOR_ID, "39-59|0x256-0x101f|0xfffff-0xf000|0x1000-0x2000", true));
+ TST_CHECK_EXPR(USBFilterMatch(&Flt1, &Dev));
+ TST_CHECK_RC(USBFilterSetNumExpression(&Flt1, USBFILTERIDX_VENDOR_ID, "0x000256-0x0101f|0xf000-0xfffff|0x000008000-0x2000|39-59", true));
+ TST_CHECK_EXPR(!USBFilterMatch(&Flt1, &Dev));
+ TST_CHECK_RC(USBFilterSetNumExpression(&Flt1, USBFILTERIDX_VENDOR_ID, "| | \t \t\t| 0x256 - 0x101f | 0xf000 - 0xfeff\t| 0x1000 -\t0x6000 | 1- 0512", true));
+ TST_CHECK_EXPR(USBFilterMatch(&Flt1, &Dev));
+ TST_CHECK_RC(USBFilterSetNumExpression(&Flt1, USBFILTERIDX_VENDOR_ID, "| | \t \t\t| 0x256 - 0x101f | 0xf000 - 0xfeff\t| 0x1112 -\t0x6000 | 1- 0512", true));
+ TST_CHECK_EXPR(!USBFilterMatch(&Flt1, &Dev));
+
+
+ USBFilterDelete(&Flt1);
+
+ /*
+ * string overflow
+ */
+ struct
+ {
+ uint64_t u64Pre;
+ USBFILTER Flt;
+ uint64_t u64Post;
+ } sOf;
+ sOf.u64Pre = sOf.u64Post = UINT64_C(0x1234567887654321);
+
+ USBFilterInit(&sOf.Flt, USBFILTERTYPE_CAPTURE);
+ TST_CHECK_EXPR(sOf.u64Pre == UINT64_C(0x1234567887654321)); TST_CHECK_EXPR(sOf.u64Post == UINT64_C(0x1234567887654321));
+
+ AssertCompileMemberSize(USBFILTER, achStrTab, 256);
+ TST_CHECK_EXPR(USBFilterSetStringExact(&sOf.Flt, USBFILTERIDX_SERIAL_NUMBER_STR, &g_szString256[0], true, false ) == VERR_BUFFER_OVERFLOW);
+ TST_CHECK_EXPR(sOf.u64Pre == UINT64_C(0x1234567887654321)); TST_CHECK_EXPR(sOf.u64Post == UINT64_C(0x1234567887654321));
+ TST_CHECK_EXPR(USBFilterSetStringExact(&sOf.Flt, USBFILTERIDX_SERIAL_NUMBER_STR, &g_szString256[1], true, false ) == VERR_BUFFER_OVERFLOW);
+ TST_CHECK_EXPR(sOf.u64Pre == UINT64_C(0x1234567887654321)); TST_CHECK_EXPR(sOf.u64Post == UINT64_C(0x1234567887654321));
+ TST_CHECK_EXPR(USBFilterSetStringExact(&sOf.Flt, USBFILTERIDX_SERIAL_NUMBER_STR, &g_szString256[2], true, false ) == VINF_SUCCESS);
+ TST_CHECK_EXPR(sOf.u64Pre == UINT64_C(0x1234567887654321)); TST_CHECK_EXPR(sOf.u64Post == UINT64_C(0x1234567887654321));
+ TST_CHECK_EXPR(USBFilterSetStringExact(&sOf.Flt, USBFILTERIDX_SERIAL_NUMBER_STR, &g_szString256[3], true, false ) == VINF_SUCCESS);
+ TST_CHECK_EXPR(sOf.u64Pre == UINT64_C(0x1234567887654321)); TST_CHECK_EXPR(sOf.u64Post == UINT64_C(0x1234567887654321));
+ /* 0 + 1 */
+ TST_CHECK_EXPR(USBFilterSetStringExact(&sOf.Flt, USBFILTERIDX_SERIAL_NUMBER_STR, "", true, false ) == VINF_SUCCESS);
+ TST_CHECK_EXPR(sOf.u64Pre == UINT64_C(0x1234567887654321)); TST_CHECK_EXPR(sOf.u64Post == UINT64_C(0x1234567887654321));
+ TST_CHECK_EXPR(USBFilterSetStringExact(&sOf.Flt, USBFILTERIDX_PRODUCT_STR, &g_szString256[2], true, false ) == VINF_SUCCESS);
+ TST_CHECK_EXPR(sOf.u64Pre == UINT64_C(0x1234567887654321)); TST_CHECK_EXPR(sOf.u64Post == UINT64_C(0x1234567887654321));
+ TST_CHECK_EXPR(USBFilterSetStringExact(&sOf.Flt, USBFILTERIDX_PRODUCT_STR, &g_szString256[1], true, false ) == VERR_BUFFER_OVERFLOW);
+ TST_CHECK_EXPR(sOf.u64Pre == UINT64_C(0x1234567887654321)); TST_CHECK_EXPR(sOf.u64Post == UINT64_C(0x1234567887654321));
+ /* 0 + 2 */
+ TST_CHECK_EXPR(USBFilterSetStringExact(&sOf.Flt, USBFILTERIDX_PRODUCT_STR, &g_szString128[2], true, false ) == VINF_SUCCESS);
+ TST_CHECK_EXPR(sOf.u64Pre == UINT64_C(0x1234567887654321)); TST_CHECK_EXPR(sOf.u64Post == UINT64_C(0x1234567887654321));
+ TST_CHECK_EXPR(USBFilterSetStringExact(&sOf.Flt, USBFILTERIDX_SERIAL_NUMBER_STR, &g_szString128[1], true, false ) == VINF_SUCCESS);
+ TST_CHECK_EXPR(sOf.u64Pre == UINT64_C(0x1234567887654321)); TST_CHECK_EXPR(sOf.u64Post == UINT64_C(0x1234567887654321));
+ /* 3 */
+ TST_CHECK_EXPR(USBFilterSetStringExact(&sOf.Flt, USBFILTERIDX_SERIAL_NUMBER_STR, &g_szString64[0], true, false ) == VINF_SUCCESS);
+ TST_CHECK_EXPR(sOf.u64Pre == UINT64_C(0x1234567887654321)); TST_CHECK_EXPR(sOf.u64Post == UINT64_C(0x1234567887654321));
+ TST_CHECK_EXPR(USBFilterSetStringExact(&sOf.Flt, USBFILTERIDX_PRODUCT_STR, &g_szString64[0], true, false ) == VINF_SUCCESS);
+ TST_CHECK_EXPR(sOf.u64Pre == UINT64_C(0x1234567887654321)); TST_CHECK_EXPR(sOf.u64Post == UINT64_C(0x1234567887654321));
+ TST_CHECK_EXPR(USBFilterSetStringExact(&sOf.Flt, USBFILTERIDX_MANUFACTURER_STR, &g_szString128[4], true, false ) == VINF_SUCCESS);
+ TST_CHECK_EXPR(USBFilterSetStringExact(&sOf.Flt, USBFILTERIDX_MANUFACTURER_STR, &g_szString128[4], true, false ) == VINF_SUCCESS);
+ TST_CHECK_EXPR(USBFilterSetStringExact(&sOf.Flt, USBFILTERIDX_MANUFACTURER_STR, &g_szString128[3], true, false ) == VERR_BUFFER_OVERFLOW);
+ TST_CHECK_EXPR(sOf.u64Pre == UINT64_C(0x1234567887654321)); TST_CHECK_EXPR(sOf.u64Post == UINT64_C(0x1234567887654321));
+
+ /*
+ * Check for a string replacement bug.
+ */
+ USBFILTER Dev2;
+ USBFilterInit(&Dev2, USBFILTERTYPE_CAPTURE);
+ TST_CHECK_EXPR(USBFilterSetNumExact(&Dev2, USBFILTERIDX_VENDOR_ID, 0x19b6, true) == VINF_SUCCESS);
+ TST_CHECK_EXPR(USBFilterSetNumExact(&Dev2, USBFILTERIDX_PRODUCT_ID, 0x1024, true) == VINF_SUCCESS);
+ TST_CHECK_EXPR(USBFilterSetNumExact(&Dev2, USBFILTERIDX_DEVICE_REV, 0x0141, true) == VINF_SUCCESS);
+ TST_CHECK_EXPR(USBFilterSetNumExact(&Dev2, USBFILTERIDX_DEVICE_CLASS, 0, true) == VINF_SUCCESS);
+ TST_CHECK_EXPR(USBFilterSetNumExact(&Dev2, USBFILTERIDX_DEVICE_SUB_CLASS, 0, true) == VINF_SUCCESS);
+ TST_CHECK_EXPR(USBFilterSetNumExact(&Dev2, USBFILTERIDX_DEVICE_PROTOCOL, 0, true) == VINF_SUCCESS);
+ TST_CHECK_EXPR(USBFilterSetNumExact(&Dev2, USBFILTERIDX_PORT, 0x1, true) == VINF_SUCCESS);
+ TST_CHECK_EXPR(USBFilterSetStringExact(&Dev2, USBFILTERIDX_MANUFACTURER_STR, "Generic", true, false ) == VINF_SUCCESS);
+ TST_CHECK_EXPR(USBFilterSetStringExact(&Dev2, USBFILTERIDX_PRODUCT_STR, "Mass Storage Device", true, false ) == VINF_SUCCESS);
+ TST_CHECK_EXPR(USBFilterSetStringExact(&Dev2, USBFILTERIDX_MANUFACTURER_STR, "YBU1PPRS", true, false ) == VINF_SUCCESS);
+ TST_CHECK_EXPR(USBFilterGetNum(&Dev2, USBFILTERIDX_VENDOR_ID) == 0x19b6);
+ TST_CHECK_EXPR(USBFilterGetNum(&Dev2, USBFILTERIDX_PRODUCT_ID) == 0x1024);
+ TST_CHECK_EXPR(USBFilterGetNum(&Dev2, USBFILTERIDX_DEVICE_REV) == 0x0141);
+ TST_CHECK_EXPR(USBFilterGetNum(&Dev2, USBFILTERIDX_DEVICE_CLASS) == 0);
+ TST_CHECK_EXPR(USBFilterGetNum(&Dev2, USBFILTERIDX_DEVICE_SUB_CLASS) == 0);
+ TST_CHECK_EXPR(USBFilterGetNum(&Dev2, USBFILTERIDX_DEVICE_PROTOCOL) == 0);
+ TST_CHECK_EXPR(USBFilterGetNum(&Dev2, USBFILTERIDX_PORT) == 1);
+
+
+ /*
+ * Summary.
+ */
+ if (!cErrors)
+ RTPrintf(TESTCASE ": SUCCESS\n");
+ else
+ RTPrintf(TESTCASE ": FAILURE - %d errors\n", cErrors);
+ return !!cErrors;
+}
diff --git a/src/VBox/HostDrivers/VBoxUSB/win/Install/Makefile.kup b/src/VBox/HostDrivers/VBoxUSB/win/Install/Makefile.kup
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/src/VBox/HostDrivers/VBoxUSB/win/Install/Makefile.kup
diff --git a/src/VBox/HostDrivers/VBoxUSB/win/Install/USBInstall.cpp b/src/VBox/HostDrivers/VBoxUSB/win/Install/USBInstall.cpp
new file mode 100644
index 00000000..0debc44c
--- /dev/null
+++ b/src/VBox/HostDrivers/VBoxUSB/win/Install/USBInstall.cpp
@@ -0,0 +1,256 @@
+/* $Id: USBInstall.cpp $ */
+/** @file
+ * VBox host drivers - USB drivers - Filter & driver installation, Installation code.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include <iprt/win/windows.h>
+#include <iprt/win/setupapi.h>
+#include <newdev.h>
+#include <iprt/assert.h>
+#include <iprt/errcore.h>
+#include <iprt/initterm.h>
+#include <iprt/message.h>
+#include <iprt/param.h>
+#include <iprt/path.h>
+#include <iprt/process.h>
+#include <iprt/stream.h>
+#include <iprt/string.h>
+#include <iprt/utf16.h>
+
+#include <VBox/VBoxDrvCfg-win.h>
+
+
+/*********************************************************************************************************************************
+* Defined Constants And Macros *
+*********************************************************************************************************************************/
+/** The support service name. */
+#define SERVICE_NAME "VBoxUSBMon"
+/** Win32 Device name. */
+#define DEVICE_NAME "\\\\.\\VBoxUSBMon"
+/** NT Device name. */
+#define DEVICE_NAME_NT L"\\Device\\VBoxUSBMon"
+/** Win32 Symlink name. */
+#define DEVICE_NAME_DOS L"\\DosDevices\\VBoxUSBMon"
+
+
+/*********************************************************************************************************************************
+* Internal Functions *
+*********************************************************************************************************************************/
+int usblibOsCreateService(void);
+
+
+static DECLCALLBACK(void) vboxUsbLog(VBOXDRVCFG_LOG_SEVERITY_T enmSeverity, char *pszMsg, void *pvContext)
+{
+ RT_NOREF1(pvContext);
+ switch (enmSeverity)
+ {
+ case VBOXDRVCFG_LOG_SEVERITY_FLOW:
+ case VBOXDRVCFG_LOG_SEVERITY_REGULAR:
+ break;
+ case VBOXDRVCFG_LOG_SEVERITY_REL:
+ RTMsgInfo("%s", pszMsg);
+ break;
+ default:
+ break;
+ }
+}
+
+static DECLCALLBACK(void) vboxUsbPanic(void *pvPanic)
+{
+ RT_NOREF1(pvPanic);
+#ifndef DEBUG_bird
+ AssertFailed();
+#endif
+}
+
+
+int __cdecl main(int argc, char **argv)
+{
+ int rc = RTR3InitExe(argc, &argv, 0);
+ if (RT_FAILURE(rc))
+ return RTMsgInitFailure(rc);
+
+ RTMsgInfo("USB installation");
+
+ VBoxDrvCfgLoggerSet(vboxUsbLog, NULL);
+ VBoxDrvCfgPanicSet(vboxUsbPanic, NULL);
+
+ rc = usblibOsCreateService();
+ if (RT_SUCCESS(rc))
+ {
+ /* Build the path to the INF file: */
+ char szInfFile[RTPATH_MAX];
+ rc = RTProcGetExecutablePath(szInfFile, sizeof(szInfFile)) ? VINF_SUCCESS : VERR_BUFFER_OVERFLOW;
+ if (RT_SUCCESS(rc))
+ {
+ RTPathStripFilename(szInfFile);
+ rc = RTPathAppend(szInfFile, sizeof(szInfFile), "VBoxUSB.inf");
+ }
+ PRTUTF16 pwszInfFile = NULL;
+ if (RT_SUCCESS(rc))
+ rc = RTStrToUtf16(szInfFile, &pwszInfFile);
+ if (RT_SUCCESS(rc))
+ {
+ /* Install the INF file: */
+ HRESULT hr = VBoxDrvCfgInfInstall(pwszInfFile);
+ if (hr == S_OK)
+ RTMsgInfo("Installation successful!");
+ else
+ {
+ RTMsgError("Installation failed: %Rhrc", hr);
+ rc = VERR_GENERAL_FAILURE;
+ }
+
+ RTUtf16Free(pwszInfFile);
+ }
+ else
+ RTMsgError("Failed to construct INF path: %Rrc", rc);
+ }
+ else
+ RTMsgError("Service creation failed: %Rrc", rc);
+
+ return RT_SUCCESS(rc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
+}
+
+
+/**
+ * Changes the USB driver service to specified driver path.
+ *
+ * @returns 0 on success.
+ * @returns < 0 on failure.
+ */
+int usblibOsChangeService(const char *pszDriverPath)
+{
+ SC_HANDLE hSMgrCreate = OpenSCManager(NULL, NULL, SERVICE_CHANGE_CONFIG);
+ DWORD dwLastError = GetLastError();
+ int rc = RTErrConvertFromWin32(dwLastError);
+ AssertPtr(pszDriverPath);
+ AssertMsg(hSMgrCreate, ("OpenSCManager(,,create) failed rc=%d\n", dwLastError));
+ if (hSMgrCreate)
+ {
+ SC_HANDLE hService = OpenService(hSMgrCreate,
+ SERVICE_NAME,
+ GENERIC_ALL);
+ dwLastError = GetLastError();
+ if (hService == NULL)
+ {
+ AssertMsg(hService, ("OpenService failed! LastError=%Rwa, pszDriver=%s\n", dwLastError, pszDriverPath));
+ rc = RTErrConvertFromWin32(dwLastError);
+ }
+ else
+ {
+ /* We only gonna change the driver image path, the rest remains like it already is */
+ if (ChangeServiceConfig(hService,
+ SERVICE_NO_CHANGE,
+ SERVICE_NO_CHANGE,
+ SERVICE_NO_CHANGE,
+ pszDriverPath,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL))
+ {
+ RTPrintf("Changed service config to new driver path: %s\n", pszDriverPath);
+ }
+ else
+ {
+ AssertMsg(hService, ("ChangeServiceConfig failed! LastError=%Rwa, pszDriver=%s\n", dwLastError, pszDriverPath));
+ rc = RTErrConvertFromWin32(dwLastError);
+ }
+ if (hService != NULL)
+ CloseServiceHandle(hService);
+ }
+
+ CloseServiceHandle(hSMgrCreate);
+ }
+ return rc;
+}
+
+
+/**
+ * Creates the service.
+ *
+ * @returns 0 on success.
+ * @returns < 0 on failure.
+ */
+int usblibOsCreateService(void)
+{
+ /*
+ * Assume it didn't exist, so we'll create the service.
+ */
+ SC_HANDLE hSMgrCreate = OpenSCManager(NULL, NULL, SERVICE_CHANGE_CONFIG);
+ DWORD dwLastError = GetLastError();
+ int rc = RTErrConvertFromWin32(dwLastError);
+ AssertMsg(hSMgrCreate, ("OpenSCManager(,,create) failed rc=%d\n", dwLastError));
+ if (hSMgrCreate)
+ {
+ char szDriver[RTPATH_MAX];
+ rc = RTPathExecDir(szDriver, sizeof(szDriver) - sizeof("\\VBoxUSBMon.sys"));
+ if (RT_SUCCESS(rc))
+ {
+ strcat(szDriver, "\\VBoxUSBMon.sys");
+ RTPrintf("Creating USB monitor driver service with path %s ...\n", szDriver);
+ SC_HANDLE hService = CreateService(hSMgrCreate,
+ SERVICE_NAME,
+ "VBox USB Monitor Driver",
+ SERVICE_QUERY_STATUS,
+ SERVICE_KERNEL_DRIVER,
+ SERVICE_DEMAND_START,
+ SERVICE_ERROR_NORMAL,
+ szDriver,
+ NULL, NULL, NULL, NULL, NULL);
+ dwLastError = GetLastError();
+ if (dwLastError == ERROR_SERVICE_EXISTS)
+ {
+ RTPrintf("USB monitor driver service already exists, skipping creation.\n");
+ rc = usblibOsChangeService(szDriver);
+ }
+ else
+ {
+ AssertMsg(hService, ("CreateService failed! LastError=%Rwa, szDriver=%s\n", dwLastError, szDriver));
+ rc = RTErrConvertFromWin32(dwLastError);
+ if (hService != NULL)
+ CloseServiceHandle(hService);
+ }
+ }
+ CloseServiceHandle(hSMgrCreate);
+ }
+ return rc;
+}
diff --git a/src/VBox/HostDrivers/VBoxUSB/win/Install/USBUninstall.cpp b/src/VBox/HostDrivers/VBoxUSB/win/Install/USBUninstall.cpp
new file mode 100644
index 00000000..3dc0d6fe
--- /dev/null
+++ b/src/VBox/HostDrivers/VBoxUSB/win/Install/USBUninstall.cpp
@@ -0,0 +1,215 @@
+/* $Id: USBUninstall.cpp $ */
+/** @file
+ * VBox host drivers - USB drivers - Filter & driver uninstallation.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include <iprt/win/windows.h>
+#include <iprt/win/setupapi.h>
+#include <newdev.h>
+
+#include <iprt/assert.h>
+#include <iprt/errcore.h>
+#include <iprt/initterm.h>
+#include <iprt/message.h>
+#include <iprt/param.h>
+#include <iprt/path.h>
+#include <iprt/string.h>
+#include <VBox/VBoxDrvCfg-win.h>
+
+
+/*********************************************************************************************************************************
+* Defined Constants And Macros *
+*********************************************************************************************************************************/
+/** The support service name. */
+#define SERVICE_NAME "VBoxUSBMon"
+/** Win32 Device name. */
+#define DEVICE_NAME "\\\\.\\VBoxUSBMon"
+/** NT Device name. */
+#define DEVICE_NAME_NT L"\\Device\\VBoxUSBMon"
+/** Win32 Symlink name. */
+#define DEVICE_NAME_DOS L"\\DosDevices\\VBoxUSBMon"
+
+
+/*********************************************************************************************************************************
+* Internal Functions *
+*********************************************************************************************************************************/
+static int usblibOsStopService(void);
+static int usblibOsDeleteService(void);
+
+
+static DECLCALLBACK(void) vboxUsbLog(VBOXDRVCFG_LOG_SEVERITY_T enmSeverity, char *pszMsg, void *pvContext)
+{
+ RT_NOREF1(pvContext);
+ switch (enmSeverity)
+ {
+ case VBOXDRVCFG_LOG_SEVERITY_FLOW:
+ case VBOXDRVCFG_LOG_SEVERITY_REGULAR:
+ break;
+ case VBOXDRVCFG_LOG_SEVERITY_REL:
+ RTMsgInfo("%s", pszMsg);
+ break;
+ default:
+ break;
+ }
+}
+
+static DECLCALLBACK(void) vboxUsbPanic(void *pvPanic)
+{
+ RT_NOREF1(pvPanic);
+#ifndef DEBUG_bird
+ AssertFailed();
+#endif
+}
+
+
+int __cdecl main(int argc, char **argv)
+{
+ RTR3InitExeNoArguments(0);
+ if (argc != 1)
+ return RTMsgErrorExit(RTEXITCODE_SYNTAX, "This utility takes no arguments\n");
+ NOREF(argv);
+ RTMsgInfo("USB uninstallation\n");
+
+ VBoxDrvCfgLoggerSet(vboxUsbLog, NULL);
+ VBoxDrvCfgPanicSet(vboxUsbPanic, NULL);
+
+ usblibOsStopService();
+ usblibOsDeleteService();
+
+ HRESULT hr = VBoxDrvCfgInfUninstallAllF(L"USB", L"USB\\VID_80EE&PID_CAFE", SUOI_FORCEDELETE);
+ if (hr != S_OK)
+ return RTMsgErrorExitFailure("SetupUninstallOEMInf failed: %Rhrc\n", hr);
+
+ RTMsgInfo("USB uninstallation succeeded!");
+ return 0;
+}
+
+
+/**
+ * Stops a possibly running service.
+ *
+ * @returns 0 on success.
+ * @returns -1 on failure.
+ */
+static int usblibOsStopService(void)
+{
+ /*
+ * Assume it didn't exist, so we'll create the service.
+ */
+ int rc = -1;
+ SC_HANDLE hSMgr = OpenSCManager(NULL, NULL, SERVICE_STOP | SERVICE_QUERY_STATUS);
+ AssertMsg(hSMgr, ("OpenSCManager(,,delete) failed rc=%d\n", GetLastError()));
+ if (hSMgr)
+ {
+ SC_HANDLE hService = OpenService(hSMgr, SERVICE_NAME, SERVICE_STOP | SERVICE_QUERY_STATUS);
+ if (hService)
+ {
+ /*
+ * Stop the service.
+ */
+ SERVICE_STATUS Status;
+ QueryServiceStatus(hService, &Status);
+ if (Status.dwCurrentState == SERVICE_STOPPED)
+ rc = 0;
+ else if (ControlService(hService, SERVICE_CONTROL_STOP, &Status))
+ {
+ /*
+ * Wait for finish about 1 minute.
+ * It should be enough for work with driver verifier
+ */
+ int iWait = 600;
+ while (Status.dwCurrentState == SERVICE_STOP_PENDING && iWait-- > 0)
+ {
+ Sleep(100);
+ QueryServiceStatus(hService, &Status);
+ }
+ if (Status.dwCurrentState == SERVICE_STOPPED)
+ rc = 0;
+ else
+ AssertMsgFailed(("Failed to stop service. status=%d\n", Status.dwCurrentState));
+ }
+ else
+ AssertMsgFailed(("ControlService failed with LastError=%Rwa. status=%d\n", GetLastError(), Status.dwCurrentState));
+ CloseServiceHandle(hService);
+ }
+ else if (GetLastError() == ERROR_SERVICE_DOES_NOT_EXIST)
+ rc = 0;
+ else
+ AssertMsgFailed(("OpenService failed LastError=%Rwa\n", GetLastError()));
+ CloseServiceHandle(hSMgr);
+ }
+ return rc;
+}
+
+
+/**
+ * Deletes the service.
+ *
+ * @returns 0 on success.
+ * @returns -1 on failure.
+ */
+static int usblibOsDeleteService(void)
+{
+ /*
+ * Assume it didn't exist, so we'll create the service.
+ */
+ int rc = -1;
+ SC_HANDLE hSMgr = OpenSCManager(NULL, NULL, SERVICE_CHANGE_CONFIG);
+ AssertMsg(hSMgr, ("OpenSCManager(,,delete) failed rc=%d\n", GetLastError()));
+ if (hSMgr)
+ {
+ SC_HANDLE hService = OpenService(hSMgr, SERVICE_NAME, DELETE);
+ if (hService)
+ {
+ /*
+ * Delete the service.
+ */
+ if (DeleteService(hService))
+ rc = 0;
+ else
+ AssertMsgFailed(("DeleteService failed LastError=%Rwa\n", GetLastError()));
+ CloseServiceHandle(hService);
+ }
+ else if (GetLastError() == ERROR_SERVICE_DOES_NOT_EXIST)
+ rc = 0;
+ else
+ AssertMsgFailed(("OpenService failed LastError=%Rwa\n", GetLastError()));
+ CloseServiceHandle(hSMgr);
+ }
+ return rc;
+}
diff --git a/src/VBox/HostDrivers/VBoxUSB/win/Makefile.kmk b/src/VBox/HostDrivers/VBoxUSB/win/Makefile.kmk
new file mode 100644
index 00000000..4211147b
--- /dev/null
+++ b/src/VBox/HostDrivers/VBoxUSB/win/Makefile.kmk
@@ -0,0 +1,204 @@
+# $Id: Makefile.kmk $
+## @file
+# Sub-Makefile for the Windows USB drivers.
+#
+
+#
+# Copyright (C) 2006-2023 Oracle and/or its affiliates.
+#
+# This file is part of VirtualBox base platform packages, as
+# available from https://www.virtualbox.org.
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation, in version 3 of the
+# License.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, see <https://www.gnu.org/licenses>.
+#
+# The contents of this file may alternatively be used under the terms
+# of the Common Development and Distribution License Version 1.0
+# (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+# in the VirtualBox distribution, in which case the provisions of the
+# CDDL are applicable instead of those of the GPL.
+#
+# You may elect to license modified versions of this file under the
+# terms and conditions of either the GPL or the CDDL or both.
+#
+# SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+#
+
+SUB_DEPTH = ../../../../..
+include $(KBUILD_PATH)/subheader.kmk
+
+LIBRARIES.win += usbd
+SYSMODS.win += VBoxUSB VBoxUSBMon
+PROGRAMS.win += USBInstall USBUninstall USBTest
+INSTALLS.win += install-infs
+
+#
+# usbd
+#
+usbd_TEMPLATE = VBoxR0Drv
+usbd_SOURCES = usbd/usbd.def
+
+#
+# VBoxUSB
+#
+VBoxUSB_TEMPLATE = VBoxR0Drv
+ifdef VBOX_SIGNING_MODE
+ VBoxUSB_INSTTYPE = none
+ VBoxUSB_DEBUG_INSTTYPE = both
+endif
+VBoxUSB_SDKS = ReorderCompilerIncs $(VBOX_WINDDK) $(VBOX_WINPSDK_INCS)
+ifdef VBOX_WITH_AUTOMATIC_DEFS_QUOTING
+ VBoxUSB_DEFS := IN_RT_R0 IN_SUP_R0 VBOX_DBG_LOG_NAME="USBDev"
+else
+ VBoxUSB_DEFS := IN_RT_R0 IN_SUP_R0 VBOX_DBG_LOG_NAME=\"USBDev\"
+endif
+VBoxUSB_LDFLAGS.x86 = -Entry:DriverEntry@8
+VBoxUSB_LDFLAGS.amd64 = -Entry:DriverEntry
+VBoxUSB_SOURCES = \
+ dev/VBoxUsbDev.cpp \
+ dev/VBoxUsbRt.cpp \
+ dev/VBoxUsbPnP.cpp \
+ dev/VBoxUsbPwr.cpp \
+ cmn/VBoxUsbTool.cpp \
+ cmn/VBoxDrvTool.cpp \
+ dev/VBoxUsbDev.rc
+VBoxUSB_LIBS = \
+ $(PATH_SDK_$(VBOX_WINDDK)_LIB)/ntoskrnl.lib \
+ $(PATH_SDK_$(VBOX_WINDDK)_LIB)/hal.lib \
+ $(PATH_STAGE_LIB)/RuntimeR0Drv$(VBOX_SUFF_LIB) \
+ $(usbd_1_TARGET)
+
+#
+# VBoxUSBMon
+#
+VBoxUSBMon_TEMPLATE = VBoxR0Drv
+ifdef VBOX_SIGNING_MODE
+ VBoxUSBMon_INSTTYPE = none
+ VBoxUSBMon_DEBUG_INSTTYPE = both
+endif
+VBoxUSBMon_INCS := $(PATH_SUB_CURRENT)/..
+VBoxUSBMon_SDKS = ReorderCompilerIncs $(VBOX_WINDDK) $(VBOX_WINPSDK_INCS)
+VBoxUSBMon_DEFS = IN_RT_R0 IN_SUP_R0 NTDDI_WINNT=_NTDDI_VISTA VBOXUSBFILTERMGR_USB_SPINLOCK
+ifdef VBOX_WITH_AUTOMATIC_DEFS_QUOTING
+ VBoxUSBMon_DEFS += VBOX_DBG_LOG_NAME="USBMon"
+else
+ VBoxUSBMon_DEFS += VBOX_DBG_LOG_NAME=\"USBMon\"
+endif
+VBoxUSBMon_LDFLAGS.x86 = -Entry:DriverEntry@8
+VBoxUSBMon_LDFLAGS.amd64 = -Entry:DriverEntry
+ifdef VBOX_USBMON_WITH_FILTER_AUTOAPPLY
+ VBoxUSBMon_DEFS += VBOX_USBMON_WITH_FILTER_AUTOAPPLY
+endif
+VBoxUSBMon_SOURCES = \
+ mon/VBoxUsbMon.cpp \
+ mon/VBoxUsbFlt.cpp \
+ mon/VBoxUsbHook.cpp \
+ cmn/VBoxUsbTool.cpp \
+ cmn/VBoxDrvTool.cpp \
+ ../USBFilter.cpp \
+ ../VBoxUSBFilterMgr.cpp \
+ mon/VBoxUsbMon.rc
+VBoxUSBMon_LIBS = \
+ $(PATH_SDK_$(VBOX_WINDDK)_LIB)/ntoskrnl.lib \
+ $(PATH_SDK_$(VBOX_WINDDK)_LIB)/hal.lib \
+ $(PATH_STAGE_LIB)/RuntimeR0Drv$(VBOX_SUFF_LIB) \
+ $(usbd_1_TARGET)
+if1of ($(KBUILD_TYPE), debug)
+ VBoxUSBMon_DEFS += LOG_ENABLED VBOX_USB_WITH_VERBOSE_LOGGING
+endif
+
+#
+# Template for USBInstalls and friends.
+#
+TEMPLATE_VBoxUsbR3 = Template for USBInstalls, USBUninstall and USBTest
+TEMPLATE_VBoxUsbR3_EXTENDS = VBoxR3Exe
+TEMPLATE_VBoxUsbR3_SDKS = $(TEMPLATE_VBoxR3Exe_SDKS) ReorderCompilerIncs $(VBOX_WINPSDK) $(VBOX_WINDDK) VBoxWinNewDevLib
+TEMPLATE_VBoxUsbR3_CXXFLAGS = $(TEMPLATE_VBoxR3Exe_CXXFLAGS) -Gz
+TEMPLATE_VBoxUsbR3_CFLAGS = $(TEMPLATE_VBoxR3Exe_CFLAGS) -Gz
+TEMPLATE_VBoxUsbR3_LIBS = $(TEMPLATE_VBoxR3Exe_LIBS) \
+ $(PATH_STAGE_LIB)/VBoxDrvCfgExe$(VBOX_SUFF_LIB) \
+ $(LIB_RUNTIME)
+
+#
+# USBInstall
+#
+USBInstall_TEMPLATE = VBoxUsbR3
+USBInstall_SOURCES = Install/USBInstall.cpp
+
+#
+# USBUninstall
+#
+USBUninstall_TEMPLATE = VBoxUsbR3
+USBUninstall_SOURCES = Install/USBUninstall.cpp
+
+#
+# USBTest
+#
+USBTest_TEMPLATE = VBoxUsbR3
+USBTest_DEFS = IN_USBLIB
+USBTest_SOURCES = \
+ testcase/USBTest.cpp \
+ ../USBFilter.cpp
+
+#
+# Install the INF files.
+#
+install-infs_TEMPLATE = VBoxR0DrvInfCat
+install-infs_SOURCES = \
+ $(PATH_TARGET)/VBoxUSBCat.dir/VBoxUSB.inf \
+ $(PATH_TARGET)/VBoxUSBMonCat.dir/VBoxUSBMon.inf
+install-infs_CLEAN = $(install-infs_SOURCES)
+install-infs_BLDDIRS = \
+ $(PATH_TARGET)/VBoxUSBCat.dir \
+ $(PATH_TARGET)/VBoxUSBMonCat.dir
+
+$(PATH_TARGET)/VBoxUSBCat.dir/VBoxUSB.inf: $(PATH_SUB_CURRENT)/dev/VBoxUSB.inf $(MAKEFILE_CURRENT) | $$(dir $$@)
+ $(call MSG_GENERATE,install-infs,$@,$<)
+ $(call VBOX_EDIT_INF_FN,$<,$@)
+
+$(PATH_TARGET)/VBoxUSBMonCat.dir/VBoxUSBMon.inf: $(PATH_SUB_CURRENT)/mon/VBoxUSBMon.inf $(MAKEFILE_CURRENT) | $$(dir $$@)
+ $(call MSG_GENERATE,install-infs,$@,$<)
+ $(call VBOX_EDIT_INF_FN,$<,$@)
+
+ifdef VBOX_SIGNING_MODE
+ install-infs_SOURCES += \
+ $(PATH_TARGET)/VBoxUSBCat.dir/VBoxUSB.cat \
+ $(PATH_TARGET)/VBoxUSBCat.dir/VBoxUSB.cat=>VBoxUSB-PreW10.cat \
+ $(PATH_TARGET)/VBoxUSBCat.dir/VBoxUSB.sys \
+ $(PATH_TARGET)/VBoxUSBMonCat.dir/VBoxUSBMon.cat \
+ $(PATH_TARGET)/VBoxUSBMonCat.dir/VBoxUSBMon.cat=>VBoxUSBMon-PreW10.cat \
+ $(PATH_TARGET)/VBoxUSBMonCat.dir/VBoxUSBMon.sys
+
+ $(PATH_TARGET)/VBoxUSBCat.dir/VBoxUSB.sys: $$(VBoxUSB_1_TARGET) | $$(dir $$@)
+ $(INSTALL) -m 644 $< $(@D)
+
+ $(PATH_TARGET)/VBoxUSBCat.dir/VBoxUSB.cat: \
+ $(PATH_TARGET)/VBoxUSBCat.dir/VBoxUSB.inf \
+ $(PATH_TARGET)/VBoxUSBCat.dir/VBoxUSB.sys
+ $(call MSG_TOOL,Inf2Cat,VBoxUSB-inf,$@,$<)
+ $(call VBOX_MAKE_CAT_FN, $(@D),$@)
+
+ $(PATH_TARGET)/VBoxUSBMonCat.dir/VBoxUSBMon.sys: $$(VBoxUSBMon_1_TARGET) | $$(dir $$@)
+ $(INSTALL) -m 644 $< $(@D)
+
+ $(PATH_TARGET)/VBoxUSBMonCat.dir/VBoxUSBMon.cat: \
+ $(PATH_TARGET)/VBoxUSBMonCat.dir/VBoxUSBMon.inf \
+ $(PATH_TARGET)/VBoxUSBMonCat.dir/VBoxUSBMon.sys
+ $(call MSG_TOOL,Inf2Cat,VBoxUSBMon-inf,$@,$<)
+ $(call VBOX_MAKE_CAT_FN, $(@D),$@)
+
+endif # signing
+
+# generate rules
+include $(FILE_KBUILD_SUB_FOOTER)
+
diff --git a/src/VBox/HostDrivers/VBoxUSB/win/cmn/Makefile.kup b/src/VBox/HostDrivers/VBoxUSB/win/cmn/Makefile.kup
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/src/VBox/HostDrivers/VBoxUSB/win/cmn/Makefile.kup
diff --git a/src/VBox/HostDrivers/VBoxUSB/win/cmn/VBoxDrvTool.cpp b/src/VBox/HostDrivers/VBoxUSB/win/cmn/VBoxDrvTool.cpp
new file mode 100644
index 00000000..63ed595b
--- /dev/null
+++ b/src/VBox/HostDrivers/VBoxUSB/win/cmn/VBoxDrvTool.cpp
@@ -0,0 +1,247 @@
+/* $Id: VBoxDrvTool.cpp $ */
+/** @file
+ * Windows Driver R0 Tooling.
+ */
+
+/*
+ * Copyright (C) 2011-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+#include "VBoxDrvTool.h"
+
+#include <iprt/assert.h>
+#include <VBox/log.h>
+
+#include "../../../win/VBoxDbgLog.h"
+
+#define VBOXDRVTOOL_MEMTAG 'TDBV'
+
+static PVOID vboxDrvToolMemAlloc(SIZE_T cbBytes)
+{
+ PVOID pvMem = ExAllocatePoolWithTag(NonPagedPool, cbBytes, VBOXDRVTOOL_MEMTAG);
+ Assert(pvMem);
+ return pvMem;
+}
+
+static PVOID vboxDrvToolMemAllocZ(SIZE_T cbBytes)
+{
+ PVOID pvMem = vboxDrvToolMemAlloc(cbBytes);
+ if (pvMem)
+ {
+ RtlZeroMemory(pvMem, cbBytes);
+ }
+ return pvMem;
+}
+
+static VOID vboxDrvToolMemFree(PVOID pvMem)
+{
+ ExFreePoolWithTag(pvMem, VBOXDRVTOOL_MEMTAG);
+}
+
+VBOXDRVTOOL_DECL(NTSTATUS) VBoxDrvToolRegOpenKeyU(OUT PHANDLE phKey, IN PUNICODE_STRING pName, IN ACCESS_MASK fAccess)
+{
+ OBJECT_ATTRIBUTES ObjAttr;
+
+ InitializeObjectAttributes(&ObjAttr, pName, OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, NULL, NULL);
+
+ return ZwOpenKey(phKey, fAccess, &ObjAttr);
+}
+
+VBOXDRVTOOL_DECL(NTSTATUS) VBoxDrvToolRegOpenKey(OUT PHANDLE phKey, IN PWCHAR pName, IN ACCESS_MASK fAccess)
+{
+ UNICODE_STRING RtlStr;
+ RtlInitUnicodeString(&RtlStr, pName);
+
+ return VBoxDrvToolRegOpenKeyU(phKey, &RtlStr, fAccess);
+}
+
+VBOXDRVTOOL_DECL(NTSTATUS) VBoxDrvToolRegCloseKey(IN HANDLE hKey)
+{
+ return ZwClose(hKey);
+}
+
+VBOXDRVTOOL_DECL(NTSTATUS) VBoxDrvToolRegQueryValueDword(IN HANDLE hKey, IN PWCHAR pName, OUT PULONG pDword)
+{
+ struct
+ {
+ KEY_VALUE_PARTIAL_INFORMATION Info;
+ UCHAR Buf[32]; /* should be enough */
+ } Buf;
+ ULONG cbBuf;
+ UNICODE_STRING RtlStr;
+ RtlInitUnicodeString(&RtlStr, pName);
+ NTSTATUS Status = ZwQueryValueKey(hKey,
+ &RtlStr,
+ KeyValuePartialInformation,
+ &Buf.Info,
+ sizeof(Buf),
+ &cbBuf);
+ if (Status == STATUS_SUCCESS)
+ {
+ if (Buf.Info.Type == REG_DWORD)
+ {
+ Assert(Buf.Info.DataLength == 4);
+ *pDword = *((PULONG)Buf.Info.Data);
+ return STATUS_SUCCESS;
+ }
+ }
+
+ return STATUS_INVALID_PARAMETER;
+}
+
+VBOXDRVTOOL_DECL(NTSTATUS) VBoxDrvToolRegSetValueDword(IN HANDLE hKey, IN PWCHAR pName, OUT ULONG val)
+{
+ UNICODE_STRING RtlStr;
+ RtlInitUnicodeString(&RtlStr, pName);
+ return ZwSetValueKey(hKey, &RtlStr,
+ NULL, /* IN ULONG TitleIndex OPTIONAL, reserved */
+ REG_DWORD,
+ &val,
+ sizeof(val));
+}
+
+static NTSTATUS vboxDrvToolIoCompletionSetEvent(IN PDEVICE_OBJECT pDevObj, IN PIRP pIrp, IN PVOID pvContext)
+{
+ RT_NOREF2(pDevObj, pIrp);
+ PKEVENT pEvent = (PKEVENT)pvContext;
+ KeSetEvent(pEvent, 0, FALSE);
+ return STATUS_MORE_PROCESSING_REQUIRED;
+}
+
+VBOXDRVTOOL_DECL(NTSTATUS) VBoxDrvToolIoPostAsync(PDEVICE_OBJECT pDevObj, PIRP pIrp, PKEVENT pEvent)
+{
+ IoSetCompletionRoutine(pIrp, vboxDrvToolIoCompletionSetEvent, pEvent, TRUE, TRUE, TRUE);
+ return IoCallDriver(pDevObj, pIrp);
+}
+
+VBOXDRVTOOL_DECL(NTSTATUS) VBoxDrvToolIoPostSync(PDEVICE_OBJECT pDevObj, PIRP pIrp)
+{
+ KEVENT Event;
+ KeInitializeEvent(&Event, NotificationEvent, FALSE);
+ NTSTATUS Status = VBoxDrvToolIoPostAsync(pDevObj, pIrp, &Event);
+ if (Status == STATUS_PENDING)
+ {
+ KeWaitForSingleObject(&Event, Executive, KernelMode, FALSE, NULL);
+ Status = pIrp->IoStatus.Status;
+ }
+ return Status;
+}
+
+/* !!!NOTE: the caller MUST be the IRP owner!!! *
+ * !! one can not post threaded IRPs this way!! */
+VBOXDRVTOOL_DECL(NTSTATUS) VBoxDrvToolIoPostSyncWithTimeout(PDEVICE_OBJECT pDevObj, PIRP pIrp, ULONG dwTimeoutMs)
+{
+ KEVENT Event;
+ LOG(("post irp (0x%p) to DevObj(0x%p) with timeout (%u)", pIrp, pDevObj, dwTimeoutMs));
+
+ KeInitializeEvent(&Event, NotificationEvent, FALSE);
+ NTSTATUS Status = VBoxDrvToolIoPostAsync(pDevObj, pIrp, &Event);
+ if (Status == STATUS_PENDING)
+ {
+ LARGE_INTEGER Interval;
+ PLARGE_INTEGER pInterval = NULL;
+ if (dwTimeoutMs != RT_INDEFINITE_WAIT)
+ {
+ Interval.QuadPart = -(int64_t) dwTimeoutMs /* ms */ * 10000;
+ pInterval = &Interval;
+ }
+
+ Status = KeWaitForSingleObject(&Event, Executive, KernelMode, FALSE, pInterval);
+ if (Status == STATUS_TIMEOUT)
+ {
+ WARN(("irp (0x%p) to DevObj(0x%p) was not completed within timeout (%u), cancelling", pIrp, pDevObj, dwTimeoutMs));
+ if (!IoCancelIrp(pIrp))
+ {
+ /* this may happen, but this is something the caller with timeout is not expecting */
+ WARN(("IoCancelIrp failed"));
+ }
+
+ /* wait for the IRP to complete */
+ KeWaitForSingleObject(&Event, Executive, KernelMode, FALSE, NULL);
+ }
+ else
+ {
+ ASSERT_WARN(Status == STATUS_SUCCESS, ("uunexpected Status (0x%x)", Status));
+ }
+
+ /* by this time the IRP is completed */
+ Status = pIrp->IoStatus.Status;
+ LOG(("Pending IRP(0x%p) completed with status(0x%x)", pIrp, Status));
+ }
+ else
+ {
+ LOG(("IRP(0x%p) completed with status(0x%x)", pIrp, Status));
+ }
+ return Status;
+}
+
+VBOXDRVTOOL_DECL(VOID) VBoxDrvToolRefWaitEqual(PVBOXDRVTOOL_REF pRef, uint32_t u32Val)
+{
+ LARGE_INTEGER Interval;
+ Interval.QuadPart = -(int64_t) 2 /* ms */ * 10000;
+ uint32_t cRefs;
+ size_t loops = 0;
+ KTIMER kTimer;
+ NTSTATUS status = STATUS_SUCCESS;
+
+ KeInitializeTimer(&kTimer);
+
+ while ((cRefs = ASMAtomicReadU32(&pRef->cRefs)) > u32Val && loops < 256)
+ {
+ Assert(cRefs >= u32Val);
+ Assert(cRefs < UINT32_MAX/2);
+
+ KeSetTimer(&kTimer, Interval, NULL);
+ status = KeWaitForSingleObject(&kTimer, Executive, KernelMode, false, NULL);
+ Assert(NT_SUCCESS(status));
+ loops++;
+ }
+}
+
+VBOXDRVTOOL_DECL(NTSTATUS) VBoxDrvToolStrCopy(PUNICODE_STRING pDst, CONST PUNICODE_STRING pSrc)
+{
+ USHORT cbLength = pSrc->Length + sizeof (pDst->Buffer[0]);
+ pDst->Buffer = (PWCHAR)vboxDrvToolMemAlloc(cbLength);
+ Assert(pDst->Buffer);
+ if (pDst->Buffer)
+ {
+ RtlMoveMemory(pDst->Buffer, pSrc->Buffer, pSrc->Length);
+ pDst->Buffer[pSrc->Length / sizeof (pDst->Buffer[0])] = L'\0';
+ pDst->Length = pSrc->Length;
+ pDst->MaximumLength = cbLength;
+ return STATUS_SUCCESS;
+ }
+ return STATUS_NO_MEMORY;
+}
+
+VBOXDRVTOOL_DECL(VOID) VBoxDrvToolStrFree(PUNICODE_STRING pStr)
+{
+ vboxDrvToolMemFree(pStr->Buffer);
+}
diff --git a/src/VBox/HostDrivers/VBoxUSB/win/cmn/VBoxDrvTool.h b/src/VBox/HostDrivers/VBoxUSB/win/cmn/VBoxDrvTool.h
new file mode 100644
index 00000000..f953f8eb
--- /dev/null
+++ b/src/VBox/HostDrivers/VBoxUSB/win/cmn/VBoxDrvTool.h
@@ -0,0 +1,113 @@
+/* $Id: VBoxDrvTool.h $ */
+/** @file
+ * Windows Driver R0 Tooling.
+ */
+
+/*
+ * Copyright (C) 2011-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+#ifndef VBOX_INCLUDED_SRC_VBoxUSB_win_cmn_VBoxDrvTool_h
+#define VBOX_INCLUDED_SRC_VBoxUSB_win_cmn_VBoxDrvTool_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+#include <VBox/cdefs.h>
+#include <iprt/stdint.h>
+#include <iprt/assert.h>
+#include <iprt/asm.h>
+#include <iprt/nt/wdm.h>
+
+
+RT_C_DECLS_BEGIN
+
+#if 0
+/* enable this in case we include this in a dll*/
+# ifdef IN_VBOXDRVTOOL
+# define VBOXDRVTOOL_DECL(a_Type) DECLEXPORT(a_Type)
+# else
+# define VBOXDRVTOOL_DECL(a_Type) DECLIMPORT(a_Type)
+# endif
+#else
+/*enable this in case we include this in a static lib*/
+# define VBOXDRVTOOL_DECL(a_Type) a_Type VBOXCALL
+#endif
+
+VBOXDRVTOOL_DECL(NTSTATUS) VBoxDrvToolRegOpenKeyU(OUT PHANDLE phKey, IN PUNICODE_STRING pName, IN ACCESS_MASK fAccess);
+VBOXDRVTOOL_DECL(NTSTATUS) VBoxDrvToolRegOpenKey(OUT PHANDLE phKey, IN PWCHAR pName, IN ACCESS_MASK fAccess);
+VBOXDRVTOOL_DECL(NTSTATUS) VBoxDrvToolRegCloseKey(IN HANDLE hKey);
+VBOXDRVTOOL_DECL(NTSTATUS) VBoxDrvToolRegQueryValueDword(IN HANDLE hKey, IN PWCHAR pName, OUT PULONG pDword);
+VBOXDRVTOOL_DECL(NTSTATUS) VBoxDrvToolRegSetValueDword(IN HANDLE hKey, IN PWCHAR pName, OUT ULONG val);
+
+VBOXDRVTOOL_DECL(NTSTATUS) VBoxDrvToolIoPostAsync(PDEVICE_OBJECT pDevObj, PIRP pIrp, PKEVENT pEvent);
+VBOXDRVTOOL_DECL(NTSTATUS) VBoxDrvToolIoPostSync(PDEVICE_OBJECT pDevObj, PIRP pIrp);
+VBOXDRVTOOL_DECL(NTSTATUS) VBoxDrvToolIoPostSyncWithTimeout(PDEVICE_OBJECT pDevObj, PIRP pIrp, ULONG dwTimeoutMs);
+DECLINLINE(NTSTATUS) VBoxDrvToolIoComplete(PIRP pIrp, NTSTATUS Status, ULONG ulInfo)
+{
+ pIrp->IoStatus.Status = Status;
+ pIrp->IoStatus.Information = ulInfo;
+ IoCompleteRequest(pIrp, IO_NO_INCREMENT);
+ return Status;
+}
+
+typedef struct VBOXDRVTOOL_REF
+{
+ volatile uint32_t cRefs;
+} VBOXDRVTOOL_REF, *PVBOXDRVTOOL_REF;
+
+DECLINLINE(void) VBoxDrvToolRefInit(PVBOXDRVTOOL_REF pRef)
+{
+ pRef->cRefs = 1;
+}
+
+DECLINLINE(uint32_t) VBoxDrvToolRefRetain(PVBOXDRVTOOL_REF pRef)
+{
+ Assert(pRef->cRefs);
+ Assert(pRef->cRefs < UINT32_MAX / 2);
+ return ASMAtomicIncU32(&pRef->cRefs);
+}
+
+DECLINLINE(uint32_t) VBoxDrvToolRefRelease(PVBOXDRVTOOL_REF pRef)
+{
+ uint32_t cRefs = ASMAtomicDecU32(&pRef->cRefs);
+ Assert(cRefs < UINT32_MAX/2);
+ return cRefs;
+}
+
+VBOXDRVTOOL_DECL(VOID) VBoxDrvToolRefWaitEqual(PVBOXDRVTOOL_REF pRef, uint32_t u32Val);
+
+VBOXDRVTOOL_DECL(NTSTATUS) VBoxDrvToolStrCopy(PUNICODE_STRING pDst, CONST PUNICODE_STRING pSrc);
+VBOXDRVTOOL_DECL(VOID) VBoxDrvToolStrFree(PUNICODE_STRING pStr);
+
+RT_C_DECLS_END
+
+#endif /* !VBOX_INCLUDED_SRC_VBoxUSB_win_cmn_VBoxDrvTool_h */
+
diff --git a/src/VBox/HostDrivers/VBoxUSB/win/cmn/VBoxUsbIdc.h b/src/VBox/HostDrivers/VBoxUSB/win/cmn/VBoxUsbIdc.h
new file mode 100644
index 00000000..9bc69afb
--- /dev/null
+++ b/src/VBox/HostDrivers/VBoxUSB/win/cmn/VBoxUsbIdc.h
@@ -0,0 +1,95 @@
+/* $Id: VBoxUsbIdc.h $ */
+/** @file
+ * Windows USB Proxy - Monitor Driver communication interface.
+ */
+
+/*
+ * Copyright (C) 2011-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+#ifndef VBOX_INCLUDED_SRC_VBoxUSB_win_cmn_VBoxUsbIdc_h
+#define VBOX_INCLUDED_SRC_VBoxUSB_win_cmn_VBoxUsbIdc_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+#define VBOXUSBIDC_VERSION_MAJOR 1
+#define VBOXUSBIDC_VERSION_MINOR 0
+
+#define VBOXUSBIDC_INTERNAL_IOCTL_GET_VERSION CTL_CODE(FILE_DEVICE_UNKNOWN, 0x618, METHOD_NEITHER, FILE_WRITE_ACCESS)
+#define VBOXUSBIDC_INTERNAL_IOCTL_PROXY_STARTUP CTL_CODE(FILE_DEVICE_UNKNOWN, 0x619, METHOD_NEITHER, FILE_WRITE_ACCESS)
+#define VBOXUSBIDC_INTERNAL_IOCTL_PROXY_TEARDOWN CTL_CODE(FILE_DEVICE_UNKNOWN, 0x61A, METHOD_NEITHER, FILE_WRITE_ACCESS)
+#define VBOXUSBIDC_INTERNAL_IOCTL_PROXY_STATE_CHANGE CTL_CODE(FILE_DEVICE_UNKNOWN, 0x61B, METHOD_NEITHER, FILE_WRITE_ACCESS)
+
+typedef struct
+{
+ uint32_t u32Major;
+ uint32_t u32Minor;
+} VBOXUSBIDC_VERSION, *PVBOXUSBIDC_VERSION;
+
+typedef void *HVBOXUSBIDCDEV;
+
+/* the initial device state is USBDEVICESTATE_HELD_BY_PROXY */
+typedef struct VBOXUSBIDC_PROXY_STARTUP
+{
+ union
+ {
+ /* in: device PDO */
+ PDEVICE_OBJECT pPDO;
+ /* out: device handle to be used for subsequent USBSUP_PROXY_XXX calls */
+ HVBOXUSBIDCDEV hDev;
+ } u;
+} VBOXUSBIDC_PROXY_STARTUP, *PVBOXUSBIDC_PROXY_STARTUP;
+
+typedef struct VBOXUSBIDC_PROXY_TEARDOWN
+{
+ HVBOXUSBIDCDEV hDev;
+} VBOXUSBIDC_PROXY_TEARDOWN, *PVBOXUSBIDC_PROXY_TEARDOWN;
+
+typedef enum
+{
+ VBOXUSBIDC_PROXY_STATE_UNKNOWN = 0,
+ VBOXUSBIDC_PROXY_STATE_IDLE,
+ VBOXUSBIDC_PROXY_STATE_INITIAL = VBOXUSBIDC_PROXY_STATE_IDLE,
+ VBOXUSBIDC_PROXY_STATE_USED_BY_GUEST
+} VBOXUSBIDC_PROXY_STATE;
+
+typedef struct VBOXUSBIDC_PROXY_STATE_CHANGE
+{
+ HVBOXUSBIDCDEV hDev;
+ VBOXUSBIDC_PROXY_STATE enmState;
+} VBOXUSBIDC_PROXY_STATE_CHANGE, *PVBOXUSBIDC_PROXY_STATE_CHANGE;
+
+NTSTATUS VBoxUsbIdcInit();
+VOID VBoxUsbIdcTerm();
+NTSTATUS VBoxUsbIdcProxyStarted(PDEVICE_OBJECT pPDO, HVBOXUSBIDCDEV *phDev);
+NTSTATUS VBoxUsbIdcProxyStopped(HVBOXUSBIDCDEV hDev);
+
+#endif /* !VBOX_INCLUDED_SRC_VBoxUSB_win_cmn_VBoxUsbIdc_h */
diff --git a/src/VBox/HostDrivers/VBoxUSB/win/cmn/VBoxUsbTool.cpp b/src/VBox/HostDrivers/VBoxUSB/win/cmn/VBoxUsbTool.cpp
new file mode 100644
index 00000000..67383ea0
--- /dev/null
+++ b/src/VBox/HostDrivers/VBoxUSB/win/cmn/VBoxUsbTool.cpp
@@ -0,0 +1,428 @@
+/* $Id: VBoxUsbTool.cpp $ */
+/** @file
+ * Windows USB R0 Tooling.
+ */
+
+/*
+ * Copyright (C) 2011-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+#define INITGUID
+#include "VBoxUsbTool.h"
+#include <usbbusif.h>
+
+#include <iprt/assert.h>
+#include <iprt/string.h>
+#include <iprt/utf16.h>
+#include <VBox/log.h>
+#include <VBox/usblib.h>
+
+#include "../../../win/VBoxDbgLog.h"
+
+#define VBOXUSBTOOL_MEMTAG 'TUBV'
+
+static PVOID vboxUsbToolMemAlloc(SIZE_T cbBytes)
+{
+ PVOID pvMem = ExAllocatePoolWithTag(NonPagedPool, cbBytes, VBOXUSBTOOL_MEMTAG);
+ Assert(pvMem);
+ return pvMem;
+}
+
+static PVOID vboxUsbToolMemAllocZ(SIZE_T cbBytes)
+{
+ PVOID pvMem = vboxUsbToolMemAlloc(cbBytes);
+ if (pvMem)
+ {
+ RtlZeroMemory(pvMem, cbBytes);
+ }
+ return pvMem;
+}
+
+static VOID vboxUsbToolMemFree(PVOID pvMem)
+{
+ ExFreePoolWithTag(pvMem, VBOXUSBTOOL_MEMTAG);
+}
+
+VBOXUSBTOOL_DECL(PURB) VBoxUsbToolUrbAlloc(USHORT u16Function, USHORT cbSize)
+{
+ PURB pUrb = (PURB)vboxUsbToolMemAlloc(cbSize);
+ Assert(pUrb);
+ if (!pUrb)
+ return NULL;
+
+ pUrb->UrbHeader.Length = cbSize;
+ pUrb->UrbHeader.Function = u16Function;
+ return pUrb;
+}
+
+VBOXUSBTOOL_DECL(PURB) VBoxUsbToolUrbAllocZ(USHORT u16Function, USHORT cbSize)
+{
+ PURB pUrb = (PURB)vboxUsbToolMemAllocZ(cbSize);
+ Assert(pUrb);
+ if (!pUrb)
+ return NULL;
+
+ pUrb->UrbHeader.Length = cbSize;
+ pUrb->UrbHeader.Function = u16Function;
+ return pUrb;
+}
+
+VBOXUSBTOOL_DECL(PURB) VBoxUsbToolUrbReinit(PURB pUrb, USHORT cbSize, USHORT u16Function)
+{
+ Assert(pUrb->UrbHeader.Length == cbSize);
+ if (pUrb->UrbHeader.Length < cbSize)
+ return NULL;
+ pUrb->UrbHeader.Length = cbSize;
+ pUrb->UrbHeader.Function = u16Function;
+ return pUrb;
+}
+
+VBOXUSBTOOL_DECL(VOID) VBoxUsbToolUrbFree(PURB pUrb)
+{
+ vboxUsbToolMemFree(pUrb);
+}
+
+VBOXUSBTOOL_DECL(NTSTATUS) VBoxUsbToolUrbPost(PDEVICE_OBJECT pDevObj, PURB pUrb, ULONG dwTimeoutMs)
+{
+ if (dwTimeoutMs == RT_INDEFINITE_WAIT)
+ return VBoxUsbToolIoInternalCtlSendSync(pDevObj, IOCTL_INTERNAL_USB_SUBMIT_URB, pUrb, NULL);
+ return VBoxUsbToolIoInternalCtlSendSyncWithTimeout(pDevObj, IOCTL_INTERNAL_USB_SUBMIT_URB, pUrb, NULL, dwTimeoutMs);
+}
+
+VBOXUSBTOOL_DECL(NTSTATUS) VBoxUsbToolGetDescriptor(PDEVICE_OBJECT pDevObj, void *pvBuffer, int cbBuffer, int Type, int iIndex, int LangId, ULONG dwTimeoutMs)
+{
+ NTSTATUS Status;
+ USHORT cbUrb = sizeof (struct _URB_CONTROL_DESCRIPTOR_REQUEST);
+ PURB pUrb = VBoxUsbToolUrbAllocZ(URB_FUNCTION_GET_DESCRIPTOR_FROM_DEVICE, cbUrb);
+ if (!pUrb)
+ {
+ WARN(("allocating URB failed"));
+ return STATUS_INSUFFICIENT_RESOURCES;
+ }
+
+ PUSB_COMMON_DESCRIPTOR pCmn = (PUSB_COMMON_DESCRIPTOR)pvBuffer;
+ pCmn->bLength = cbBuffer;
+ pCmn->bDescriptorType = Type;
+
+ pUrb->UrbHeader.Function = URB_FUNCTION_GET_DESCRIPTOR_FROM_DEVICE;
+ pUrb->UrbHeader.Length = sizeof(struct _URB_CONTROL_DESCRIPTOR_REQUEST);
+ pUrb->UrbControlDescriptorRequest.TransferBufferLength = cbBuffer;
+ pUrb->UrbControlDescriptorRequest.TransferBuffer = pvBuffer;
+ pUrb->UrbControlDescriptorRequest.Index = (UCHAR)iIndex;
+ pUrb->UrbControlDescriptorRequest.DescriptorType = (UCHAR)Type;
+ pUrb->UrbControlDescriptorRequest.LanguageId = (USHORT)LangId;
+
+ Status = VBoxUsbToolUrbPost(pDevObj, pUrb, dwTimeoutMs);
+ ASSERT_WARN(Status == STATUS_SUCCESS, ("VBoxUsbToolUrbPost failed Status (0x%x)", Status));
+
+ VBoxUsbToolUrbFree(pUrb);
+
+ return Status;
+}
+
+VBOXUSBTOOL_DECL(VOID) VBoxUsbToolStringDescriptorToUnicodeString(PUSB_STRING_DESCRIPTOR pDr, PUNICODE_STRING pUnicode)
+{
+ /* for some reason the string dr sometimes contains a non-null terminated string
+ * although we zeroed up the complete descriptor buffer
+ * this is why RtlInitUnicodeString won't work
+ * we need to init the scting length based on dr length */
+ pUnicode->Buffer = pDr->bString;
+ pUnicode->Length = pUnicode->MaximumLength = pDr->bLength - RT_OFFSETOF(USB_STRING_DESCRIPTOR, bString);
+}
+
+VBOXUSBTOOL_DECL(NTSTATUS) VBoxUsbToolGetStringDescriptor(PDEVICE_OBJECT pDevObj, char *pszResult, ULONG cbResult,
+ int iIndex, int LangId, ULONG dwTimeoutMs)
+{
+ char aBuf[MAXIMUM_USB_STRING_LENGTH];
+ AssertCompile(sizeof (aBuf) <= UINT8_MAX);
+ UCHAR cbBuf = (UCHAR)sizeof (aBuf);
+ PUSB_STRING_DESCRIPTOR pDr = (PUSB_STRING_DESCRIPTOR)&aBuf;
+
+ Assert(pszResult);
+ *pszResult = 0;
+
+ memset(pDr, 0, cbBuf);
+ pDr->bLength = cbBuf;
+ pDr->bDescriptorType = USB_STRING_DESCRIPTOR_TYPE;
+
+ NTSTATUS Status = VBoxUsbToolGetDescriptor(pDevObj, pDr, cbBuf, USB_STRING_DESCRIPTOR_TYPE, iIndex, LangId, dwTimeoutMs);
+ if (NT_SUCCESS(Status))
+ {
+ if (pDr->bLength >= sizeof (USB_STRING_DESCRIPTOR))
+ {
+ int rc = RTUtf16ToUtf8Ex(pDr->bString, (pDr->bLength - RT_OFFSETOF(USB_STRING_DESCRIPTOR, bString)) / sizeof(RTUTF16),
+ &pszResult, cbResult, NULL /*pcch*/);
+ if (RT_SUCCESS(rc))
+ {
+ USBLibPurgeEncoding(pszResult);
+ Status = STATUS_SUCCESS;
+ }
+ else
+ Status = STATUS_UNSUCCESSFUL;
+ }
+ else
+ Status = STATUS_INVALID_PARAMETER;
+ }
+ return Status;
+}
+
+VBOXUSBTOOL_DECL(NTSTATUS) VBoxUsbToolGetLangID(PDEVICE_OBJECT pDevObj, int *pLangId, ULONG dwTimeoutMs)
+{
+ char aBuf[MAXIMUM_USB_STRING_LENGTH];
+ AssertCompile(sizeof (aBuf) <= UINT8_MAX);
+ UCHAR cbBuf = (UCHAR)sizeof (aBuf);
+ PUSB_STRING_DESCRIPTOR pDr = (PUSB_STRING_DESCRIPTOR)&aBuf;
+
+ Assert(pLangId);
+ *pLangId = 0;
+
+ memset(pDr, 0, cbBuf);
+ pDr->bLength = cbBuf;
+ pDr->bDescriptorType = USB_STRING_DESCRIPTOR_TYPE;
+
+ NTSTATUS Status = VBoxUsbToolGetDescriptor(pDevObj, pDr, cbBuf, USB_STRING_DESCRIPTOR_TYPE, 0, 0, dwTimeoutMs);
+ if (NT_SUCCESS(Status))
+ {
+ /* Just grab the first lang ID if available. In 99% cases, it will be US English (0x0409).*/
+ if (pDr->bLength >= sizeof (USB_STRING_DESCRIPTOR))
+ {
+ AssertCompile(sizeof (pDr->bString[0]) == sizeof (uint16_t));
+ *pLangId = pDr->bString[0];
+ Status = STATUS_SUCCESS;
+ }
+ else
+ {
+ Status = STATUS_INVALID_PARAMETER;
+ }
+ }
+ return Status;
+}
+
+VBOXUSBTOOL_DECL(NTSTATUS) VBoxUsbToolGetDeviceSpeed(PDEVICE_OBJECT pDevObj, BOOLEAN *pbIsHigh)
+{
+ Assert(pbIsHigh);
+ *pbIsHigh = FALSE;
+
+ PIRP pIrp = IoAllocateIrp(pDevObj->StackSize, FALSE);
+ Assert(pIrp);
+ if (!pIrp)
+ {
+ return STATUS_INSUFFICIENT_RESOURCES;
+ }
+
+ USB_BUS_INTERFACE_USBDI_V1 BusIf;
+ PIO_STACK_LOCATION pSl = IoGetNextIrpStackLocation(pIrp);
+ pSl->MajorFunction = IRP_MJ_PNP;
+ pSl->MinorFunction = IRP_MN_QUERY_INTERFACE;
+ pSl->Parameters.QueryInterface.InterfaceType = &USB_BUS_INTERFACE_USBDI_GUID;
+ pSl->Parameters.QueryInterface.Size = sizeof (BusIf);
+ pSl->Parameters.QueryInterface.Version = USB_BUSIF_USBDI_VERSION_1;
+ pSl->Parameters.QueryInterface.Interface = (PINTERFACE)&BusIf;
+ pSl->Parameters.QueryInterface.InterfaceSpecificData = NULL;
+
+ pIrp->IoStatus.Status = STATUS_NOT_SUPPORTED;
+
+ NTSTATUS Status = VBoxDrvToolIoPostSync(pDevObj, pIrp);
+ Assert(NT_SUCCESS(Status) || Status == STATUS_NOT_SUPPORTED);
+ if (NT_SUCCESS(Status))
+ {
+ *pbIsHigh = BusIf.IsDeviceHighSpeed(BusIf.BusContext);
+ BusIf.InterfaceDereference(BusIf.BusContext);
+ }
+ IoFreeIrp(pIrp);
+
+ return Status;
+}
+
+VBOXUSBTOOL_DECL(NTSTATUS) VBoxUsbToolPipeClear(PDEVICE_OBJECT pDevObj, HANDLE hPipe, bool fReset)
+{
+ if (!hPipe)
+ {
+ Log(("Resetting the control pipe??\n"));
+ return STATUS_SUCCESS;
+ }
+ USHORT u16Function = fReset ? URB_FUNCTION_RESET_PIPE : URB_FUNCTION_ABORT_PIPE;
+ PURB pUrb = VBoxUsbToolUrbAlloc(u16Function, sizeof (struct _URB_PIPE_REQUEST));
+ if (!pUrb)
+ {
+ AssertMsgFailed((__FUNCTION__": VBoxUsbToolUrbAlloc failed!\n"));
+ return STATUS_INSUFFICIENT_RESOURCES;
+ }
+ pUrb->UrbPipeRequest.PipeHandle = hPipe;
+ pUrb->UrbPipeRequest.Reserved = 0;
+
+ NTSTATUS Status = VBoxUsbToolUrbPost(pDevObj, pUrb, RT_INDEFINITE_WAIT);
+ if (!NT_SUCCESS(Status) || !USBD_SUCCESS(pUrb->UrbHeader.Status))
+ {
+ AssertMsgFailed((__FUNCTION__": vboxUsbToolRequest failed with %x (%x)\n", Status, pUrb->UrbHeader.Status));
+ }
+
+ VBoxUsbToolUrbFree(pUrb);
+
+ return Status;
+}
+
+VBOXUSBTOOL_DECL(NTSTATUS) VBoxUsbToolCurrentFrame(PDEVICE_OBJECT pDevObj, PIRP pIrp, PULONG piFrame)
+{
+ struct _URB_GET_CURRENT_FRAME_NUMBER Urb;
+ Urb.Hdr.Function = URB_FUNCTION_GET_CURRENT_FRAME_NUMBER;
+ Urb.Hdr.Length = sizeof(Urb);
+ Urb.FrameNumber = (ULONG)-1;
+
+ Assert(piFrame);
+ *piFrame = (ULONG)-1;
+
+ PIO_STACK_LOCATION pSl = IoGetNextIrpStackLocation(pIrp);
+ pSl->MajorFunction = IRP_MJ_INTERNAL_DEVICE_CONTROL;
+ pSl->Parameters.DeviceIoControl.IoControlCode = IOCTL_INTERNAL_USB_SUBMIT_URB;
+ pSl->Parameters.Others.Argument1 = (PVOID)&Urb;
+ pSl->Parameters.Others.Argument2 = NULL;
+
+ NTSTATUS Status = VBoxUsbToolUrbPost(pDevObj, (PURB)&Urb, RT_INDEFINITE_WAIT);
+ Assert(NT_SUCCESS(Status));
+ if (NT_SUCCESS(Status))
+ {
+ *piFrame = Urb.FrameNumber;
+ }
+
+ return Status;
+}
+
+VBOXUSBTOOL_DECL(NTSTATUS) VBoxUsbToolDevUnconfigure(PDEVICE_OBJECT pDevObj)
+{
+ USHORT cbUrb = sizeof (struct _URB_SELECT_CONFIGURATION);
+ PURB pUrb = VBoxUsbToolUrbAlloc(URB_FUNCTION_SELECT_CONFIGURATION, cbUrb);
+ Assert(pUrb);
+ if (!pUrb)
+ return STATUS_INSUFFICIENT_RESOURCES;
+
+ UsbBuildSelectConfigurationRequest(pUrb, (USHORT)cbUrb, NULL);
+
+ NTSTATUS Status = VBoxUsbToolUrbPost(pDevObj, pUrb, RT_INDEFINITE_WAIT);
+ Assert(NT_SUCCESS(Status));
+
+ VBoxUsbToolUrbFree(pUrb);
+
+ return Status;
+}
+
+VBOXUSBTOOL_DECL(PIRP) VBoxUsbToolIoBuildAsyncInternalCtl(PDEVICE_OBJECT pDevObj, ULONG uCtl, void *pvArg1, void *pvArg2)
+{
+ PIRP pIrp = IoAllocateIrp(pDevObj->StackSize, FALSE);
+ Assert(pIrp);
+ if (!pIrp)
+ {
+ return NULL;
+ }
+
+ pIrp->IoStatus.Status = STATUS_SUCCESS;
+ pIrp->IoStatus.Information = NULL;
+
+ PIO_STACK_LOCATION pSl = IoGetNextIrpStackLocation(pIrp);
+ pSl->MajorFunction = IRP_MJ_INTERNAL_DEVICE_CONTROL;
+ pSl->MinorFunction = 0;
+ pSl->Parameters.DeviceIoControl.IoControlCode = uCtl;
+ pSl->Parameters.Others.Argument1 = pvArg1;
+ pSl->Parameters.Others.Argument2 = pvArg2;
+ return pIrp;
+}
+
+VBOXUSBTOOL_DECL(NTSTATUS) VBoxUsbToolIoInternalCtlSendSyncWithTimeout(PDEVICE_OBJECT pDevObj, ULONG uCtl,
+ void *pvArg1, void *pvArg2, ULONG dwTimeoutMs)
+{
+ /* since we're going to cancel the irp on timeout, we should allocate our own IRP rather than using the threaded one
+ * */
+ PIRP pIrp = VBoxUsbToolIoBuildAsyncInternalCtl(pDevObj, uCtl, pvArg1, pvArg2);
+ if (!pIrp)
+ {
+ WARN(("VBoxUsbToolIoBuildAsyncInternalCtl failed"));
+ return STATUS_INSUFFICIENT_RESOURCES;
+ }
+
+ NTSTATUS Status = VBoxDrvToolIoPostSyncWithTimeout(pDevObj, pIrp, dwTimeoutMs);
+
+ IoFreeIrp(pIrp);
+
+ return Status;
+}
+
+VBOXUSBTOOL_DECL(NTSTATUS) VBoxUsbToolIoInternalCtlSendAsync(PDEVICE_OBJECT pDevObj, ULONG uCtl, void *pvArg1, void *pvArg2,
+ PKEVENT pEvent, PIO_STATUS_BLOCK pIoStatus)
+{
+ NTSTATUS Status;
+ PIRP pIrp;
+ PIO_STACK_LOCATION pSl;
+ Assert(KeGetCurrentIrql() == PASSIVE_LEVEL);
+
+ pIrp = IoBuildDeviceIoControlRequest(uCtl, pDevObj, NULL, 0, NULL, 0, TRUE, pEvent, pIoStatus);
+ if (!pIrp)
+ {
+ WARN(("IoBuildDeviceIoControlRequest failed!!\n"));
+ pIoStatus->Status = STATUS_INSUFFICIENT_RESOURCES;
+ pIoStatus->Information = 0;
+ return STATUS_INSUFFICIENT_RESOURCES;
+ }
+
+ /* Get the next stack location as that is used for the new irp */
+ pSl = IoGetNextIrpStackLocation(pIrp);
+ pSl->Parameters.Others.Argument1 = pvArg1;
+ pSl->Parameters.Others.Argument2 = pvArg2;
+
+ Status = IoCallDriver(pDevObj, pIrp);
+
+ return Status;
+}
+
+VBOXUSBTOOL_DECL(NTSTATUS) VBoxUsbToolIoInternalCtlSendSync(PDEVICE_OBJECT pDevObj, ULONG uCtl, void *pvArg1, void *pvArg2)
+{
+ IO_STATUS_BLOCK IoStatus = {0};
+ KEVENT Event;
+ NTSTATUS Status;
+
+ KeInitializeEvent(&Event, NotificationEvent, FALSE);
+
+ LOG(("Sending sync Ctl pDevObj(0x%p), uCtl(0x%x), pvArg1(0x%p), pvArg2(0x%p)", pDevObj, uCtl, pvArg1, pvArg2));
+
+ Status = VBoxUsbToolIoInternalCtlSendAsync(pDevObj, uCtl, pvArg1, pvArg2, &Event, &IoStatus);
+
+ if (Status == STATUS_PENDING)
+ {
+ LOG(("VBoxUsbToolIoInternalCtlSendAsync returned pending for pDevObj(0x%p)", pDevObj));
+ KeWaitForSingleObject(&Event, Executive, KernelMode, FALSE, NULL);
+ Status = IoStatus.Status;
+ LOG(("Pending VBoxUsbToolIoInternalCtlSendAsync completed with Status (0x%x) for pDevObj(0x%p)", Status, pDevObj));
+ }
+ else
+ {
+ LOG(("VBoxUsbToolIoInternalCtlSendAsync completed with Status (0x%x) for pDevObj(0x%p)", Status, pDevObj));
+ }
+
+ return Status;
+}
diff --git a/src/VBox/HostDrivers/VBoxUSB/win/cmn/VBoxUsbTool.h b/src/VBox/HostDrivers/VBoxUSB/win/cmn/VBoxUsbTool.h
new file mode 100644
index 00000000..e6c6c236
--- /dev/null
+++ b/src/VBox/HostDrivers/VBoxUSB/win/cmn/VBoxUsbTool.h
@@ -0,0 +1,89 @@
+/* $Id: VBoxUsbTool.h $ */
+/** @file
+ * Windows USB R0 Tooling.
+ */
+
+/*
+ * Copyright (C) 2011-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+#ifndef VBOX_INCLUDED_SRC_VBoxUSB_win_cmn_VBoxUsbTool_h
+#define VBOX_INCLUDED_SRC_VBoxUSB_win_cmn_VBoxUsbTool_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+#include "VBoxDrvTool.h"
+
+RT_C_DECLS_BEGIN
+#pragma warning( disable : 4200 )
+#include <usbdi.h>
+#pragma warning( default : 4200 )
+#include <usbdlib.h>
+
+RT_C_DECLS_END
+
+#if 0
+/* enable this in case we include this in a dll*/
+# ifdef IN_VBOXUSBTOOL
+# define VBOXUSBTOOL_DECL(a_Type) DECLEXPORT(a_Type)
+# else
+# define VBOXUSBTOOL_DECL(a_Type) DECLIMPORT(a_Type)
+# endif
+#else
+/*enable this in case we include this in a static lib*/
+# define VBOXUSBTOOL_DECL(a_Type) a_Type VBOXCALL
+#endif
+
+
+RT_C_DECLS_BEGIN
+
+VBOXUSBTOOL_DECL(PURB) VBoxUsbToolUrbAlloc(USHORT u16Function, USHORT cbSize);
+VBOXUSBTOOL_DECL(PURB) VBoxUsbToolUrbAllocZ(USHORT u16Function, USHORT cbSize);
+VBOXUSBTOOL_DECL(PURB) VBoxUsbToolUrbReinit(PURB pUrb, USHORT cbSize, USHORT u16Function);
+VBOXUSBTOOL_DECL(VOID) VBoxUsbToolUrbFree(PURB pUrb);
+VBOXUSBTOOL_DECL(NTSTATUS) VBoxUsbToolUrbPost(PDEVICE_OBJECT pDevObj, PURB pUrb, ULONG dwTimeoutMs);
+VBOXUSBTOOL_DECL(NTSTATUS) VBoxUsbToolGetDescriptor(PDEVICE_OBJECT pDevObj, void *pvBuffer, int cbBuffer, int Type, int iIndex, int LangId, ULONG dwTimeoutMs);
+VBOXUSBTOOL_DECL(NTSTATUS) VBoxUsbToolGetStringDescriptor(PDEVICE_OBJECT pDevObj, char *pszResult, ULONG cbResult, int iIndex, int LangId, ULONG dwTimeoutMs);
+VBOXUSBTOOL_DECL(NTSTATUS) VBoxUsbToolGetLangID(PDEVICE_OBJECT pDevObj, int *pLangId, ULONG dwTimeoutMs);
+VBOXUSBTOOL_DECL(NTSTATUS) VBoxUsbToolGetDeviceSpeed(PDEVICE_OBJECT pDevObj, BOOLEAN *pbIsHigh);
+VBOXUSBTOOL_DECL(NTSTATUS) VBoxUsbToolPipeClear(PDEVICE_OBJECT pDevObj, HANDLE hPipe, bool fReset);
+VBOXUSBTOOL_DECL(NTSTATUS) VBoxUsbToolCurrentFrame(PDEVICE_OBJECT pDevObj, PIRP pIrp, PULONG piFrame);
+VBOXUSBTOOL_DECL(NTSTATUS) VBoxUsbToolDevUnconfigure(PDEVICE_OBJECT pDevObj);
+VBOXUSBTOOL_DECL(NTSTATUS) VBoxUsbToolIoInternalCtlSendAsync(PDEVICE_OBJECT pDevObj, ULONG uCtl, void *pvArg1, void *pvArg2, PKEVENT pEvent, PIO_STATUS_BLOCK pIoStatus);
+VBOXUSBTOOL_DECL(NTSTATUS) VBoxUsbToolIoInternalCtlSendSync(PDEVICE_OBJECT pDevObj, ULONG uCtl, void *pvArg1, void *pvArg2);
+VBOXUSBTOOL_DECL(PIRP) VBoxUsbToolIoBuildAsyncInternalCtl(PDEVICE_OBJECT pDevObj, ULONG uCtl, void *pvArg1, void *pvArg2);
+VBOXUSBTOOL_DECL(NTSTATUS) VBoxUsbToolIoInternalCtlSendSyncWithTimeout(PDEVICE_OBJECT pDevObj, ULONG uCtl, void *pvArg1, void *pvArg2, ULONG dwTimeoutMs);
+VBOXUSBTOOL_DECL(VOID) VBoxUsbToolStringDescriptorToUnicodeString(PUSB_STRING_DESCRIPTOR pDr, PUNICODE_STRING pUnicode);
+
+
+RT_C_DECLS_END
+
+#endif /* !VBOX_INCLUDED_SRC_VBoxUSB_win_cmn_VBoxUsbTool_h */
diff --git a/src/VBox/HostDrivers/VBoxUSB/win/dev/Makefile.kup b/src/VBox/HostDrivers/VBoxUSB/win/dev/Makefile.kup
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/src/VBox/HostDrivers/VBoxUSB/win/dev/Makefile.kup
diff --git a/src/VBox/HostDrivers/VBoxUSB/win/dev/VBoxUSB.inf b/src/VBox/HostDrivers/VBoxUSB/win/dev/VBoxUSB.inf
new file mode 100644
index 00000000..9b580b98
--- /dev/null
+++ b/src/VBox/HostDrivers/VBoxUSB/win/dev/VBoxUSB.inf
@@ -0,0 +1,88 @@
+; $Id: VBoxUSB.inf $
+;; @file
+; VBox host drivers - USB drivers - Win32 USB device
+;
+
+;
+; Copyright (C) 2011-2023 Oracle and/or its affiliates.
+;
+; This file is part of VirtualBox base platform packages, as
+; available from https://www.virtualbox.org.
+;
+; This program is free software; you can redistribute it and/or
+; modify it under the terms of the GNU General Public License
+; as published by the Free Software Foundation, in version 3 of the
+; License.
+;
+; This program is distributed in the hope that it will be useful, but
+; WITHOUT ANY WARRANTY; without even the implied warranty of
+; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+; General Public License for more details.
+;
+; You should have received a copy of the GNU General Public License
+; along with this program; if not, see <https://www.gnu.org/licenses>.
+;
+; The contents of this file may alternatively be used under the terms
+; of the Common Development and Distribution License Version 1.0
+; (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+; in the VirtualBox distribution, in which case the provisions of the
+; CDDL are applicable instead of those of the GPL.
+;
+; You may elect to license modified versions of this file under the
+; terms and conditions of either the GPL or the CDDL or both.
+;
+; SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+;
+
+[Version]
+Signature="$Windows NT$"
+Class=USB
+ClassGUID={36FC9E60-C465-11CF-8056-444553540000}
+provider=%ORACLE%
+;edit-DriverVer=08/26/2008,2.00.0000
+;cat CatalogFile=VBoxUSB.cat
+
+[SourceDisksNames]
+1=%Disk_Description%,,,
+
+[SourceDisksFiles]
+VBoxUSB.sys = 1
+
+[Manufacturer]
+%ORACLE%=VBoxUSB@COMMA-NT-ARCH@
+
+[VBoxUSB@DOT-NT-ARCH@]
+%USB\VID_80EE&PID_CAFE.DeviceDesc%=VBoxUSB.Dev, USB\VID_80EE&PID_CAFE
+
+[DestinationDirs]
+VBoxUSB.Files.Ext = 10,System32\Drivers
+
+[VBoxUSB.Dev.NT]
+CopyFiles=VBoxUSB.Files.Ext
+
+[VBoxUSB.Dev.NT.Services]
+Addservice = VBoxUSB, 0x00000002, VBoxUSB.AddService
+
+[VBoxUSB.AddService]
+DisplayName = %VBoxUSB.SvcDesc%
+ServiceType = 1 ; SERVICE_KERNEL_DRIVER
+StartType = 3 ; SERVICE_DEMAND_START
+ErrorControl = 1 ; SERVICE_ERROR_NORMAL
+ServiceBinary = %10%\System32\Drivers\VBoxUSB.sys
+AddReg = VBoxUSB.AddReg
+LoadOrderGroup = Base
+
+[VBoxUSB.AddReg]
+HKR,,DevLoader,,*ntkern
+HKR,,NTMPDriver,,VBoxUSB.sys
+
+[VBoxUSB.Files.Ext]
+VBoxUSB.sys
+
+;---------------------------------------------------------------;
+
+[Strings]
+ORACLE="Oracle Corporation"
+Disk_Description="VBoxUSB Installation Disk"
+USB\VID_80EE&PID_CAFE.DeviceDesc="VirtualBox USB"
+VBoxUSB.SvcDesc="VirtualBox USB"
diff --git a/src/VBox/HostDrivers/VBoxUSB/win/dev/VBoxUsbCmn.h b/src/VBox/HostDrivers/VBoxUSB/win/dev/VBoxUsbCmn.h
new file mode 100644
index 00000000..57eeeafa
--- /dev/null
+++ b/src/VBox/HostDrivers/VBoxUSB/win/dev/VBoxUsbCmn.h
@@ -0,0 +1,103 @@
+/* $Id: VBoxUsbCmn.h $ */
+/** @file
+ * VBoxUsmCmn.h - USB device. Common defs
+ */
+/*
+ * Copyright (C) 2011-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+#ifndef VBOX_INCLUDED_SRC_VBoxUSB_win_dev_VBoxUsbCmn_h
+#define VBOX_INCLUDED_SRC_VBoxUSB_win_dev_VBoxUsbCmn_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+#include "../cmn/VBoxDrvTool.h"
+#include "../cmn/VBoxUsbTool.h"
+
+#include <iprt/cdefs.h>
+#include <iprt/asm.h>
+
+#include <VBox/usblib-win.h>
+
+#define VBOXUSB_CFG_IDLE_TIME_MS 5000
+
+typedef struct VBOXUSBDEV_EXT *PVBOXUSBDEV_EXT;
+
+RT_C_DECLS_BEGIN
+
+#ifdef _WIN64
+#define DECLSPEC_USBIMPORT DECLSPEC_IMPORT
+#else
+#define DECLSPEC_USBIMPORT
+
+#define USBD_ParseDescriptors _USBD_ParseDescriptors
+#define USBD_ParseConfigurationDescriptorEx _USBD_ParseConfigurationDescriptorEx
+#define USBD_CreateConfigurationRequestEx _USBD_CreateConfigurationRequestEx
+#endif
+
+DECLSPEC_USBIMPORT PUSB_COMMON_DESCRIPTOR
+USBD_ParseDescriptors(
+ IN PVOID DescriptorBuffer,
+ IN ULONG TotalLength,
+ IN PVOID StartPosition,
+ IN LONG DescriptorType
+ );
+
+DECLSPEC_USBIMPORT PUSB_INTERFACE_DESCRIPTOR
+USBD_ParseConfigurationDescriptorEx(
+ IN PUSB_CONFIGURATION_DESCRIPTOR ConfigurationDescriptor,
+ IN PVOID StartPosition,
+ IN LONG InterfaceNumber,
+ IN LONG AlternateSetting,
+ IN LONG InterfaceClass,
+ IN LONG InterfaceSubClass,
+ IN LONG InterfaceProtocol
+ );
+
+DECLSPEC_USBIMPORT PURB
+USBD_CreateConfigurationRequestEx(
+ IN PUSB_CONFIGURATION_DESCRIPTOR ConfigurationDescriptor,
+ IN PUSBD_INTERFACE_LIST_ENTRY InterfaceList
+ );
+
+RT_C_DECLS_END
+
+DECLHIDDEN(PVOID) vboxUsbMemAlloc(SIZE_T cbBytes);
+DECLHIDDEN(PVOID) vboxUsbMemAllocZ(SIZE_T cbBytes);
+DECLHIDDEN(VOID) vboxUsbMemFree(PVOID pvMem);
+
+#include "VBoxUsbRt.h"
+#include "VBoxUsbPnP.h"
+#include "VBoxUsbPwr.h"
+#include "VBoxUsbDev.h"
+
+
+#endif /* !VBOX_INCLUDED_SRC_VBoxUSB_win_dev_VBoxUsbCmn_h */
diff --git a/src/VBox/HostDrivers/VBoxUSB/win/dev/VBoxUsbDev.cpp b/src/VBox/HostDrivers/VBoxUSB/win/dev/VBoxUsbDev.cpp
new file mode 100644
index 00000000..680f5d2f
--- /dev/null
+++ b/src/VBox/HostDrivers/VBoxUSB/win/dev/VBoxUsbDev.cpp
@@ -0,0 +1,357 @@
+/* $Id: VBoxUsbDev.cpp $ */
+/** @file
+ * VBoxUsbDev.cpp - USB device.
+ */
+
+/*
+ * Copyright (C) 2011-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include "VBoxUsbCmn.h"
+#include <iprt/assert.h>
+#include <VBox/log.h>
+
+
+/*********************************************************************************************************************************
+* Defined Constants And Macros *
+*********************************************************************************************************************************/
+#define VBOXUSB_MEMTAG 'bUBV'
+
+
+
+DECLHIDDEN(PVOID) vboxUsbMemAlloc(SIZE_T cbBytes)
+{
+ PVOID pvMem = ExAllocatePoolWithTag(NonPagedPool, cbBytes, VBOXUSB_MEMTAG);
+ Assert(pvMem);
+ return pvMem;
+}
+
+DECLHIDDEN(PVOID) vboxUsbMemAllocZ(SIZE_T cbBytes)
+{
+ PVOID pvMem = vboxUsbMemAlloc(cbBytes);
+ if (pvMem)
+ {
+ RtlZeroMemory(pvMem, cbBytes);
+ }
+ return pvMem;
+}
+
+DECLHIDDEN(VOID) vboxUsbMemFree(PVOID pvMem)
+{
+ ExFreePoolWithTag(pvMem, VBOXUSB_MEMTAG);
+}
+
+VBOXUSB_GLOBALS g_VBoxUsbGlobals = {0};
+
+static NTSTATUS vboxUsbDdiAddDevice(PDRIVER_OBJECT pDriverObject,
+ PDEVICE_OBJECT pPDO)
+{
+ PDEVICE_OBJECT pFDO = NULL;
+ NTSTATUS Status = IoCreateDevice(pDriverObject,
+ sizeof (VBOXUSBDEV_EXT),
+ NULL, /* IN PUNICODE_STRING pDeviceName OPTIONAL */
+ FILE_DEVICE_UNKNOWN, /* IN DEVICE_TYPE DeviceType */
+ FILE_AUTOGENERATED_DEVICE_NAME, /* IN ULONG DeviceCharacteristics */
+ FALSE, /* IN BOOLEAN fExclusive */
+ &pFDO);
+ Assert(Status == STATUS_SUCCESS);
+ if (Status == STATUS_SUCCESS)
+ {
+ PVBOXUSBDEV_EXT pDevExt = (PVBOXUSBDEV_EXT)pFDO->DeviceExtension;
+ /* init Device Object bits */
+ pFDO->Flags |= DO_DIRECT_IO;
+ if (pPDO->Flags & DO_POWER_PAGABLE)
+ pFDO->Flags |= DO_POWER_PAGABLE;
+
+
+ /* now init our state bits */
+
+ pDevExt->cHandles = 0;
+
+ pDevExt->pFDO = pFDO;
+ pDevExt->pPDO = pPDO;
+ pDevExt->pLowerDO = IoAttachDeviceToDeviceStack(pFDO, pPDO);
+ Assert(pDevExt->pLowerDO);
+ if (pDevExt->pLowerDO)
+ {
+ vboxUsbDdiStateInit(pDevExt);
+ Status = vboxUsbRtInit(pDevExt);
+ if (Status == STATUS_SUCCESS)
+ {
+ /* we're done! */
+ pFDO->Flags &= ~DO_DEVICE_INITIALIZING;
+ return STATUS_SUCCESS;
+ }
+
+ IoDetachDevice(pDevExt->pLowerDO);
+ }
+ else
+ Status = STATUS_NO_SUCH_DEVICE;
+
+ IoDeleteDevice(pFDO);
+ }
+
+ return Status;
+}
+
+static VOID vboxUsbDdiUnload(PDRIVER_OBJECT pDriverObject)
+{
+ RT_NOREF1(pDriverObject);
+ LogRel(("VBoxUsb::DriverUnload. Built Date (%s) Time (%s)\n", __DATE__, __TIME__));
+ VBoxDrvToolStrFree(&g_VBoxUsbGlobals.RegPath);
+
+ vboxUsbRtGlobalsTerm();
+
+ PRTLOGGER pLogger = RTLogRelSetDefaultInstance(NULL);
+ if (pLogger)
+ {
+ RTLogDestroy(pLogger);
+ }
+ pLogger = RTLogSetDefaultInstance(NULL);
+ if (pLogger)
+ {
+ RTLogDestroy(pLogger);
+ }
+}
+
+static NTSTATUS vboxUsbDispatchCreate(IN PDEVICE_OBJECT pDeviceObject, IN PIRP pIrp)
+{
+ PVBOXUSBDEV_EXT pDevExt = (PVBOXUSBDEV_EXT)pDeviceObject->DeviceExtension;
+ NTSTATUS Status = STATUS_INVALID_HANDLE;
+ do
+ {
+ if (vboxUsbPnPStateGet(pDevExt) != ENMVBOXUSB_PNPSTATE_STARTED)
+ {
+ Status = STATUS_INVALID_DEVICE_STATE;
+ break;
+ }
+
+ PIO_STACK_LOCATION pSl = IoGetCurrentIrpStackLocation(pIrp);
+ PFILE_OBJECT pFObj = pSl->FileObject;
+ if (!pFObj)
+ {
+ Status = STATUS_INVALID_PARAMETER;
+ break;
+ }
+
+ pFObj->FsContext = NULL;
+
+ if (pFObj->FileName.Length)
+ {
+ Status = STATUS_INVALID_PARAMETER;
+ break;
+ }
+
+ Status = vboxUsbRtCreate(pDevExt, pIrp);
+ if (!NT_SUCCESS(Status))
+ {
+ AssertFailed();
+ break;
+ }
+
+ ASMAtomicIncU32(&pDevExt->cHandles);
+ Status = STATUS_SUCCESS;
+ break;
+ } while (0);
+
+ Status = VBoxDrvToolIoComplete(pIrp, Status, 0);
+ return Status;
+}
+
+static NTSTATUS vboxUsbDispatchClose(IN PDEVICE_OBJECT pDeviceObject, IN PIRP pIrp)
+{
+ PVBOXUSBDEV_EXT pDevExt = (PVBOXUSBDEV_EXT)pDeviceObject->DeviceExtension;
+ NTSTATUS Status = STATUS_SUCCESS;
+#ifdef VBOX_STRICT
+ PIO_STACK_LOCATION pSl = IoGetCurrentIrpStackLocation(pIrp);
+ PFILE_OBJECT pFObj = pSl->FileObject;
+ Assert(pFObj);
+ Assert(!pFObj->FileName.Length);
+#endif
+ Status = vboxUsbRtClose(pDevExt, pIrp);
+ if (NT_SUCCESS(Status))
+ {
+ ASMAtomicDecU32(&pDevExt->cHandles);
+ }
+ else
+ {
+ AssertFailed();
+ }
+ Status = VBoxDrvToolIoComplete(pIrp, Status, 0);
+ return Status;
+}
+
+static NTSTATUS vboxUsbDispatchDeviceControl(IN PDEVICE_OBJECT pDeviceObject, IN PIRP pIrp)
+{
+ PVBOXUSBDEV_EXT pDevExt = (PVBOXUSBDEV_EXT)pDeviceObject->DeviceExtension;
+ if (vboxUsbDdiStateRetainIfStarted(pDevExt))
+ return vboxUsbRtDispatch(pDevExt, pIrp);
+ return VBoxDrvToolIoComplete(pIrp, STATUS_INVALID_DEVICE_STATE, 0);
+}
+
+static NTSTATUS vboxUsbDispatchCleanup(IN PDEVICE_OBJECT pDeviceObject, IN PIRP pIrp)
+{
+ RT_NOREF1(pDeviceObject);
+ return VBoxDrvToolIoComplete(pIrp, STATUS_SUCCESS, 0);
+}
+
+static NTSTATUS vboxUsbDevAccessDeviedDispatchStub(IN PDEVICE_OBJECT pDeviceObject, IN PIRP pIrp)
+{
+ PVBOXUSBDEV_EXT pDevExt = (PVBOXUSBDEV_EXT)pDeviceObject->DeviceExtension;
+ if (!vboxUsbDdiStateRetainIfNotRemoved(pDevExt))
+ {
+ VBoxDrvToolIoComplete(pIrp, STATUS_DELETE_PENDING, 0);
+ return STATUS_DELETE_PENDING;
+ }
+
+ NTSTATUS Status = VBoxDrvToolIoComplete(pIrp, STATUS_ACCESS_DENIED, 0);
+
+ vboxUsbDdiStateRelease(pDevExt);
+
+ return Status;
+}
+
+static NTSTATUS vboxUsbDispatchSystemControl(IN PDEVICE_OBJECT pDeviceObject, IN PIRP pIrp)
+{
+ PVBOXUSBDEV_EXT pDevExt = (PVBOXUSBDEV_EXT)pDeviceObject->DeviceExtension;
+ if (!vboxUsbDdiStateRetainIfNotRemoved(pDevExt))
+ {
+ VBoxDrvToolIoComplete(pIrp, STATUS_DELETE_PENDING, 0);
+ return STATUS_DELETE_PENDING;
+ }
+
+ IoSkipCurrentIrpStackLocation(pIrp);
+
+ NTSTATUS Status = IoCallDriver(pDevExt->pLowerDO, pIrp);
+
+ vboxUsbDdiStateRelease(pDevExt);
+
+ return Status;
+}
+
+static NTSTATUS vboxUsbDispatchRead(IN PDEVICE_OBJECT pDeviceObject, IN PIRP pIrp)
+{
+#ifdef DEBUG_misha
+ AssertFailed();
+#endif
+ return vboxUsbDevAccessDeviedDispatchStub(pDeviceObject, pIrp);
+}
+
+static NTSTATUS vboxUsbDispatchWrite(IN PDEVICE_OBJECT pDeviceObject, IN PIRP pIrp)
+{
+#ifdef DEBUG_misha
+ AssertFailed();
+#endif
+ return vboxUsbDevAccessDeviedDispatchStub(pDeviceObject, pIrp);
+}
+
+RT_C_DECLS_BEGIN
+
+NTSTATUS DriverEntry(IN PDRIVER_OBJECT pDriverObject, IN PUNICODE_STRING pRegistryPath);
+
+RT_C_DECLS_END
+
+NTSTATUS DriverEntry(IN PDRIVER_OBJECT pDriverObject, IN PUNICODE_STRING pRegistryPath)
+{
+ LogRel(("VBoxUsb::DriverEntry. Built Date (%s) Time (%s)\n", __DATE__, __TIME__));
+
+ NTSTATUS Status = vboxUsbRtGlobalsInit();
+ Assert(Status == STATUS_SUCCESS);
+ if (Status == STATUS_SUCCESS)
+ {
+ Status = VBoxDrvToolStrCopy(&g_VBoxUsbGlobals.RegPath, pRegistryPath);
+ Assert(Status == STATUS_SUCCESS);
+ if (Status == STATUS_SUCCESS)
+ {
+ g_VBoxUsbGlobals.pDrvObj = pDriverObject;
+
+ pDriverObject->DriverExtension->AddDevice = vboxUsbDdiAddDevice;
+
+ pDriverObject->DriverUnload = vboxUsbDdiUnload;
+
+ pDriverObject->MajorFunction[IRP_MJ_CREATE] = vboxUsbDispatchCreate;
+ pDriverObject->MajorFunction[IRP_MJ_CLOSE] = vboxUsbDispatchClose;
+ pDriverObject->MajorFunction[IRP_MJ_READ] = vboxUsbDispatchRead;
+ pDriverObject->MajorFunction[IRP_MJ_WRITE] = vboxUsbDispatchWrite;
+ pDriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = vboxUsbDispatchDeviceControl;
+ pDriverObject->MajorFunction[IRP_MJ_CLEANUP] = vboxUsbDispatchCleanup;
+ pDriverObject->MajorFunction[IRP_MJ_POWER] = vboxUsbDispatchPower;
+ pDriverObject->MajorFunction[IRP_MJ_SYSTEM_CONTROL] = vboxUsbDispatchSystemControl;
+ pDriverObject->MajorFunction[IRP_MJ_PNP] = vboxUsbDispatchPnP;
+
+ return STATUS_SUCCESS;
+ }
+ vboxUsbRtGlobalsTerm();
+ }
+
+ LogRel(("VBoxUsb::DriverEntry. failed with Status (0x%x)\n", Status));
+
+ return Status;
+}
+
+#ifdef VBOX_STRICT
+DECLHIDDEN(VOID) vboxUsbPnPStateGbgChange(ENMVBOXUSB_PNPSTATE enmOldState, ENMVBOXUSB_PNPSTATE enmNewState)
+{
+ /* *ensure the state change is valid */
+ switch (enmNewState)
+ {
+ case ENMVBOXUSB_PNPSTATE_STARTED:
+ Assert( enmOldState == ENMVBOXUSB_PNPSTATE_START_PENDING
+ || enmOldState == ENMVBOXUSB_PNPSTATE_REMOVE_PENDING
+ || enmOldState == ENMVBOXUSB_PNPSTATE_STOPPED
+ || enmOldState == ENMVBOXUSB_PNPSTATE_STOP_PENDING);
+ break;
+ case ENMVBOXUSB_PNPSTATE_STOP_PENDING:
+ Assert(enmOldState == ENMVBOXUSB_PNPSTATE_STARTED);
+ break;
+ case ENMVBOXUSB_PNPSTATE_STOPPED:
+ Assert(enmOldState == ENMVBOXUSB_PNPSTATE_STOP_PENDING);
+ break;
+ case ENMVBOXUSB_PNPSTATE_SURPRISE_REMOVED:
+ Assert(enmOldState == ENMVBOXUSB_PNPSTATE_STARTED);
+ break;
+ case ENMVBOXUSB_PNPSTATE_REMOVE_PENDING:
+ Assert(enmOldState == ENMVBOXUSB_PNPSTATE_STARTED);
+ break;
+ case ENMVBOXUSB_PNPSTATE_REMOVED:
+ Assert( enmOldState == ENMVBOXUSB_PNPSTATE_REMOVE_PENDING
+ || enmOldState == ENMVBOXUSB_PNPSTATE_SURPRISE_REMOVED);
+ break;
+ default:
+ AssertFailed();
+ break;
+ }
+
+}
+#endif
diff --git a/src/VBox/HostDrivers/VBoxUSB/win/dev/VBoxUsbDev.h b/src/VBox/HostDrivers/VBoxUSB/win/dev/VBoxUsbDev.h
new file mode 100644
index 00000000..7dd19d86
--- /dev/null
+++ b/src/VBox/HostDrivers/VBoxUSB/win/dev/VBoxUsbDev.h
@@ -0,0 +1,218 @@
+/* $Id: VBoxUsbDev.h $ */
+/** @file
+ * VBoxUsbDev.h - USB device.
+ */
+
+/*
+ * Copyright (C) 2011-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+#ifndef VBOX_INCLUDED_SRC_VBoxUSB_win_dev_VBoxUsbDev_h
+#define VBOX_INCLUDED_SRC_VBoxUSB_win_dev_VBoxUsbDev_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+#include "VBoxUsbCmn.h"
+#include <VBox/cdefs.h>
+#include <iprt/assert.h>
+
+typedef struct VBOXUSB_GLOBALS
+{
+ PDRIVER_OBJECT pDrvObj;
+ UNICODE_STRING RegPath;
+ VBOXUSBRT_IDC RtIdc;
+} VBOXUSB_GLOBALS, *PVBOXUSB_GLOBALS;
+
+extern VBOXUSB_GLOBALS g_VBoxUsbGlobals;
+
+/* pnp state decls */
+typedef enum
+{
+ ENMVBOXUSB_PNPSTATE_UNKNOWN = 0,
+ ENMVBOXUSB_PNPSTATE_START_PENDING,
+ ENMVBOXUSB_PNPSTATE_STARTED,
+ ENMVBOXUSB_PNPSTATE_STOP_PENDING,
+ ENMVBOXUSB_PNPSTATE_STOPPED,
+ ENMVBOXUSB_PNPSTATE_SURPRISE_REMOVED,
+ ENMVBOXUSB_PNPSTATE_REMOVE_PENDING,
+ ENMVBOXUSB_PNPSTATE_REMOVED,
+ ENMVBOXUSB_PNPSTATE_FORSEDWORD = 0x8fffffff
+} ENMVBOXUSB_PNPSTATE;
+AssertCompile(sizeof (ENMVBOXUSB_PNPSTATE) == sizeof (uint32_t));
+
+#ifdef VBOX_STRICT
+DECLHIDDEN(VOID) vboxUsbPnPStateGbgChange(ENMVBOXUSB_PNPSTATE enmOld, ENMVBOXUSB_PNPSTATE enmNew);
+# define VBOXUSB_PNP_GBG_STATE_CHANGE(_old, _new) vboxUsbPnPStateGbgChange((_old), (_new))
+#else
+# define VBOXUSB_PNP_GBG_STATE_CHANGE(_old, _new) do { } while (0)
+#endif
+
+
+typedef struct VBOXUSB_PNPSTATE
+{
+ /* Current state */
+ volatile ENMVBOXUSB_PNPSTATE Curr;
+ /* Previous state, used to restore state info on cancell stop device */
+ ENMVBOXUSB_PNPSTATE Prev;
+} VBOXUSB_PNPSTATE, *PVBOXUSB_PNPSTATE;
+
+typedef struct VBOXUSBDEV_DDISTATE
+{
+ /* Lock */
+ KSPIN_LOCK Lock;
+ VBOXDRVTOOL_REF Ref;
+ VBOXUSB_PNPSTATE PnPState;
+ VBOXUSB_PWRSTATE PwrState;
+ /* current dev caps */
+ DEVICE_CAPABILITIES DevCaps;
+} VBOXUSBDEV_DDISTATE, *PVBOXUSBDEV_DDISTATE;
+
+typedef struct VBOXUSBDEV_EXT
+{
+ PDEVICE_OBJECT pFDO;
+ PDEVICE_OBJECT pPDO;
+ PDEVICE_OBJECT pLowerDO;
+
+ VBOXUSBDEV_DDISTATE DdiState;
+
+ uint32_t cHandles;
+
+ VBOXUSB_RT Rt;
+
+} VBOXUSBDEV_EXT, *PVBOXUSBDEV_EXT;
+
+/* pnp state api */
+DECLINLINE(ENMVBOXUSB_PNPSTATE) vboxUsbPnPStateGet(PVBOXUSBDEV_EXT pDevExt)
+{
+ return (ENMVBOXUSB_PNPSTATE)ASMAtomicUoReadU32((volatile uint32_t*)&pDevExt->DdiState.PnPState.Curr);
+}
+
+DECLINLINE(ENMVBOXUSB_PNPSTATE) vboxUsbPnPStateSet(PVBOXUSBDEV_EXT pDevExt, ENMVBOXUSB_PNPSTATE enmState)
+{
+ KIRQL Irql;
+ ENMVBOXUSB_PNPSTATE enmOldState;
+ KeAcquireSpinLock(&pDevExt->DdiState.Lock, &Irql);
+ pDevExt->DdiState.PnPState.Prev = (ENMVBOXUSB_PNPSTATE)ASMAtomicUoReadU32((volatile uint32_t*)&pDevExt->DdiState.PnPState.Curr);
+ ASMAtomicWriteU32((volatile uint32_t*)&pDevExt->DdiState.PnPState.Curr, (uint32_t)enmState);
+ pDevExt->DdiState.PnPState.Curr = enmState;
+ enmOldState = pDevExt->DdiState.PnPState.Prev;
+ KeReleaseSpinLock(&pDevExt->DdiState.Lock, Irql);
+ VBOXUSB_PNP_GBG_STATE_CHANGE(enmOldState, enmState);
+ return enmState;
+}
+
+DECLINLINE(ENMVBOXUSB_PNPSTATE) vboxUsbPnPStateRestore(PVBOXUSBDEV_EXT pDevExt)
+{
+ ENMVBOXUSB_PNPSTATE enmNewState, enmOldState;
+ KIRQL Irql;
+ KeAcquireSpinLock(&pDevExt->DdiState.Lock, &Irql);
+ enmOldState = pDevExt->DdiState.PnPState.Curr;
+ enmNewState = pDevExt->DdiState.PnPState.Prev;
+ ASMAtomicWriteU32((volatile uint32_t*)&pDevExt->DdiState.PnPState.Curr, (uint32_t)pDevExt->DdiState.PnPState.Prev);
+ KeReleaseSpinLock(&pDevExt->DdiState.Lock, Irql);
+ VBOXUSB_PNP_GBG_STATE_CHANGE(enmOldState, enmNewState);
+ Assert(enmNewState == ENMVBOXUSB_PNPSTATE_STARTED);
+ Assert(enmOldState == ENMVBOXUSB_PNPSTATE_STOP_PENDING
+ || enmOldState == ENMVBOXUSB_PNPSTATE_REMOVE_PENDING);
+ return enmNewState;
+}
+
+DECLINLINE(VOID) vboxUsbPnPStateInit(PVBOXUSBDEV_EXT pDevExt)
+{
+ pDevExt->DdiState.PnPState.Curr = pDevExt->DdiState.PnPState.Prev = ENMVBOXUSB_PNPSTATE_START_PENDING;
+}
+
+DECLINLINE(VOID) vboxUsbDdiStateInit(PVBOXUSBDEV_EXT pDevExt)
+{
+ KeInitializeSpinLock(&pDevExt->DdiState.Lock);
+ VBoxDrvToolRefInit(&pDevExt->DdiState.Ref);
+ vboxUsbPwrStateInit(pDevExt);
+ vboxUsbPnPStateInit(pDevExt);
+}
+
+DECLINLINE(bool) vboxUsbDdiStateRetainIfStarted(PVBOXUSBDEV_EXT pDevExt)
+{
+ KIRQL oldIrql;
+ bool bRetained = true;
+ KeAcquireSpinLock(&pDevExt->DdiState.Lock, &oldIrql);
+ if (vboxUsbPnPStateGet(pDevExt) == ENMVBOXUSB_PNPSTATE_STARTED)
+ {
+ VBoxDrvToolRefRetain(&pDevExt->DdiState.Ref);
+ }
+ else
+ {
+ bRetained = false;
+ }
+ KeReleaseSpinLock(&pDevExt->DdiState.Lock, oldIrql);
+ return bRetained;
+}
+
+/* if device is removed - does nothing and returns zero,
+ * otherwise increments a ref counter and returns the current pnp state
+ * NOTE: never returns ENMVBOXUSB_PNPSTATE_REMOVED
+ * */
+DECLINLINE(ENMVBOXUSB_PNPSTATE) vboxUsbDdiStateRetainIfNotRemoved(PVBOXUSBDEV_EXT pDevExt)
+{
+ KIRQL oldIrql;
+ ENMVBOXUSB_PNPSTATE enmState;
+ KeAcquireSpinLock(&pDevExt->DdiState.Lock, &oldIrql);
+ enmState = vboxUsbPnPStateGet(pDevExt);
+ if (enmState != ENMVBOXUSB_PNPSTATE_REMOVED)
+ {
+ VBoxDrvToolRefRetain(&pDevExt->DdiState.Ref);
+ }
+ KeReleaseSpinLock(&pDevExt->DdiState.Lock, oldIrql);
+ return enmState != ENMVBOXUSB_PNPSTATE_REMOVED ? enmState : (ENMVBOXUSB_PNPSTATE)0;
+}
+
+DECLINLINE(uint32_t) vboxUsbDdiStateRetain(PVBOXUSBDEV_EXT pDevExt)
+{
+ return VBoxDrvToolRefRetain(&pDevExt->DdiState.Ref);
+}
+
+DECLINLINE(uint32_t) vboxUsbDdiStateRelease(PVBOXUSBDEV_EXT pDevExt)
+{
+ return VBoxDrvToolRefRelease(&pDevExt->DdiState.Ref);
+}
+
+DECLINLINE(VOID) vboxUsbDdiStateReleaseAndWaitCompleted(PVBOXUSBDEV_EXT pDevExt)
+{
+ VBoxDrvToolRefRelease(&pDevExt->DdiState.Ref);
+ VBoxDrvToolRefWaitEqual(&pDevExt->DdiState.Ref, 1);
+}
+
+DECLINLINE(VOID) vboxUsbDdiStateReleaseAndWaitRemoved(PVBOXUSBDEV_EXT pDevExt)
+{
+ VBoxDrvToolRefRelease(&pDevExt->DdiState.Ref);
+ VBoxDrvToolRefWaitEqual(&pDevExt->DdiState.Ref, 0);
+}
+
+#endif /* !VBOX_INCLUDED_SRC_VBoxUSB_win_dev_VBoxUsbDev_h */
diff --git a/src/VBox/HostDrivers/VBoxUSB/win/dev/VBoxUsbDev.rc b/src/VBox/HostDrivers/VBoxUSB/win/dev/VBoxUsbDev.rc
new file mode 100644
index 00000000..83ccd753
--- /dev/null
+++ b/src/VBox/HostDrivers/VBoxUSB/win/dev/VBoxUsbDev.rc
@@ -0,0 +1,70 @@
+/* $Id: VBoxUsbDev.rc $ */
+/** @file
+ * VBoxUSB - Resource file containing version info and icon.
+ */
+
+/*
+ * Copyright (C) 2011-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+#include <windows.h>
+#include <VBox/version.h>
+
+LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
+
+VS_VERSION_INFO VERSIONINFO
+ FILEVERSION VBOX_RC_FILE_VERSION
+ PRODUCTVERSION VBOX_RC_FILE_VERSION
+ FILEFLAGSMASK VS_FFI_FILEFLAGSMASK
+ FILEFLAGS VBOX_RC_FILE_FLAGS
+ FILEOS VBOX_RC_FILE_OS
+ FILETYPE VBOX_RC_TYPE_DRV
+ FILESUBTYPE VFT2_DRV_SYSTEM
+BEGIN
+ BLOCK "StringFileInfo"
+ BEGIN
+ BLOCK "040904b0" // Lang=US English, CharSet=Unicode
+ BEGIN
+ VALUE "FileDescription", "VirtualBox USB Driver\0"
+ VALUE "InternalName", "VBoxUSB\0"
+ VALUE "OriginalFilename", "VBoxUSB.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_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/HostDrivers/VBoxUSB/win/dev/VBoxUsbPnP.cpp b/src/VBox/HostDrivers/VBoxUSB/win/dev/VBoxUsbPnP.cpp
new file mode 100644
index 00000000..18a068c8
--- /dev/null
+++ b/src/VBox/HostDrivers/VBoxUSB/win/dev/VBoxUsbPnP.cpp
@@ -0,0 +1,274 @@
+/* $Id: VBoxUsbPnP.cpp $ */
+/** @file
+ * USB PnP Handling
+ */
+
+/*
+ * Copyright (C) 2011-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+#include "VBoxUsbCmn.h"
+
+static NTSTATUS vboxUsbPnPMnStartDevice(PVBOXUSBDEV_EXT pDevExt, PIRP pIrp)
+{
+ IoCopyCurrentIrpStackLocationToNext(pIrp);
+ NTSTATUS Status = VBoxDrvToolIoPostSync(pDevExt->pLowerDO, pIrp);
+ Assert(NT_SUCCESS(Status) || Status == STATUS_NOT_SUPPORTED);
+ if (NT_SUCCESS(Status))
+ {
+ Status = vboxUsbRtStart(pDevExt);
+ Assert(Status == STATUS_SUCCESS);
+ if (NT_SUCCESS(Status))
+ {
+ vboxUsbPnPStateSet(pDevExt, ENMVBOXUSB_PNPSTATE_STARTED);
+ }
+ }
+
+ VBoxDrvToolIoComplete(pIrp, Status, 0);
+ vboxUsbDdiStateRelease(pDevExt);
+ return Status;
+}
+
+static NTSTATUS vboxUsbPnPMnQueryStopDevice(PVBOXUSBDEV_EXT pDevExt, PIRP pIrp)
+{
+ vboxUsbPnPStateSet(pDevExt, ENMVBOXUSB_PNPSTATE_STOP_PENDING);
+
+ vboxUsbDdiStateReleaseAndWaitCompleted(pDevExt);
+
+ pIrp->IoStatus.Status = STATUS_SUCCESS;
+ pIrp->IoStatus.Information = 0;
+ IoSkipCurrentIrpStackLocation(pIrp);
+ return IoCallDriver(pDevExt->pLowerDO, pIrp);
+}
+
+static NTSTATUS vboxUsbPnPMnStopDevice(PVBOXUSBDEV_EXT pDevExt, PIRP pIrp)
+{
+ vboxUsbPnPStateSet(pDevExt, ENMVBOXUSB_PNPSTATE_STOPPED);
+
+ vboxUsbRtClear(pDevExt);
+
+ NTSTATUS Status = VBoxUsbToolDevUnconfigure(pDevExt->pLowerDO);
+ Assert(NT_SUCCESS(Status));
+
+ pIrp->IoStatus.Status = Status;
+ pIrp->IoStatus.Information = 0;
+ IoSkipCurrentIrpStackLocation(pIrp);
+ Status = IoCallDriver(pDevExt->pLowerDO, pIrp);
+
+ vboxUsbDdiStateRelease(pDevExt);
+ return Status;
+}
+
+static NTSTATUS vboxUsbPnPMnCancelStopDevice(PVBOXUSBDEV_EXT pDevExt, PIRP pIrp)
+{
+ ENMVBOXUSB_PNPSTATE enmState = vboxUsbPnPStateGet(pDevExt);
+ NTSTATUS Status = STATUS_SUCCESS;
+
+ IoCopyCurrentIrpStackLocationToNext(pIrp);
+ Status = VBoxDrvToolIoPostSync(pDevExt->pLowerDO, pIrp);
+ if (NT_SUCCESS(Status) && enmState == ENMVBOXUSB_PNPSTATE_STOP_PENDING)
+ {
+ vboxUsbPnPStateRestore(pDevExt);
+ }
+
+ Status = STATUS_SUCCESS;
+ VBoxDrvToolIoComplete(pIrp, Status, 0);
+ vboxUsbDdiStateRelease(pDevExt);
+
+ return Status;
+}
+
+static NTSTATUS vboxUsbPnPMnQueryRemoveDevice(PVBOXUSBDEV_EXT pDevExt, PIRP pIrp)
+{
+ vboxUsbPnPStateSet(pDevExt, ENMVBOXUSB_PNPSTATE_REMOVE_PENDING);
+
+ vboxUsbDdiStateReleaseAndWaitCompleted(pDevExt);
+
+ pIrp->IoStatus.Status = STATUS_SUCCESS;
+ pIrp->IoStatus.Information = 0;
+ IoSkipCurrentIrpStackLocation(pIrp);
+ return IoCallDriver(pDevExt->pLowerDO, pIrp);
+}
+
+static NTSTATUS vboxUsbPnPRmDev(PVBOXUSBDEV_EXT pDevExt)
+{
+ NTSTATUS Status = vboxUsbRtRm(pDevExt);
+ Assert(Status == STATUS_SUCCESS);
+
+ return Status;
+}
+
+static NTSTATUS vboxUsbPnPMnRemoveDevice(PVBOXUSBDEV_EXT pDevExt, PIRP pIrp)
+{
+ ENMVBOXUSB_PNPSTATE enmState = vboxUsbPnPStateGet(pDevExt);
+ NTSTATUS Status = STATUS_SUCCESS;
+ if (enmState != ENMVBOXUSB_PNPSTATE_SURPRISE_REMOVED)
+ {
+ Status = vboxUsbPnPRmDev(pDevExt);
+ Assert(Status == STATUS_SUCCESS);
+ }
+
+ vboxUsbPnPStateSet(pDevExt, ENMVBOXUSB_PNPSTATE_REMOVED);
+
+ vboxUsbDdiStateRelease(pDevExt);
+
+ vboxUsbDdiStateReleaseAndWaitRemoved(pDevExt);
+
+ vboxUsbRtClear(pDevExt);
+
+ pIrp->IoStatus.Status = STATUS_SUCCESS;
+ pIrp->IoStatus.Information = 0;
+ IoSkipCurrentIrpStackLocation(pIrp);
+ Status = IoCallDriver(pDevExt->pLowerDO, pIrp);
+
+ IoDetachDevice(pDevExt->pLowerDO);
+ IoDeleteDevice(pDevExt->pFDO);
+
+ return Status;
+}
+
+static NTSTATUS vboxUsbPnPMnCancelRemoveDevice(PVBOXUSBDEV_EXT pDevExt, PIRP pIrp)
+{
+ ENMVBOXUSB_PNPSTATE enmState = vboxUsbPnPStateGet(pDevExt);
+ NTSTATUS Status = STATUS_SUCCESS;
+ IoCopyCurrentIrpStackLocationToNext(pIrp);
+
+ Status = VBoxDrvToolIoPostSync(pDevExt->pLowerDO, pIrp);
+
+ if (NT_SUCCESS(Status) &&
+ enmState == ENMVBOXUSB_PNPSTATE_REMOVE_PENDING)
+ {
+ vboxUsbPnPStateRestore(pDevExt);
+ }
+
+ Status = STATUS_SUCCESS;
+ VBoxDrvToolIoComplete(pIrp, Status, 0);
+ vboxUsbDdiStateRelease(pDevExt);
+
+ return Status;
+}
+
+static NTSTATUS vboxUsbPnPMnSurpriseRemoval(PVBOXUSBDEV_EXT pDevExt, PIRP pIrp)
+{
+ vboxUsbPnPStateSet(pDevExt, ENMVBOXUSB_PNPSTATE_SURPRISE_REMOVED);
+
+ NTSTATUS Status = vboxUsbPnPRmDev(pDevExt);
+ Assert(Status == STATUS_SUCCESS);
+
+ pIrp->IoStatus.Status = STATUS_SUCCESS;
+ pIrp->IoStatus.Information = 0;
+ IoSkipCurrentIrpStackLocation(pIrp);
+ Status = IoCallDriver(pDevExt->pLowerDO, pIrp);
+
+ vboxUsbDdiStateRelease(pDevExt);
+
+ return Status;
+}
+
+static NTSTATUS vboxUsbPnPMnQueryCapabilities(PVBOXUSBDEV_EXT pDevExt, PIRP pIrp)
+{
+ PIO_STACK_LOCATION pSl = IoGetCurrentIrpStackLocation(pIrp);
+ PDEVICE_CAPABILITIES pDevCaps = pSl->Parameters.DeviceCapabilities.Capabilities;
+
+ if (pDevCaps->Version < 1 || pDevCaps->Size < sizeof (*pDevCaps))
+ {
+ AssertFailed();
+ /** @todo return more appropriate status ?? */
+ return STATUS_UNSUCCESSFUL;
+ }
+
+ pDevCaps->SurpriseRemovalOK = TRUE;
+ pIrp->IoStatus.Status = STATUS_SUCCESS;
+
+ IoCopyCurrentIrpStackLocationToNext(pIrp);
+ NTSTATUS Status = VBoxDrvToolIoPostSync(pDevExt->pLowerDO, pIrp);
+ Assert(NT_SUCCESS(Status));
+ if (NT_SUCCESS(Status))
+ {
+ pDevCaps->SurpriseRemovalOK = 1;
+ pDevExt->DdiState.DevCaps = *pDevCaps;
+ }
+
+ VBoxDrvToolIoComplete(pIrp, Status, 0);
+ vboxUsbDdiStateRelease(pDevExt);
+
+ return Status;
+}
+
+static NTSTATUS vboxUsbPnPMnDefault(PVBOXUSBDEV_EXT pDevExt, PIRP pIrp)
+{
+ NTSTATUS Status;
+ IoSkipCurrentIrpStackLocation(pIrp);
+ Status = IoCallDriver(pDevExt->pLowerDO, pIrp);
+ vboxUsbDdiStateRelease(pDevExt);
+ return Status;
+}
+
+DECLHIDDEN(NTSTATUS) vboxUsbDispatchPnP(IN PDEVICE_OBJECT pDeviceObject, IN PIRP pIrp)
+{
+ PVBOXUSBDEV_EXT pDevExt = (PVBOXUSBDEV_EXT)pDeviceObject->DeviceExtension;
+ if (!vboxUsbDdiStateRetainIfNotRemoved(pDevExt))
+ return VBoxDrvToolIoComplete(pIrp, STATUS_DELETE_PENDING, 0);
+
+ PIO_STACK_LOCATION pSl = IoGetCurrentIrpStackLocation(pIrp);
+ switch (pSl->MinorFunction)
+ {
+ case IRP_MN_START_DEVICE:
+ return vboxUsbPnPMnStartDevice(pDevExt, pIrp);
+
+ case IRP_MN_QUERY_STOP_DEVICE:
+ return vboxUsbPnPMnQueryStopDevice(pDevExt, pIrp);
+
+ case IRP_MN_STOP_DEVICE:
+ return vboxUsbPnPMnStopDevice(pDevExt, pIrp);
+
+ case IRP_MN_CANCEL_STOP_DEVICE:
+ return vboxUsbPnPMnCancelStopDevice(pDevExt, pIrp);
+
+ case IRP_MN_QUERY_REMOVE_DEVICE:
+ return vboxUsbPnPMnQueryRemoveDevice(pDevExt, pIrp);
+
+ case IRP_MN_REMOVE_DEVICE:
+ return vboxUsbPnPMnRemoveDevice(pDevExt, pIrp);
+
+ case IRP_MN_CANCEL_REMOVE_DEVICE:
+ return vboxUsbPnPMnCancelRemoveDevice(pDevExt, pIrp);
+
+ case IRP_MN_SURPRISE_REMOVAL:
+ return vboxUsbPnPMnSurpriseRemoval(pDevExt, pIrp);
+
+ case IRP_MN_QUERY_CAPABILITIES:
+ return vboxUsbPnPMnQueryCapabilities(pDevExt, pIrp);
+
+ default:
+ return vboxUsbPnPMnDefault(pDevExt, pIrp);
+ }
+}
+
diff --git a/src/VBox/HostDrivers/VBoxUSB/win/dev/VBoxUsbPnP.h b/src/VBox/HostDrivers/VBoxUSB/win/dev/VBoxUsbPnP.h
new file mode 100644
index 00000000..c1799080
--- /dev/null
+++ b/src/VBox/HostDrivers/VBoxUSB/win/dev/VBoxUsbPnP.h
@@ -0,0 +1,46 @@
+/* $Id: VBoxUsbPnP.h $ */
+/** @file
+ * USB PnP Handling
+ */
+
+/*
+ * Copyright (C) 2011-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+#ifndef VBOX_INCLUDED_SRC_VBoxUSB_win_dev_VBoxUsbPnP_h
+#define VBOX_INCLUDED_SRC_VBoxUSB_win_dev_VBoxUsbPnP_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+#include "VBoxUsbCmn.h"
+
+DECLHIDDEN(NTSTATUS) vboxUsbDispatchPnP(IN PDEVICE_OBJECT pDeviceObject, IN PIRP pIrp);
+
+#endif /* !VBOX_INCLUDED_SRC_VBoxUSB_win_dev_VBoxUsbPnP_h */
diff --git a/src/VBox/HostDrivers/VBoxUSB/win/dev/VBoxUsbPwr.cpp b/src/VBox/HostDrivers/VBoxUSB/win/dev/VBoxUsbPwr.cpp
new file mode 100644
index 00000000..35b57159
--- /dev/null
+++ b/src/VBox/HostDrivers/VBoxUSB/win/dev/VBoxUsbPwr.cpp
@@ -0,0 +1,427 @@
+/* $Id: VBoxUsbPwr.cpp $ */
+/** @file
+ * USB Power state Handling
+ */
+
+/*
+ * Copyright (C) 2011-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+#include "VBoxUsbCmn.h"
+
+#include <iprt/assert.h>
+
+DECLHIDDEN(VOID) vboxUsbPwrStateInit(PVBOXUSBDEV_EXT pDevExt)
+{
+ POWER_STATE PowerState;
+ PowerState.SystemState = PowerSystemWorking;
+ PowerState.DeviceState = PowerDeviceD0;
+ PoSetPowerState(pDevExt->pFDO, DevicePowerState, PowerState);
+ pDevExt->DdiState.PwrState.PowerState = PowerState;
+ pDevExt->DdiState.PwrState.PowerDownLevel = PowerDeviceUnspecified;
+}
+
+static NTSTATUS vboxUsbPwrMnDefault(IN PVBOXUSBDEV_EXT pDevExt, IN PIRP pIrp)
+{
+ NTSTATUS Status;
+ PoStartNextPowerIrp(pIrp);
+ IoSkipCurrentIrpStackLocation(pIrp);
+ Status = PoCallDriver(pDevExt->pLowerDO, pIrp);
+ Assert(NT_SUCCESS(Status) || Status == STATUS_NOT_SUPPORTED);
+ vboxUsbDdiStateRelease(pDevExt);
+ return Status;
+}
+
+static NTSTATUS vboxUsbPwrMnPowerSequence(IN PVBOXUSBDEV_EXT pDevExt, IN PIRP pIrp)
+{
+ AssertFailed();
+ return vboxUsbPwrMnDefault(pDevExt, pIrp);
+}
+
+typedef struct VBOXUSB_PWRDEV_CTX
+{
+ PVBOXUSBDEV_EXT pDevExt;
+ PIRP pIrp;
+} VBOXUSB_PWRDEV_CTX, *PVBOXUSB_PWRDEV_CTX;
+
+static VOID vboxUsbPwrIoDeviceCompletion(IN PDEVICE_OBJECT pDeviceObject,
+ IN UCHAR MinorFunction,
+ IN POWER_STATE PowerState,
+ IN PVOID pvContext,
+ IN PIO_STATUS_BLOCK pIoStatus)
+{
+ RT_NOREF3(pDeviceObject, MinorFunction, PowerState);
+ PVBOXUSB_PWRDEV_CTX pDevCtx = (PVBOXUSB_PWRDEV_CTX)pvContext;
+ PVBOXUSBDEV_EXT pDevExt = pDevCtx->pDevExt;
+ PIRP pIrp = pDevCtx->pIrp;
+ pIrp->IoStatus.Status = pIoStatus->Status;
+ pIrp->IoStatus.Information = 0;
+
+ PoStartNextPowerIrp(pIrp);
+ IoCompleteRequest(pIrp, IO_NO_INCREMENT);
+ vboxUsbDdiStateRelease(pDevExt);
+
+ vboxUsbMemFree(pDevCtx);
+}
+
+static NTSTATUS vboxUsbPwrIoRequestDev(IN PVBOXUSBDEV_EXT pDevExt, IN PIRP pIrp)
+{
+ PIO_STACK_LOCATION pSl = IoGetCurrentIrpStackLocation(pIrp);
+ POWER_STATE PwrState;
+ PwrState.SystemState = pSl->Parameters.Power.State.SystemState;
+ PwrState.DeviceState = pDevExt->DdiState.DevCaps.DeviceState[PwrState.SystemState];
+
+ NTSTATUS Status = STATUS_INSUFFICIENT_RESOURCES;
+ PVBOXUSB_PWRDEV_CTX pDevCtx = (PVBOXUSB_PWRDEV_CTX)vboxUsbMemAlloc(sizeof (*pDevCtx));
+ Assert(pDevCtx);
+ if (pDevCtx)
+ {
+ pDevCtx->pDevExt = pDevExt;
+ pDevCtx->pIrp = pIrp;
+
+ Status = PoRequestPowerIrp(pDevExt->pPDO, pSl->MinorFunction, PwrState,
+ vboxUsbPwrIoDeviceCompletion, pDevCtx, NULL);
+ Assert(NT_SUCCESS(Status));
+ if (NT_SUCCESS(Status))
+ return STATUS_MORE_PROCESSING_REQUIRED;
+
+ vboxUsbMemFree(pDevCtx);
+ }
+
+ PoStartNextPowerIrp(pIrp);
+ pIrp->IoStatus.Status = Status;
+ pIrp->IoStatus.Information = 0;
+ vboxUsbDdiStateRelease(pDevExt);
+
+ /* the "real" Status is stored in pIrp->IoStatus.Status,
+ * return success here to complete the Io */
+ return STATUS_SUCCESS;
+}
+
+static NTSTATUS vboxUsbPwrIoPostSysCompletion(IN PDEVICE_OBJECT pDevObj, IN PIRP pIrp, IN PVOID pvContext)
+{
+ RT_NOREF1(pDevObj);
+ PVBOXUSBDEV_EXT pDevExt = (PVBOXUSBDEV_EXT)pvContext;
+ NTSTATUS Status = pIrp->IoStatus.Status;
+ Assert(Status == STATUS_SUCCESS);
+ if (NT_SUCCESS(Status))
+ {
+ PIO_STACK_LOCATION pSl = IoGetCurrentIrpStackLocation(pIrp);
+ switch (pSl->MinorFunction)
+ {
+ case IRP_MN_SET_POWER:
+ pDevExt->DdiState.PwrState.PowerState.SystemState = pSl->Parameters.Power.State.SystemState;
+ break;
+
+ default:
+ break;
+ }
+
+ return vboxUsbPwrIoRequestDev(pDevExt, pIrp);
+ }
+
+ PoStartNextPowerIrp(pIrp);
+ vboxUsbDdiStateRelease(pDevExt);
+ return STATUS_SUCCESS;
+}
+
+static NTSTATUS vboxUsbPwrIoPostSys(IN PVBOXUSBDEV_EXT pDevExt, IN PIRP pIrp)
+{
+ IoMarkIrpPending(pIrp);
+ IoCopyCurrentIrpStackLocationToNext(pIrp);
+ IoSetCompletionRoutine(pIrp, vboxUsbPwrIoPostSysCompletion, pDevExt, TRUE, TRUE, TRUE);
+ NTSTATUS Status = PoCallDriver(pDevExt->pLowerDO, pIrp);
+ Assert(NT_SUCCESS(Status)); NOREF(Status);
+ return STATUS_PENDING;
+}
+
+static NTSTATUS vboxUsbPwrQueryPowerSys(IN PVBOXUSBDEV_EXT pDevExt, IN PIRP pIrp)
+{
+ /*PIO_STACK_LOCATION pSl = IoGetCurrentIrpStackLocation(pIrp);
+ SYSTEM_POWER_STATE enmSysPState = pSl->Parameters.Power.State.SystemState;*/
+
+ return vboxUsbPwrIoPostSys(pDevExt, pIrp);
+}
+
+static NTSTATUS vboxUsbPwrIoPostDevCompletion(IN PDEVICE_OBJECT pDevObj, IN PIRP pIrp, IN PVOID pvContext)
+{
+ RT_NOREF1(pDevObj);
+ PVBOXUSBDEV_EXT pDevExt = (PVBOXUSBDEV_EXT)pvContext;
+
+ if (pIrp->PendingReturned)
+ IoMarkIrpPending(pIrp);
+
+ NTSTATUS Status = pIrp->IoStatus.Status;
+ Assert(Status == STATUS_SUCCESS);
+ if (NT_SUCCESS(Status))
+ {
+ PIO_STACK_LOCATION pSl = IoGetCurrentIrpStackLocation(pIrp);
+ switch (pSl->MinorFunction)
+ {
+ case IRP_MN_SET_POWER:
+ pDevExt->DdiState.PwrState.PowerState.DeviceState = pSl->Parameters.Power.State.DeviceState;
+ PoSetPowerState(pDevExt->pFDO, DevicePowerState, pSl->Parameters.Power.State);
+ break;
+
+ default:
+ break;
+ }
+ }
+
+ PoStartNextPowerIrp(pIrp);
+ vboxUsbDdiStateRelease(pDevExt);
+ return STATUS_SUCCESS;
+}
+
+static NTSTATUS vboxUsbPwrIoPostDev(IN PVBOXUSBDEV_EXT pDevExt, IN PIRP pIrp)
+{
+ IoMarkIrpPending(pIrp);
+ IoCopyCurrentIrpStackLocationToNext(pIrp);
+ IoSetCompletionRoutine(pIrp, vboxUsbPwrIoPostDevCompletion, pDevExt, TRUE, TRUE, TRUE);
+ NTSTATUS Status = PoCallDriver(pDevExt->pLowerDO, pIrp);
+ Assert(NT_SUCCESS(Status)); RT_NOREF_PV(Status);
+ return STATUS_PENDING;
+}
+
+typedef struct VBOXUSB_IOASYNC_CTX
+{
+ PIO_WORKITEM pWrkItem;
+ PIRP pIrp;
+} VBOXUSB_IOASYNC_CTX, *PVBOXUSB_IOASYNC_CTX;
+
+static VOID vboxUsbPwrIoWaitCompletionAndPostAsyncWorker(IN PDEVICE_OBJECT pDeviceObject, IN PVOID pvContext)
+{
+ PVBOXUSBDEV_EXT pDevExt = (PVBOXUSBDEV_EXT)pDeviceObject->DeviceExtension;
+ PVBOXUSB_IOASYNC_CTX pCtx = (PVBOXUSB_IOASYNC_CTX)pvContext;
+ PIRP pIrp = pCtx->pIrp;
+
+ vboxUsbPwrIoPostDev(pDevExt, pIrp);
+
+ IoFreeWorkItem(pCtx->pWrkItem);
+ vboxUsbMemFree(pCtx);
+}
+
+static NTSTATUS vboxUsbPwrIoWaitCompletionAndPostAsync(IN PVBOXUSBDEV_EXT pDevExt, IN PIRP pIrp)
+{
+ NTSTATUS Status = STATUS_INSUFFICIENT_RESOURCES;
+ PVBOXUSB_IOASYNC_CTX pCtx = (PVBOXUSB_IOASYNC_CTX)vboxUsbMemAlloc(sizeof (*pCtx));
+ Assert(pCtx);
+ if (pCtx)
+ {
+ PIO_WORKITEM pWrkItem = IoAllocateWorkItem(pDevExt->pFDO);
+ Assert(pWrkItem);
+ if (pWrkItem)
+ {
+ pCtx->pWrkItem = pWrkItem;
+ pCtx->pIrp = pIrp;
+ IoMarkIrpPending(pIrp);
+ IoQueueWorkItem(pWrkItem, vboxUsbPwrIoWaitCompletionAndPostAsyncWorker, DelayedWorkQueue, pCtx);
+ return STATUS_PENDING;
+ }
+ vboxUsbMemFree(pCtx);
+ }
+ return Status;
+}
+
+static NTSTATUS vboxUsbPwrQueryPowerDev(IN PVBOXUSBDEV_EXT pDevExt, IN PIRP pIrp)
+{
+ PIO_STACK_LOCATION pSl = IoGetCurrentIrpStackLocation(pIrp);
+ DEVICE_POWER_STATE enmDevPState = pSl->Parameters.Power.State.DeviceState;
+ NTSTATUS Status = STATUS_SUCCESS;
+
+ if (enmDevPState >= pDevExt->DdiState.PwrState.PowerState.DeviceState)
+ {
+ Status = vboxUsbPwrIoWaitCompletionAndPostAsync(pDevExt, pIrp);
+ Assert(NT_SUCCESS(Status));
+ if (NT_SUCCESS(Status))
+ return Status;
+ }
+
+ pIrp->IoStatus.Status = Status;
+ pIrp->IoStatus.Information = 0;
+
+ PoStartNextPowerIrp(pIrp);
+
+ if (NT_SUCCESS(Status))
+ {
+ IoSkipCurrentIrpStackLocation(pIrp);
+ Status = PoCallDriver(pDevExt->pLowerDO, pIrp);
+ }
+ else
+ {
+ IoCompleteRequest(pIrp, IO_NO_INCREMENT);
+ }
+
+ vboxUsbDdiStateRelease(pDevExt);
+
+ return Status;
+}
+
+static NTSTATUS vboxUsbPwrMnQueryPower(IN PVBOXUSBDEV_EXT pDevExt, IN PIRP pIrp)
+{
+ PIO_STACK_LOCATION pSl = IoGetCurrentIrpStackLocation(pIrp);
+ switch (pSl->Parameters.Power.Type)
+ {
+ case SystemPowerState:
+ return vboxUsbPwrQueryPowerSys(pDevExt, pIrp);
+
+ case DevicePowerState:
+ return vboxUsbPwrQueryPowerDev(pDevExt, pIrp);
+
+ default:
+ AssertFailed();
+ return vboxUsbPwrMnDefault(pDevExt, pIrp);
+
+ }
+}
+
+static NTSTATUS vboxUsbPwrSetPowerSys(IN PVBOXUSBDEV_EXT pDevExt, IN PIRP pIrp)
+{
+ /*PIO_STACK_LOCATION pSl = IoGetCurrentIrpStackLocation(pIrp);
+ SYSTEM_POWER_STATE enmSysPState = pSl->Parameters.Power.State.SystemState;*/
+
+ return vboxUsbPwrIoPostSys(pDevExt, pIrp);
+}
+
+static NTSTATUS vboxUsbPwrSetPowerDev(IN PVBOXUSBDEV_EXT pDevExt, IN PIRP pIrp)
+{
+ PIO_STACK_LOCATION pSl = IoGetCurrentIrpStackLocation(pIrp);
+ DEVICE_POWER_STATE enmDevPState = pSl->Parameters.Power.State.DeviceState;
+ DEVICE_POWER_STATE enmCurDevPState = pDevExt->DdiState.PwrState.PowerState.DeviceState;
+ NTSTATUS Status = STATUS_SUCCESS;
+
+ if (enmDevPState > enmCurDevPState && enmCurDevPState == PowerDeviceD0)
+ {
+ Status = vboxUsbPwrIoWaitCompletionAndPostAsync(pDevExt, pIrp);
+ Assert(NT_SUCCESS(Status));
+ if (NT_SUCCESS(Status))
+ return Status;
+ }
+
+ PoStartNextPowerIrp(pIrp);
+
+ if (NT_SUCCESS(Status))
+ {
+ IoCopyCurrentIrpStackLocationToNext(pIrp);
+ IoSetCompletionRoutine(pIrp, vboxUsbPwrIoPostDevCompletion, pDevExt, TRUE, TRUE, TRUE);
+ Status = PoCallDriver(pDevExt->pLowerDO, pIrp);
+ }
+ else
+ {
+ pIrp->IoStatus.Status = Status;
+ pIrp->IoStatus.Information = 0;
+
+ IoCompleteRequest(pIrp, IO_NO_INCREMENT);
+ vboxUsbDdiStateRelease(pDevExt);
+ }
+
+ return Status;
+}
+
+
+static NTSTATUS vboxUsbPwrMnSetPower(IN PVBOXUSBDEV_EXT pDevExt, IN PIRP pIrp)
+{
+ PIO_STACK_LOCATION pSl = IoGetCurrentIrpStackLocation(pIrp);
+ switch (pSl->Parameters.Power.Type)
+ {
+ case SystemPowerState:
+ return vboxUsbPwrSetPowerSys(pDevExt, pIrp);
+
+ case DevicePowerState:
+ return vboxUsbPwrSetPowerDev(pDevExt, pIrp);
+
+ default:
+ AssertFailed();
+ return vboxUsbPwrMnDefault(pDevExt, pIrp);
+ }
+}
+
+static NTSTATUS vboxUsbPwrMnWaitWake(IN PVBOXUSBDEV_EXT pDevExt, IN PIRP pIrp)
+{
+ AssertFailed();
+ return vboxUsbPwrMnDefault(pDevExt, pIrp);
+}
+
+
+static NTSTATUS vboxUsbPwrDispatch(IN PVBOXUSBDEV_EXT pDevExt, IN PIRP pIrp)
+{
+ PIO_STACK_LOCATION pSl = IoGetCurrentIrpStackLocation(pIrp);
+
+ switch (pSl->MinorFunction)
+ {
+ case IRP_MN_POWER_SEQUENCE:
+ return vboxUsbPwrMnPowerSequence(pDevExt, pIrp);
+
+ case IRP_MN_QUERY_POWER:
+ return vboxUsbPwrMnQueryPower(pDevExt, pIrp);
+
+ case IRP_MN_SET_POWER:
+ return vboxUsbPwrMnSetPower(pDevExt, pIrp);
+
+ case IRP_MN_WAIT_WAKE:
+ return vboxUsbPwrMnWaitWake(pDevExt, pIrp);
+
+ default:
+// AssertFailed();
+ return vboxUsbPwrMnDefault(pDevExt, pIrp);
+ }
+}
+
+DECLHIDDEN(NTSTATUS) vboxUsbDispatchPower(IN PDEVICE_OBJECT pDeviceObject, IN PIRP pIrp)
+{
+ PVBOXUSBDEV_EXT pDevExt = (PVBOXUSBDEV_EXT)pDeviceObject->DeviceExtension;
+ ENMVBOXUSB_PNPSTATE enmState = vboxUsbDdiStateRetainIfNotRemoved(pDevExt);
+ switch (enmState)
+ {
+ case ENMVBOXUSB_PNPSTATE_REMOVED:
+ PoStartNextPowerIrp(pIrp);
+
+ pIrp->IoStatus.Status = STATUS_DELETE_PENDING;
+ pIrp->IoStatus.Information = 0;
+
+ IoCompleteRequest(pIrp, IO_NO_INCREMENT);
+
+ vboxUsbDdiStateRelease(pDevExt);
+
+ return STATUS_DELETE_PENDING;
+
+ case ENMVBOXUSB_PNPSTATE_START_PENDING:
+ PoStartNextPowerIrp(pIrp);
+ IoSkipCurrentIrpStackLocation(pIrp);
+
+ vboxUsbDdiStateRelease(pDevExt);
+
+ return PoCallDriver(pDevExt->pLowerDO, pIrp);
+
+ default:
+ return vboxUsbPwrDispatch(pDevExt, pIrp);
+ }
+}
+
diff --git a/src/VBox/HostDrivers/VBoxUSB/win/dev/VBoxUsbPwr.h b/src/VBox/HostDrivers/VBoxUSB/win/dev/VBoxUsbPwr.h
new file mode 100644
index 00000000..792187ed
--- /dev/null
+++ b/src/VBox/HostDrivers/VBoxUSB/win/dev/VBoxUsbPwr.h
@@ -0,0 +1,51 @@
+/* $Id: VBoxUsbPwr.h $ */
+/** @file
+ * USB Power state Handling
+ */
+/*
+ * Copyright (C) 2011-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+#ifndef VBOX_INCLUDED_SRC_VBoxUSB_win_dev_VBoxUsbPwr_h
+#define VBOX_INCLUDED_SRC_VBoxUSB_win_dev_VBoxUsbPwr_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+typedef struct VBOXUSB_PWRSTATE
+{
+ POWER_STATE PowerState;
+ ULONG PowerDownLevel;
+} VBOXUSB_PWRSTATE, *PVBOXUSB_PWRSTATE;
+
+DECLHIDDEN(VOID) vboxUsbPwrStateInit(PVBOXUSBDEV_EXT pDevExt);
+DECLHIDDEN(NTSTATUS) vboxUsbDispatchPower(IN PDEVICE_OBJECT pDeviceObject, IN PIRP pIrp);
+
+#endif /* !VBOX_INCLUDED_SRC_VBoxUSB_win_dev_VBoxUsbPwr_h */
diff --git a/src/VBox/HostDrivers/VBoxUSB/win/dev/VBoxUsbRt.cpp b/src/VBox/HostDrivers/VBoxUSB/win/dev/VBoxUsbRt.cpp
new file mode 100644
index 00000000..f4e82a33
--- /dev/null
+++ b/src/VBox/HostDrivers/VBoxUSB/win/dev/VBoxUsbRt.cpp
@@ -0,0 +1,1593 @@
+/* $Id: VBoxUsbRt.cpp $ */
+/** @file
+ * VBox USB R0 runtime
+ */
+
+/*
+ * Copyright (C) 2011-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include "VBoxUsbCmn.h"
+#include "../cmn/VBoxUsbIdc.h"
+#include "../cmn/VBoxUsbTool.h"
+
+#include <VBox/usblib-win.h>
+#include <iprt/assert.h>
+#include <VBox/log.h>
+
+
+/*********************************************************************************************************************************
+* Defined Constants And Macros *
+*********************************************************************************************************************************/
+#define _USBD_ /** @todo r=bird: What is this?? */
+
+#define USBD_DEFAULT_PIPE_TRANSFER 0x00000008
+
+#define VBOXUSB_MAGIC 0xABCF1423
+
+
+/*********************************************************************************************************************************
+* Structures and Typedefs *
+*********************************************************************************************************************************/
+typedef struct VBOXUSB_URB_CONTEXT
+{
+ PURB pUrb;
+ PMDL pMdlBuf;
+ PVBOXUSBDEV_EXT pDevExt;
+ PVOID pOut;
+ ULONG ulTransferType;
+ ULONG ulMagic;
+} VBOXUSB_URB_CONTEXT, * PVBOXUSB_URB_CONTEXT;
+
+typedef struct VBOXUSB_SETUP
+{
+ uint8_t bmRequestType;
+ uint8_t bRequest;
+ uint16_t wValue;
+ uint16_t wIndex;
+ uint16_t wLength;
+} VBOXUSB_SETUP, *PVBOXUSB_SETUP;
+
+
+
+static bool vboxUsbRtCtxSetOwner(PVBOXUSBDEV_EXT pDevExt, PFILE_OBJECT pFObj)
+{
+ bool fRc = ASMAtomicCmpXchgPtr(&pDevExt->Rt.pOwner, pFObj, NULL);
+ if (fRc)
+ LogFunc(("pDevExt (0x%x) Owner(0x%x) acquired\n", pFObj));
+ else
+ LogFunc(("pDevExt (0x%x) Owner(0x%x) FAILED!!\n", pFObj));
+ return fRc;
+}
+
+static bool vboxUsbRtCtxReleaseOwner(PVBOXUSBDEV_EXT pDevExt, PFILE_OBJECT pFObj)
+{
+ bool fRc = ASMAtomicCmpXchgPtr(&pDevExt->Rt.pOwner, NULL, pFObj);
+ if (fRc)
+ LogFunc(("pDevExt (0x%x) Owner(0x%x) released\n", pFObj));
+ else
+ LogFunc(("pDevExt (0x%x) Owner(0x%x) release: is NOT an owner\n", pFObj));
+ return fRc;
+}
+
+static bool vboxUsbRtCtxIsOwner(PVBOXUSBDEV_EXT pDevExt, PFILE_OBJECT pFObj)
+{
+ PFILE_OBJECT pOwner = (PFILE_OBJECT)ASMAtomicReadPtr((void *volatile *)(&pDevExt->Rt.pOwner));
+ return pOwner == pFObj;
+}
+
+static NTSTATUS vboxUsbRtIdcSubmit(ULONG uCtl, void *pvBuffer)
+{
+ /* we just reuse the standard usb tooling for simplicity here */
+ NTSTATUS Status = VBoxUsbToolIoInternalCtlSendSync(g_VBoxUsbGlobals.RtIdc.pDevice, uCtl, pvBuffer, NULL);
+ Assert(Status == STATUS_SUCCESS);
+ return Status;
+}
+
+static NTSTATUS vboxUsbRtIdcInit()
+{
+ UNICODE_STRING UniName;
+ RtlInitUnicodeString(&UniName, USBMON_DEVICE_NAME_NT);
+ NTSTATUS Status = IoGetDeviceObjectPointer(&UniName, FILE_ALL_ACCESS, &g_VBoxUsbGlobals.RtIdc.pFile, &g_VBoxUsbGlobals.RtIdc.pDevice);
+ if (NT_SUCCESS(Status))
+ {
+ VBOXUSBIDC_VERSION Version;
+ vboxUsbRtIdcSubmit(VBOXUSBIDC_INTERNAL_IOCTL_GET_VERSION, &Version);
+ if (NT_SUCCESS(Status))
+ {
+ if ( Version.u32Major == VBOXUSBIDC_VERSION_MAJOR
+#if VBOXUSBIDC_VERSION_MINOR != 0
+ && Version.u32Minor >= VBOXUSBIDC_VERSION_MINOR
+#endif
+ )
+ return STATUS_SUCCESS;
+ AssertFailed();
+ }
+ else
+ {
+ AssertFailed();
+ }
+
+ /* this will as well dereference the dev obj */
+ ObDereferenceObject(g_VBoxUsbGlobals.RtIdc.pFile);
+ }
+ else
+ {
+ AssertFailed();
+ }
+
+ memset(&g_VBoxUsbGlobals.RtIdc, 0, sizeof (g_VBoxUsbGlobals.RtIdc));
+ return Status;
+}
+
+static VOID vboxUsbRtIdcTerm()
+{
+ Assert(g_VBoxUsbGlobals.RtIdc.pFile);
+ Assert(g_VBoxUsbGlobals.RtIdc.pDevice);
+ ObDereferenceObject(g_VBoxUsbGlobals.RtIdc.pFile);
+ memset(&g_VBoxUsbGlobals.RtIdc, 0, sizeof (g_VBoxUsbGlobals.RtIdc));
+}
+
+static NTSTATUS vboxUsbRtIdcReportDevStart(PDEVICE_OBJECT pPDO, HVBOXUSBIDCDEV *phDev)
+{
+ VBOXUSBIDC_PROXY_STARTUP Start;
+ Start.u.pPDO = pPDO;
+
+ *phDev = NULL;
+
+ NTSTATUS Status = vboxUsbRtIdcSubmit(VBOXUSBIDC_INTERNAL_IOCTL_PROXY_STARTUP, &Start);
+ Assert(Status == STATUS_SUCCESS);
+ if (!NT_SUCCESS(Status))
+ return Status;
+
+ *phDev = Start.u.hDev;
+ return STATUS_SUCCESS;
+}
+
+static NTSTATUS vboxUsbRtIdcReportDevStop(HVBOXUSBIDCDEV hDev)
+{
+ VBOXUSBIDC_PROXY_TEARDOWN Stop;
+ Stop.hDev = hDev;
+
+ NTSTATUS Status = vboxUsbRtIdcSubmit(VBOXUSBIDC_INTERNAL_IOCTL_PROXY_TEARDOWN, &Stop);
+ Assert(Status == STATUS_SUCCESS);
+ return Status;
+}
+
+
+DECLHIDDEN(NTSTATUS) vboxUsbRtGlobalsInit()
+{
+ return vboxUsbRtIdcInit();
+}
+
+DECLHIDDEN(VOID) vboxUsbRtGlobalsTerm()
+{
+ vboxUsbRtIdcTerm();
+}
+
+
+DECLHIDDEN(NTSTATUS) vboxUsbRtInit(PVBOXUSBDEV_EXT pDevExt)
+{
+ RtlZeroMemory(&pDevExt->Rt, sizeof (pDevExt->Rt));
+ NTSTATUS Status = IoRegisterDeviceInterface(pDevExt->pPDO, &GUID_CLASS_VBOXUSB,
+ NULL, /* IN PUNICODE_STRING ReferenceString OPTIONAL */
+ &pDevExt->Rt.IfName);
+ Assert(Status == STATUS_SUCCESS);
+ if (NT_SUCCESS(Status))
+ {
+ Status = vboxUsbRtIdcReportDevStart(pDevExt->pPDO, &pDevExt->Rt.hMonDev);
+ Assert(Status == STATUS_SUCCESS);
+ if (NT_SUCCESS(Status))
+ {
+ Assert(pDevExt->Rt.hMonDev);
+ return STATUS_SUCCESS;
+ }
+
+ NTSTATUS tmpStatus = IoSetDeviceInterfaceState(&pDevExt->Rt.IfName, FALSE);
+ Assert(tmpStatus == STATUS_SUCCESS);
+ if (NT_SUCCESS(tmpStatus))
+ {
+ RtlFreeUnicodeString(&pDevExt->Rt.IfName);
+ }
+ }
+ return Status;
+}
+
+/**
+ * Free cached USB device/configuration descriptors
+ *
+ * @param pDevExt USB DevExt pointer
+ */
+static void vboxUsbRtFreeCachedDescriptors(PVBOXUSBDEV_EXT pDevExt)
+{
+ if (pDevExt->Rt.devdescr)
+ {
+ vboxUsbMemFree(pDevExt->Rt.devdescr);
+ pDevExt->Rt.devdescr = NULL;
+ }
+ for (ULONG i = 0; i < VBOXUSBRT_MAX_CFGS; ++i)
+ {
+ if (pDevExt->Rt.cfgdescr[i])
+ {
+ vboxUsbMemFree(pDevExt->Rt.cfgdescr[i]);
+ pDevExt->Rt.cfgdescr[i] = NULL;
+ }
+ }
+}
+
+/**
+ * Free per-device interface info
+ *
+ * @param pDevExt USB DevExt pointer
+ * @param fAbortPipes If true, also abort any open pipes
+ */
+static void vboxUsbRtFreeInterfaces(PVBOXUSBDEV_EXT pDevExt, BOOLEAN fAbortPipes)
+{
+ unsigned i;
+ unsigned j;
+
+ /*
+ * Free old interface info
+ */
+ if (pDevExt->Rt.pVBIfaceInfo)
+ {
+ for (i=0;i<pDevExt->Rt.uNumInterfaces;i++)
+ {
+ if (pDevExt->Rt.pVBIfaceInfo[i].pInterfaceInfo)
+ {
+ if (fAbortPipes)
+ {
+ for (j=0; j<pDevExt->Rt.pVBIfaceInfo[i].pInterfaceInfo->NumberOfPipes; j++)
+ {
+ Log(("Aborting Pipe %d handle %x address %x\n", j,
+ pDevExt->Rt.pVBIfaceInfo[i].pInterfaceInfo->Pipes[j].PipeHandle,
+ pDevExt->Rt.pVBIfaceInfo[i].pInterfaceInfo->Pipes[j].EndpointAddress));
+ VBoxUsbToolPipeClear(pDevExt->pLowerDO, pDevExt->Rt.pVBIfaceInfo[i].pInterfaceInfo->Pipes[j].PipeHandle, FALSE);
+ }
+ }
+ vboxUsbMemFree(pDevExt->Rt.pVBIfaceInfo[i].pInterfaceInfo);
+ }
+ pDevExt->Rt.pVBIfaceInfo[i].pInterfaceInfo = NULL;
+ if (pDevExt->Rt.pVBIfaceInfo[i].pPipeInfo)
+ vboxUsbMemFree(pDevExt->Rt.pVBIfaceInfo[i].pPipeInfo);
+ pDevExt->Rt.pVBIfaceInfo[i].pPipeInfo = NULL;
+ }
+ vboxUsbMemFree(pDevExt->Rt.pVBIfaceInfo);
+ pDevExt->Rt.pVBIfaceInfo = NULL;
+ }
+}
+
+DECLHIDDEN(VOID) vboxUsbRtClear(PVBOXUSBDEV_EXT pDevExt)
+{
+ vboxUsbRtFreeCachedDescriptors(pDevExt);
+ vboxUsbRtFreeInterfaces(pDevExt, FALSE);
+}
+
+DECLHIDDEN(NTSTATUS) vboxUsbRtRm(PVBOXUSBDEV_EXT pDevExt)
+{
+ if (!pDevExt->Rt.IfName.Buffer)
+ return STATUS_SUCCESS;
+
+ NTSTATUS Status = vboxUsbRtIdcReportDevStop(pDevExt->Rt.hMonDev);
+ Assert(Status == STATUS_SUCCESS);
+ Status = IoSetDeviceInterfaceState(&pDevExt->Rt.IfName, FALSE);
+ Assert(Status == STATUS_SUCCESS);
+ if (NT_SUCCESS(Status))
+ {
+ RtlFreeUnicodeString(&pDevExt->Rt.IfName);
+ pDevExt->Rt.IfName.Buffer = NULL;
+ }
+ return Status;
+}
+
+DECLHIDDEN(NTSTATUS) vboxUsbRtStart(PVBOXUSBDEV_EXT pDevExt)
+{
+ NTSTATUS Status = IoSetDeviceInterfaceState(&pDevExt->Rt.IfName, TRUE);
+ Assert(Status == STATUS_SUCCESS);
+ return Status;
+}
+
+static NTSTATUS vboxUsbRtCacheDescriptors(PVBOXUSBDEV_EXT pDevExt)
+{
+ NTSTATUS Status = STATUS_INSUFFICIENT_RESOURCES;
+// uint32_t uTotalLength;
+// unsigned i;
+
+ /* Read device descriptor */
+ Assert(!pDevExt->Rt.devdescr);
+ pDevExt->Rt.devdescr = (PUSB_DEVICE_DESCRIPTOR)vboxUsbMemAlloc(sizeof (USB_DEVICE_DESCRIPTOR));
+ if (pDevExt->Rt.devdescr)
+ {
+ memset(pDevExt->Rt.devdescr, 0, sizeof (USB_DEVICE_DESCRIPTOR));
+ Status = VBoxUsbToolGetDescriptor(pDevExt->pLowerDO, pDevExt->Rt.devdescr, sizeof (USB_DEVICE_DESCRIPTOR), USB_DEVICE_DESCRIPTOR_TYPE, 0, 0, RT_INDEFINITE_WAIT);
+ if (NT_SUCCESS(Status))
+ {
+ Assert(pDevExt->Rt.devdescr->bNumConfigurations > 0);
+ PUSB_CONFIGURATION_DESCRIPTOR pDr = (PUSB_CONFIGURATION_DESCRIPTOR)vboxUsbMemAlloc(sizeof (USB_CONFIGURATION_DESCRIPTOR));
+ Assert(pDr);
+ if (pDr)
+ {
+ UCHAR i = 0;
+ for (; i < pDevExt->Rt.devdescr->bNumConfigurations; ++i)
+ {
+ Status = VBoxUsbToolGetDescriptor(pDevExt->pLowerDO, pDr, sizeof (USB_CONFIGURATION_DESCRIPTOR), USB_CONFIGURATION_DESCRIPTOR_TYPE, i, 0, RT_INDEFINITE_WAIT);
+ if (!NT_SUCCESS(Status))
+ {
+ break;
+ }
+
+ USHORT uTotalLength = pDr->wTotalLength;
+ pDevExt->Rt.cfgdescr[i] = (PUSB_CONFIGURATION_DESCRIPTOR)vboxUsbMemAlloc(uTotalLength);
+ if (!pDevExt->Rt.cfgdescr[i])
+ {
+ Status = STATUS_INSUFFICIENT_RESOURCES;
+ break;
+ }
+
+ Status = VBoxUsbToolGetDescriptor(pDevExt->pLowerDO, pDevExt->Rt.cfgdescr[i], uTotalLength, USB_CONFIGURATION_DESCRIPTOR_TYPE, i, 0, RT_INDEFINITE_WAIT);
+ if (!NT_SUCCESS(Status))
+ {
+ break;
+ }
+ }
+
+ vboxUsbMemFree(pDr);
+
+ if (NT_SUCCESS(Status))
+ return Status;
+
+ /* recources will be freed in vboxUsbRtFreeCachedDescriptors below */
+ }
+ }
+
+ vboxUsbRtFreeCachedDescriptors(pDevExt);
+ }
+
+ /* shoud be only on fail here */
+ Assert(!NT_SUCCESS(Status));
+ return Status;
+}
+
+static NTSTATUS vboxUsbRtDispatchClaimDevice(PVBOXUSBDEV_EXT pDevExt, PIRP pIrp)
+{
+ PIO_STACK_LOCATION pSl = IoGetCurrentIrpStackLocation(pIrp);
+ PFILE_OBJECT pFObj = pSl->FileObject;
+ PUSBSUP_CLAIMDEV pDev = (PUSBSUP_CLAIMDEV)pIrp->AssociatedIrp.SystemBuffer;
+ ULONG cbOut = 0;
+ NTSTATUS Status = STATUS_SUCCESS;
+
+ do
+ {
+ if (!pFObj)
+ {
+ AssertFailed();
+ Status = STATUS_INVALID_PARAMETER;
+ break;
+ }
+
+ if ( !pDev
+ || pSl->Parameters.DeviceIoControl.InputBufferLength != sizeof (*pDev)
+ || pSl->Parameters.DeviceIoControl.OutputBufferLength != sizeof (*pDev))
+ {
+ AssertFailed();
+ Status = STATUS_INVALID_PARAMETER;
+ break;
+ }
+
+ if (!vboxUsbRtCtxSetOwner(pDevExt, pFObj))
+ {
+ AssertFailed();
+ pDev->fClaimed = false;
+ cbOut = sizeof (*pDev);
+ break;
+ }
+
+ vboxUsbRtFreeCachedDescriptors(pDevExt);
+ Status = vboxUsbRtCacheDescriptors(pDevExt);
+ if (NT_SUCCESS(Status))
+ {
+ pDev->fClaimed = true;
+ cbOut = sizeof (*pDev);
+ }
+ } while (0);
+
+ Assert(Status != STATUS_PENDING);
+ VBoxDrvToolIoComplete(pIrp, Status, cbOut);
+ vboxUsbDdiStateRelease(pDevExt);
+ return Status;
+}
+
+static NTSTATUS vboxUsbRtDispatchReleaseDevice(PVBOXUSBDEV_EXT pDevExt, PIRP pIrp)
+{
+ PIO_STACK_LOCATION pSl = IoGetCurrentIrpStackLocation(pIrp);
+ PFILE_OBJECT pFObj = pSl->FileObject;
+ NTSTATUS Status= STATUS_SUCCESS;
+
+ if (vboxUsbRtCtxIsOwner(pDevExt, pFObj))
+ {
+ vboxUsbRtFreeCachedDescriptors(pDevExt);
+ bool fRc = vboxUsbRtCtxReleaseOwner(pDevExt, pFObj);
+ Assert(fRc); NOREF(fRc);
+ }
+ else
+ {
+ AssertFailed();
+ Status = STATUS_ACCESS_DENIED;
+ }
+
+ VBoxDrvToolIoComplete(pIrp, STATUS_SUCCESS, 0);
+ vboxUsbDdiStateRelease(pDevExt);
+ return STATUS_SUCCESS;
+}
+
+static NTSTATUS vboxUsbRtGetDeviceDescription(PVBOXUSBDEV_EXT pDevExt)
+{
+ NTSTATUS Status = STATUS_INSUFFICIENT_RESOURCES;
+ PUSB_DEVICE_DESCRIPTOR pDr = (PUSB_DEVICE_DESCRIPTOR)vboxUsbMemAllocZ(sizeof (USB_DEVICE_DESCRIPTOR));
+ if (pDr)
+ {
+ Status = VBoxUsbToolGetDescriptor(pDevExt->pLowerDO, pDr, sizeof(*pDr), USB_DEVICE_DESCRIPTOR_TYPE, 0, 0, RT_INDEFINITE_WAIT);
+ if (NT_SUCCESS(Status))
+ {
+ pDevExt->Rt.idVendor = pDr->idVendor;
+ pDevExt->Rt.idProduct = pDr->idProduct;
+ pDevExt->Rt.bcdDevice = pDr->bcdDevice;
+ pDevExt->Rt.szSerial[0] = 0;
+
+ if (pDr->iSerialNumber
+#ifdef DEBUG
+ || pDr->iProduct || pDr->iManufacturer
+#endif
+ )
+ {
+ int langId;
+ Status = VBoxUsbToolGetLangID(pDevExt->pLowerDO, &langId, RT_INDEFINITE_WAIT);
+ if (NT_SUCCESS(Status))
+ {
+ Status = VBoxUsbToolGetStringDescriptor(pDevExt->pLowerDO, pDevExt->Rt.szSerial, sizeof (pDevExt->Rt.szSerial),
+ pDr->iSerialNumber, langId, RT_INDEFINITE_WAIT);
+ }
+ else
+ {
+ Status = STATUS_SUCCESS;
+ }
+ }
+ }
+ vboxUsbMemFree(pDr);
+ }
+
+ return Status;
+}
+
+static NTSTATUS vboxUsbRtDispatchGetDevice(PVBOXUSBDEV_EXT pDevExt, PIRP pIrp)
+{
+ PIO_STACK_LOCATION pSl = IoGetCurrentIrpStackLocation(pIrp);
+ PUSBSUP_GETDEV pDev = (PUSBSUP_GETDEV)pIrp->AssociatedIrp.SystemBuffer;
+ ULONG cbOut = 0;
+
+ /* don't check for owner since this request is allowed for non-owners as well */
+ NTSTATUS Status;
+ if ( pDev
+ && pSl->Parameters.DeviceIoControl.InputBufferLength == sizeof(*pDev)
+ && pSl->Parameters.DeviceIoControl.OutputBufferLength == sizeof(*pDev))
+ {
+ /* Even if we don't return it, we need to query the HS flag for later use. */
+ Status = VBoxUsbToolGetDeviceSpeed(pDevExt->pLowerDO, &pDevExt->Rt.fIsHighSpeed);
+ if (NT_SUCCESS(Status))
+ {
+ pDev->hDevice = pDevExt->Rt.hMonDev;
+ cbOut = sizeof (*pDev);
+ }
+ }
+ else
+ Status = STATUS_INVALID_PARAMETER;
+
+ Assert(Status != STATUS_PENDING);
+ VBoxDrvToolIoComplete(pIrp, Status, cbOut);
+ vboxUsbDdiStateRelease(pDevExt);
+ return Status;
+}
+
+static NTSTATUS vboxUsbRtDispatchUsbReset(PVBOXUSBDEV_EXT pDevExt, PIRP pIrp)
+{
+ PIO_STACK_LOCATION pSl = IoGetCurrentIrpStackLocation(pIrp);
+ PFILE_OBJECT pFObj = pSl->FileObject;
+ NTSTATUS rcNt;
+ if (pFObj)
+ {
+ if (vboxUsbRtCtxIsOwner(pDevExt, pFObj))
+ {
+ if ( pIrp->AssociatedIrp.SystemBuffer == NULL
+ && pSl->Parameters.DeviceIoControl.InputBufferLength == 0
+ && pSl->Parameters.DeviceIoControl.OutputBufferLength == 0)
+ {
+ rcNt = VBoxUsbToolIoInternalCtlSendSync(pDevExt->pLowerDO, IOCTL_INTERNAL_USB_RESET_PORT, NULL, NULL);
+ Assert(NT_SUCCESS(rcNt));
+ }
+ else
+ {
+ AssertFailed();
+ rcNt = STATUS_INVALID_PARAMETER;
+ }
+ }
+ else
+ {
+ AssertFailed();
+ rcNt = STATUS_ACCESS_DENIED;
+ }
+ }
+ else
+ {
+ AssertFailed();
+ rcNt = STATUS_INVALID_PARAMETER;
+ }
+
+ Assert(rcNt != STATUS_PENDING);
+ VBoxDrvToolIoComplete(pIrp, rcNt, 0);
+ vboxUsbDdiStateRelease(pDevExt);
+ return rcNt;
+}
+
+static PUSB_CONFIGURATION_DESCRIPTOR vboxUsbRtFindConfigDesc(PVBOXUSBDEV_EXT pDevExt, uint8_t uConfiguration)
+{
+ PUSB_CONFIGURATION_DESCRIPTOR pCfgDr = NULL;
+
+ for (ULONG i = 0; i < VBOXUSBRT_MAX_CFGS; ++i)
+ {
+ if (pDevExt->Rt.cfgdescr[i])
+ {
+ if (pDevExt->Rt.cfgdescr[i]->bConfigurationValue == uConfiguration)
+ {
+ pCfgDr = pDevExt->Rt.cfgdescr[i];
+ break;
+ }
+ }
+ }
+
+ return pCfgDr;
+}
+
+static NTSTATUS vboxUsbRtSetConfig(PVBOXUSBDEV_EXT pDevExt, uint8_t uConfiguration)
+{
+ PURB pUrb = NULL;
+ NTSTATUS Status = STATUS_SUCCESS;
+ uint32_t i;
+
+ if (!uConfiguration)
+ {
+ pUrb = VBoxUsbToolUrbAllocZ(URB_FUNCTION_SELECT_CONFIGURATION, sizeof (struct _URB_SELECT_CONFIGURATION));
+ if (!pUrb)
+ {
+ AssertMsgFailed((__FUNCTION__": VBoxUsbToolUrbAlloc failed\n"));
+ return STATUS_INSUFFICIENT_RESOURCES;
+ }
+
+ vboxUsbRtFreeInterfaces(pDevExt, TRUE);
+
+ pUrb->UrbSelectConfiguration.ConfigurationDescriptor = NULL;
+
+ Status = VBoxUsbToolUrbPost(pDevExt->pLowerDO, pUrb, RT_INDEFINITE_WAIT);
+ if (NT_SUCCESS(Status) && USBD_SUCCESS(pUrb->UrbHeader.Status))
+ {
+ pDevExt->Rt.hConfiguration = pUrb->UrbSelectConfiguration.ConfigurationHandle;
+ pDevExt->Rt.uConfigValue = uConfiguration;
+ }
+ else
+ {
+ AssertMsgFailed((__FUNCTION__": VBoxUsbToolUrbPost failed Status (0x%x), usb Status (0x%x)\n", Status, pUrb->UrbHeader.Status));
+ }
+
+ VBoxUsbToolUrbFree(pUrb);
+
+ return Status;
+ }
+
+/** @todo r=bird: Need to write a script for fixing these kind of clueless use
+ * of AssertMsgFailed (into AssertMsgReturn). The __FUNCTION__ is just
+ * the topping it off - the assertion message includes function, file and
+ * line number. Duh! */
+ PUSB_CONFIGURATION_DESCRIPTOR pCfgDr = vboxUsbRtFindConfigDesc(pDevExt, uConfiguration);
+ if (!pCfgDr)
+ {
+ AssertMsgFailed((__FUNCTION__": VBoxUSBFindConfigDesc did not find cfg (%d)\n", uConfiguration));
+ return STATUS_INVALID_PARAMETER;
+ }
+
+ PUSBD_INTERFACE_LIST_ENTRY pIfLe = (PUSBD_INTERFACE_LIST_ENTRY)vboxUsbMemAllocZ((pCfgDr->bNumInterfaces + 1) * sizeof(USBD_INTERFACE_LIST_ENTRY));
+ if (!pIfLe)
+ {
+ AssertMsgFailed((__FUNCTION__": vboxUsbMemAllocZ for pIfLe failed\n"));
+ return STATUS_INSUFFICIENT_RESOURCES;
+ }
+
+ for (i = 0; i < pCfgDr->bNumInterfaces; i++)
+ {
+ pIfLe[i].InterfaceDescriptor = USBD_ParseConfigurationDescriptorEx(pCfgDr, pCfgDr, i, 0, -1, -1, -1);
+ if (!pIfLe[i].InterfaceDescriptor)
+ {
+ AssertMsgFailed((__FUNCTION__": interface %d not found\n", i));
+ Status = STATUS_INVALID_PARAMETER;
+ break;
+ }
+ }
+ pIfLe[pCfgDr->bNumInterfaces].InterfaceDescriptor = NULL;
+
+ if (NT_SUCCESS(Status))
+ {
+ pUrb = USBD_CreateConfigurationRequestEx(pCfgDr, pIfLe);
+ if (pUrb)
+ {
+ Status = VBoxUsbToolUrbPost(pDevExt->pLowerDO, pUrb, RT_INDEFINITE_WAIT);
+ if (NT_SUCCESS(Status) && USBD_SUCCESS(pUrb->UrbHeader.Status))
+ {
+ vboxUsbRtFreeInterfaces(pDevExt, FALSE);
+
+ pDevExt->Rt.hConfiguration = pUrb->UrbSelectConfiguration.ConfigurationHandle;
+ pDevExt->Rt.uConfigValue = uConfiguration;
+ pDevExt->Rt.uNumInterfaces = pCfgDr->bNumInterfaces;
+
+ pDevExt->Rt.pVBIfaceInfo = (VBOXUSB_IFACE_INFO*)vboxUsbMemAllocZ(pDevExt->Rt.uNumInterfaces * sizeof (VBOXUSB_IFACE_INFO));
+ if (pDevExt->Rt.pVBIfaceInfo)
+ {
+ Assert(NT_SUCCESS(Status));
+ for (i = 0; i < pDevExt->Rt.uNumInterfaces; i++)
+ {
+ size_t uTotalIfaceInfoLength = GET_USBD_INTERFACE_SIZE(pIfLe[i].Interface->NumberOfPipes);
+ pDevExt->Rt.pVBIfaceInfo[i].pInterfaceInfo = (PUSBD_INTERFACE_INFORMATION)vboxUsbMemAlloc(uTotalIfaceInfoLength);
+ if (!pDevExt->Rt.pVBIfaceInfo[i].pInterfaceInfo)
+ {
+ AssertMsgFailed((__FUNCTION__": vboxUsbMemAlloc failed\n"));
+ Status = STATUS_INSUFFICIENT_RESOURCES;
+ break;
+ }
+
+ if (pIfLe[i].Interface->NumberOfPipes > 0)
+ {
+ pDevExt->Rt.pVBIfaceInfo[i].pPipeInfo = (VBOXUSB_PIPE_INFO *)vboxUsbMemAlloc(pIfLe[i].Interface->NumberOfPipes * sizeof(VBOXUSB_PIPE_INFO));
+ if (!pDevExt->Rt.pVBIfaceInfo[i].pPipeInfo)
+ {
+ AssertMsgFailed((__FUNCTION__": vboxUsbMemAlloc failed\n"));
+ Status = STATUS_NO_MEMORY;
+ break;
+ }
+ }
+ else
+ {
+ pDevExt->Rt.pVBIfaceInfo[i].pPipeInfo = NULL;
+ }
+
+ RtlCopyMemory(pDevExt->Rt.pVBIfaceInfo[i].pInterfaceInfo, pIfLe[i].Interface, uTotalIfaceInfoLength);
+
+ for (ULONG j = 0; j < pIfLe[i].Interface->NumberOfPipes; j++)
+ {
+ pDevExt->Rt.pVBIfaceInfo[i].pPipeInfo[j].EndpointAddress = pIfLe[i].Interface->Pipes[j].EndpointAddress;
+ pDevExt->Rt.pVBIfaceInfo[i].pPipeInfo[j].NextScheduledFrame = 0;
+ }
+ }
+
+// if (NT_SUCCESS(Status))
+// {
+//
+// }
+ }
+ else
+ {
+ AssertMsgFailed((__FUNCTION__": vboxUsbMemAllocZ failed\n"));
+ Status = STATUS_NO_MEMORY;
+ }
+ }
+ else
+ {
+ AssertMsgFailed((__FUNCTION__": VBoxUsbToolUrbPost failed Status (0x%x), usb Status (0x%x)\n", Status, pUrb->UrbHeader.Status));
+ }
+ ExFreePool(pUrb);
+ }
+ else
+ {
+ AssertMsgFailed((__FUNCTION__": USBD_CreateConfigurationRequestEx failed\n"));
+ Status = STATUS_INSUFFICIENT_RESOURCES;
+ }
+ }
+
+ vboxUsbMemFree(pIfLe);
+
+ return Status;
+}
+
+static NTSTATUS vboxUsbRtDispatchUsbSetConfig(PVBOXUSBDEV_EXT pDevExt, PIRP pIrp)
+{
+ PIO_STACK_LOCATION pSl = IoGetCurrentIrpStackLocation(pIrp);
+ PFILE_OBJECT pFObj = pSl->FileObject;
+ PUSBSUP_SET_CONFIG pCfg = (PUSBSUP_SET_CONFIG)pIrp->AssociatedIrp.SystemBuffer;
+ NTSTATUS Status = STATUS_SUCCESS;
+
+ do
+ {
+ if (!pFObj)
+ {
+ AssertFailed();
+ Status = STATUS_INVALID_PARAMETER;
+ break;
+ }
+
+ if (!vboxUsbRtCtxIsOwner(pDevExt, pFObj))
+ {
+ AssertFailed();
+ Status = STATUS_ACCESS_DENIED;
+ break;
+ }
+
+ if ( !pCfg
+ || pSl->Parameters.DeviceIoControl.InputBufferLength != sizeof (*pCfg)
+ || pSl->Parameters.DeviceIoControl.OutputBufferLength != 0)
+ {
+ AssertMsgFailed((__FUNCTION__": STATUS_INVALID_PARAMETER\n"));
+ Status = STATUS_INVALID_PARAMETER;
+ break;
+ }
+
+ Status = vboxUsbRtSetConfig(pDevExt, pCfg->bConfigurationValue);
+ } while (0);
+
+ Assert(Status != STATUS_PENDING);
+ VBoxDrvToolIoComplete(pIrp, Status, 0);
+ vboxUsbDdiStateRelease(pDevExt);
+ return Status;
+}
+
+static NTSTATUS vboxUsbRtSetInterface(PVBOXUSBDEV_EXT pDevExt, uint32_t InterfaceNumber, int AlternateSetting)
+{
+ AssertMsgReturn(pDevExt->Rt.uConfigValue, ("Can't select an interface without an active configuration\n"),
+ STATUS_INVALID_PARAMETER);
+ AssertMsgReturn(InterfaceNumber < pDevExt->Rt.uNumInterfaces, ("InterfaceNumber %d too high!!\n", InterfaceNumber),
+ STATUS_INVALID_PARAMETER);
+ PUSB_CONFIGURATION_DESCRIPTOR pCfgDr = vboxUsbRtFindConfigDesc(pDevExt, pDevExt->Rt.uConfigValue);
+ AssertMsgReturn(pCfgDr, ("configuration %d not found!!\n", pDevExt->Rt.uConfigValue),
+ STATUS_INVALID_PARAMETER);
+ PUSB_INTERFACE_DESCRIPTOR pIfDr = USBD_ParseConfigurationDescriptorEx(pCfgDr, pCfgDr, InterfaceNumber, AlternateSetting, -1, -1, -1);
+ AssertMsgReturn(pIfDr, ("invalid interface %d or alternate setting %d\n", InterfaceNumber, AlternateSetting),
+ STATUS_UNSUCCESSFUL);
+
+ USHORT uUrbSize = GET_SELECT_INTERFACE_REQUEST_SIZE(pIfDr->bNumEndpoints);
+ ULONG uTotalIfaceInfoLength = GET_USBD_INTERFACE_SIZE(pIfDr->bNumEndpoints);
+ NTSTATUS Status = STATUS_SUCCESS;
+ PURB pUrb;
+ PUSBD_INTERFACE_INFORMATION pNewIFInfo = NULL;
+ VBOXUSB_PIPE_INFO *pNewPipeInfo = NULL;
+
+ if (pDevExt->Rt.pVBIfaceInfo[InterfaceNumber].pInterfaceInfo)
+ {
+ /* Clear pipes associated with the interface, else Windows may hang. */
+ for (ULONG i = 0; i < pDevExt->Rt.pVBIfaceInfo[InterfaceNumber].pInterfaceInfo->NumberOfPipes; i++)
+ VBoxUsbToolPipeClear(pDevExt->pLowerDO, pDevExt->Rt.pVBIfaceInfo[InterfaceNumber].pInterfaceInfo->Pipes[i].PipeHandle, FALSE);
+ }
+
+ do {
+ /* First allocate all the structures we'll need. */
+ pUrb = VBoxUsbToolUrbAllocZ(0, uUrbSize);
+ if (!pUrb)
+ {
+ AssertMsgFailed((__FUNCTION__": VBoxUsbToolUrbAllocZ failed\n"));
+ Status = STATUS_NO_MEMORY;
+ break;
+ }
+
+ pNewIFInfo = (PUSBD_INTERFACE_INFORMATION)vboxUsbMemAlloc(uTotalIfaceInfoLength);
+ if (!pNewIFInfo)
+ {
+ AssertMsgFailed((__FUNCTION__": Failed allocating interface storage\n"));
+ Status = STATUS_NO_MEMORY;
+ break;
+ }
+
+ if (pIfDr->bNumEndpoints > 0)
+ {
+ pNewPipeInfo = (VBOXUSB_PIPE_INFO *)vboxUsbMemAlloc(pIfDr->bNumEndpoints * sizeof(VBOXUSB_PIPE_INFO));
+ if (!pNewPipeInfo)
+ {
+ AssertMsgFailed((__FUNCTION__": Failed allocating pipe info storage\n"));
+ Status = STATUS_NO_MEMORY;
+ break;
+ }
+ }
+ else
+ pNewPipeInfo = NULL;
+
+ /* Now that we have all the bits, select the interface. */
+ UsbBuildSelectInterfaceRequest(pUrb, uUrbSize, pDevExt->Rt.hConfiguration, InterfaceNumber, AlternateSetting);
+ pUrb->UrbSelectInterface.Interface.Length = GET_USBD_INTERFACE_SIZE(pIfDr->bNumEndpoints);
+
+ Status = VBoxUsbToolUrbPost(pDevExt->pLowerDO, pUrb, RT_INDEFINITE_WAIT);
+ if (NT_SUCCESS(Status) && USBD_SUCCESS(pUrb->UrbHeader.Status))
+ {
+ /* Free the old memory and put new in. */
+ if (pDevExt->Rt.pVBIfaceInfo[InterfaceNumber].pInterfaceInfo)
+ vboxUsbMemFree(pDevExt->Rt.pVBIfaceInfo[InterfaceNumber].pInterfaceInfo);
+ pDevExt->Rt.pVBIfaceInfo[InterfaceNumber].pInterfaceInfo = pNewIFInfo;
+ if (pDevExt->Rt.pVBIfaceInfo[InterfaceNumber].pPipeInfo)
+ vboxUsbMemFree(pDevExt->Rt.pVBIfaceInfo[InterfaceNumber].pPipeInfo);
+ pDevExt->Rt.pVBIfaceInfo[InterfaceNumber].pPipeInfo = pNewPipeInfo;
+ pNewPipeInfo = NULL; pNewIFInfo = NULL; /* Don't try to free it again. */
+
+ USBD_INTERFACE_INFORMATION *pIfInfo = &pUrb->UrbSelectInterface.Interface;
+ memcpy(pDevExt->Rt.pVBIfaceInfo[InterfaceNumber].pInterfaceInfo, pIfInfo, GET_USBD_INTERFACE_SIZE(pIfDr->bNumEndpoints));
+
+ Assert(pIfInfo->NumberOfPipes == pIfDr->bNumEndpoints);
+ for (ULONG i = 0; i < pIfInfo->NumberOfPipes; i++)
+ {
+ pDevExt->Rt.pVBIfaceInfo[InterfaceNumber].pPipeInfo[i].EndpointAddress = pIfInfo->Pipes[i].EndpointAddress;
+ pDevExt->Rt.pVBIfaceInfo[InterfaceNumber].pPipeInfo[i].NextScheduledFrame = 0;
+ }
+ }
+ else
+ {
+ AssertMsgFailed((__FUNCTION__": VBoxUsbToolUrbPost failed Status (0x%x) usb Status (0x%x)\n", Status, pUrb->UrbHeader.Status));
+ }
+ } while (0);
+
+ /* Clean up. */
+ if (pUrb)
+ VBoxUsbToolUrbFree(pUrb);
+ if (pNewIFInfo)
+ vboxUsbMemFree(pNewIFInfo);
+ if (pNewPipeInfo)
+ vboxUsbMemFree(pNewPipeInfo);
+
+ return Status;
+}
+
+static NTSTATUS vboxUsbRtDispatchUsbSelectInterface(PVBOXUSBDEV_EXT pDevExt, PIRP pIrp)
+{
+ PIO_STACK_LOCATION pSl = IoGetCurrentIrpStackLocation(pIrp);
+ PFILE_OBJECT pFObj = pSl->FileObject;
+ PUSBSUP_SELECT_INTERFACE pIf = (PUSBSUP_SELECT_INTERFACE)pIrp->AssociatedIrp.SystemBuffer;
+ NTSTATUS Status;
+
+ do
+ {
+ if (!pFObj)
+ {
+ AssertFailed();
+ Status = STATUS_INVALID_PARAMETER;
+ break;
+ }
+
+ if (!vboxUsbRtCtxIsOwner(pDevExt, pFObj))
+ {
+ AssertFailed();
+ Status = STATUS_ACCESS_DENIED;
+ break;
+ }
+
+ if ( !pIf
+ || pSl->Parameters.DeviceIoControl.InputBufferLength != sizeof (*pIf)
+ || pSl->Parameters.DeviceIoControl.OutputBufferLength != 0)
+ {
+ AssertMsgFailed((__FUNCTION__": STATUS_INVALID_PARAMETER\n"));
+ Status = STATUS_INVALID_PARAMETER;
+ break;
+ }
+
+ Status = vboxUsbRtSetInterface(pDevExt, pIf->bInterfaceNumber, pIf->bAlternateSetting);
+ } while (0);
+
+ Assert(Status != STATUS_PENDING);
+ VBoxDrvToolIoComplete(pIrp, Status, 0);
+ vboxUsbDdiStateRelease(pDevExt);
+ return Status;
+}
+
+static HANDLE vboxUsbRtGetPipeHandle(PVBOXUSBDEV_EXT pDevExt, uint32_t EndPointAddress)
+{
+ if (EndPointAddress == 0)
+ return pDevExt->Rt.hPipe0;
+
+ for (ULONG i = 0; i < pDevExt->Rt.uNumInterfaces; i++)
+ {
+ for (ULONG j = 0; j < pDevExt->Rt.pVBIfaceInfo[i].pInterfaceInfo->NumberOfPipes; j++)
+ {
+ /* Note that bit 7 determines pipe direction, but is still significant
+ * because endpoints may be numbered like 0x01, 0x81, 0x02, 0x82 etc.
+ */
+ if (pDevExt->Rt.pVBIfaceInfo[i].pInterfaceInfo->Pipes[j].EndpointAddress == EndPointAddress)
+ return pDevExt->Rt.pVBIfaceInfo[i].pInterfaceInfo->Pipes[j].PipeHandle;
+ }
+ }
+ return 0;
+}
+
+static VBOXUSB_PIPE_INFO* vboxUsbRtGetPipeInfo(PVBOXUSBDEV_EXT pDevExt, uint32_t EndPointAddress)
+{
+ for (ULONG i = 0; i < pDevExt->Rt.uNumInterfaces; i++)
+ {
+ for (ULONG j = 0; j < pDevExt->Rt.pVBIfaceInfo[i].pInterfaceInfo->NumberOfPipes; j++)
+ {
+ if (pDevExt->Rt.pVBIfaceInfo[i].pPipeInfo[j].EndpointAddress == EndPointAddress)
+ return &pDevExt->Rt.pVBIfaceInfo[i].pPipeInfo[j];
+ }
+ }
+ return NULL;
+}
+
+
+
+static NTSTATUS vboxUsbRtClearEndpoint(PVBOXUSBDEV_EXT pDevExt, uint32_t EndPointAddress, bool fReset)
+{
+ NTSTATUS Status = VBoxUsbToolPipeClear(pDevExt->pLowerDO, vboxUsbRtGetPipeHandle(pDevExt, EndPointAddress), fReset);
+ if (!NT_SUCCESS(Status))
+ {
+ AssertMsgFailed((__FUNCTION__": VBoxUsbToolPipeClear failed Status (0x%x)\n", Status));
+ }
+
+ return Status;
+}
+
+static NTSTATUS vboxUsbRtDispatchUsbClearEndpoint(PVBOXUSBDEV_EXT pDevExt, PIRP pIrp)
+{
+ PIO_STACK_LOCATION pSl = IoGetCurrentIrpStackLocation(pIrp);
+ PFILE_OBJECT pFObj = pSl->FileObject;
+ PUSBSUP_CLEAR_ENDPOINT pCe = (PUSBSUP_CLEAR_ENDPOINT)pIrp->AssociatedIrp.SystemBuffer;
+ NTSTATUS Status;
+
+ do
+ {
+ if (!pFObj)
+ {
+ AssertFailed();
+ Status = STATUS_INVALID_PARAMETER;
+ break;
+ }
+
+ if (!vboxUsbRtCtxIsOwner(pDevExt, pFObj))
+ {
+ AssertFailed();
+ Status = STATUS_ACCESS_DENIED;
+ break;
+ }
+
+ if ( !pCe
+ || pSl->Parameters.DeviceIoControl.InputBufferLength != sizeof (*pCe)
+ || pSl->Parameters.DeviceIoControl.OutputBufferLength != 0)
+ {
+ AssertMsgFailed((__FUNCTION__": STATUS_INVALID_PARAMETER\n"));
+ Status = STATUS_INVALID_PARAMETER;
+ break;
+ }
+
+ Status = vboxUsbRtClearEndpoint(pDevExt, pCe->bEndpoint, TRUE);
+ } while (0);
+
+ Assert(Status != STATUS_PENDING);
+ VBoxDrvToolIoComplete(pIrp, Status, 0);
+ vboxUsbDdiStateRelease(pDevExt);
+ return Status;
+}
+
+static NTSTATUS vboxUsbRtDispatchUsbAbortEndpoint(PVBOXUSBDEV_EXT pDevExt, PIRP pIrp)
+{
+ PIO_STACK_LOCATION pSl = IoGetCurrentIrpStackLocation(pIrp);
+ PFILE_OBJECT pFObj = pSl->FileObject;
+ PUSBSUP_CLEAR_ENDPOINT pCe = (PUSBSUP_CLEAR_ENDPOINT)pIrp->AssociatedIrp.SystemBuffer;
+ NTSTATUS Status;
+
+ do
+ {
+ if (!pFObj)
+ {
+ AssertFailed();
+ Status = STATUS_INVALID_PARAMETER;
+ break;
+ }
+
+ if (!vboxUsbRtCtxIsOwner(pDevExt, pFObj))
+ {
+ AssertFailed();
+ Status = STATUS_ACCESS_DENIED;
+ break;
+ }
+
+ if ( !pCe
+ || pSl->Parameters.DeviceIoControl.InputBufferLength != sizeof (*pCe)
+ || pSl->Parameters.DeviceIoControl.OutputBufferLength != 0)
+ {
+ AssertMsgFailed((__FUNCTION__": STATUS_INVALID_PARAMETER\n"));
+ Status = STATUS_INVALID_PARAMETER;
+ break;
+ }
+
+ Status = vboxUsbRtClearEndpoint(pDevExt, pCe->bEndpoint, FALSE);
+ } while (0);
+
+ Assert(Status != STATUS_PENDING);
+ VBoxDrvToolIoComplete(pIrp, Status, 0);
+ vboxUsbDdiStateRelease(pDevExt);
+ return Status;
+}
+
+static NTSTATUS vboxUsbRtUrbSendCompletion(PDEVICE_OBJECT pDevObj, IRP *pIrp, void *pvContext)
+{
+ RT_NOREF1(pDevObj);
+
+ if (!pvContext)
+ {
+ AssertMsgFailed((__FUNCTION__": context is NULL\n"));
+ pIrp->IoStatus.Information = 0;
+ return STATUS_CONTINUE_COMPLETION;
+ }
+
+ PVBOXUSB_URB_CONTEXT pContext = (PVBOXUSB_URB_CONTEXT)pvContext;
+
+ if (pContext->ulMagic != VBOXUSB_MAGIC)
+ {
+ AssertMsgFailed((__FUNCTION__": Invalid context magic\n"));
+ pIrp->IoStatus.Information = 0;
+ return STATUS_CONTINUE_COMPLETION;
+ }
+
+ PURB pUrb = pContext->pUrb;
+ PMDL pMdlBuf = pContext->pMdlBuf;
+ PUSBSUP_URB pUrbInfo = (PUSBSUP_URB)pContext->pOut;
+ PVBOXUSBDEV_EXT pDevExt = pContext->pDevExt;
+
+ if (!pUrb || !pMdlBuf || !pUrbInfo || !pDevExt)
+ {
+ AssertMsgFailed((__FUNCTION__": Invalid args\n"));
+ if (pDevExt)
+ vboxUsbDdiStateRelease(pDevExt);
+ pIrp->IoStatus.Information = 0;
+ return STATUS_CONTINUE_COMPLETION;
+ }
+
+ NTSTATUS Status = pIrp->IoStatus.Status;
+ if (Status == STATUS_SUCCESS)
+ {
+ switch(pUrb->UrbHeader.Status)
+ {
+ case USBD_STATUS_CRC:
+ pUrbInfo->error = USBSUP_XFER_CRC;
+ break;
+ case USBD_STATUS_SUCCESS:
+ pUrbInfo->error = USBSUP_XFER_OK;
+ break;
+ case USBD_STATUS_STALL_PID:
+ pUrbInfo->error = USBSUP_XFER_STALL;
+ break;
+ case USBD_STATUS_INVALID_URB_FUNCTION:
+ case USBD_STATUS_INVALID_PARAMETER:
+ AssertMsgFailed((__FUNCTION__": sw error, urb Status (0x%x)\n", pUrb->UrbHeader.Status));
+ case USBD_STATUS_DEV_NOT_RESPONDING:
+ default:
+ pUrbInfo->error = USBSUP_XFER_DNR;
+ break;
+ }
+
+ switch(pContext->ulTransferType)
+ {
+ case USBSUP_TRANSFER_TYPE_MSG:
+ pUrbInfo->len = pUrb->UrbControlTransfer.TransferBufferLength;
+ /* QUSB_TRANSFER_TYPE_MSG is a control transfer, but it is special
+ * the first 8 bytes of the buffer is the setup packet so the real
+ * data length is therefore urb->len - 8
+ */
+ pUrbInfo->len += sizeof (pUrb->UrbControlTransfer.SetupPacket);
+
+ /* If a control URB was successfully completed on the default control
+ * pipe, stash away the handle. When submitting the URB, we don't need
+ * to know (and initially don't have) the handle. If we want to abort
+ * the default control pipe, we *have* to have a handle. This is how we
+ * find out what the handle is.
+ */
+ if (!pUrbInfo->ep && (pDevExt->Rt.hPipe0 == NULL))
+ {
+ pDevExt->Rt.hPipe0 = pUrb->UrbControlTransfer.PipeHandle;
+ }
+
+ break;
+ case USBSUP_TRANSFER_TYPE_ISOC:
+ pUrbInfo->len = pUrb->UrbIsochronousTransfer.TransferBufferLength;
+ break;
+ case USBSUP_TRANSFER_TYPE_BULK:
+ case USBSUP_TRANSFER_TYPE_INTR:
+ if (pUrbInfo->dir == USBSUP_DIRECTION_IN && pUrbInfo->error == USBSUP_XFER_OK
+ && !(pUrbInfo->flags & USBSUP_FLAG_SHORT_OK)
+ && pUrbInfo->len > pUrb->UrbBulkOrInterruptTransfer.TransferBufferLength
+ )
+ {
+ /* If we don't use the USBD_SHORT_TRANSFER_OK flag, the returned buffer lengths are
+ * wrong for short transfers (always a multiple of max packet size?). So we just figure
+ * out if this was a data underrun on our own.
+ */
+ pUrbInfo->error = USBSUP_XFER_UNDERRUN;
+ }
+ pUrbInfo->len = pUrb->UrbBulkOrInterruptTransfer.TransferBufferLength;
+ break;
+ default:
+ break;
+ }
+ }
+ else
+ {
+ pUrbInfo->len = 0;
+
+ LogFunc(("URB failed Status (0x%x) urb Status (0x%x)\n", Status, pUrb->UrbHeader.Status));
+#ifdef DEBUG
+ switch(pContext->ulTransferType)
+ {
+ case USBSUP_TRANSFER_TYPE_MSG:
+ LogRel(("Msg (CTRL) length=%d\n", pUrb->UrbControlTransfer.TransferBufferLength));
+ break;
+ case USBSUP_TRANSFER_TYPE_ISOC:
+ LogRel(("ISOC length=%d\n", pUrb->UrbIsochronousTransfer.TransferBufferLength));
+ break;
+ case USBSUP_TRANSFER_TYPE_BULK:
+ case USBSUP_TRANSFER_TYPE_INTR:
+ LogRel(("BULK/INTR length=%d\n", pUrb->UrbBulkOrInterruptTransfer.TransferBufferLength));
+ break;
+ }
+#endif
+ switch(pUrb->UrbHeader.Status)
+ {
+ case USBD_STATUS_CRC:
+ pUrbInfo->error = USBSUP_XFER_CRC;
+ Status = STATUS_SUCCESS;
+ break;
+ case USBD_STATUS_STALL_PID:
+ pUrbInfo->error = USBSUP_XFER_STALL;
+ Status = STATUS_SUCCESS;
+ break;
+ case USBD_STATUS_DEV_NOT_RESPONDING:
+ case USBD_STATUS_DEVICE_GONE:
+ pUrbInfo->error = USBSUP_XFER_DNR;
+ Status = STATUS_SUCCESS;
+ break;
+ case ((USBD_STATUS)0xC0010000L): // USBD_STATUS_CANCELED - too bad usbdi.h and usb.h aren't consistent!
+ /// @todo What the heck are we really supposed to do here?
+ pUrbInfo->error = USBSUP_XFER_STALL;
+ Status = STATUS_SUCCESS;
+ break;
+ case USBD_STATUS_BAD_START_FRAME: // This one really shouldn't happen
+ case USBD_STATUS_ISOCH_REQUEST_FAILED:
+ pUrbInfo->error = USBSUP_XFER_NAC;
+ Status = STATUS_SUCCESS;
+ break;
+ default:
+ AssertMsgFailed((__FUNCTION__": err Status (0x%x) (0x%x)\n", Status, pUrb->UrbHeader.Status));
+ pUrbInfo->error = USBSUP_XFER_DNR;
+ Status = STATUS_SUCCESS;
+ break;
+ }
+ }
+ // For isochronous transfers, always update the individual packets
+ if (pContext->ulTransferType == USBSUP_TRANSFER_TYPE_ISOC)
+ {
+ Assert(pUrbInfo->numIsoPkts == pUrb->UrbIsochronousTransfer.NumberOfPackets);
+ for (ULONG i = 0; i < pUrbInfo->numIsoPkts; ++i)
+ {
+ Assert(pUrbInfo->aIsoPkts[i].off == pUrb->UrbIsochronousTransfer.IsoPacket[i].Offset);
+ pUrbInfo->aIsoPkts[i].cb = (uint16_t)pUrb->UrbIsochronousTransfer.IsoPacket[i].Length;
+ switch (pUrb->UrbIsochronousTransfer.IsoPacket[i].Status)
+ {
+ case USBD_STATUS_SUCCESS:
+ pUrbInfo->aIsoPkts[i].stat = USBSUP_XFER_OK;
+ break;
+ case USBD_STATUS_NOT_ACCESSED:
+ pUrbInfo->aIsoPkts[i].stat = USBSUP_XFER_NAC;
+ break;
+ default:
+ pUrbInfo->aIsoPkts[i].stat = USBSUP_XFER_STALL;
+ break;
+ }
+ }
+ }
+
+ MmUnlockPages(pMdlBuf);
+ IoFreeMdl(pMdlBuf);
+
+ vboxUsbMemFree(pContext);
+
+ vboxUsbDdiStateRelease(pDevExt);
+
+ Assert(pIrp->IoStatus.Status != STATUS_IO_TIMEOUT);
+ pIrp->IoStatus.Information = sizeof(*pUrbInfo);
+ pIrp->IoStatus.Status = Status;
+ return STATUS_CONTINUE_COMPLETION;
+}
+
+static NTSTATUS vboxUsbRtUrbSend(PVBOXUSBDEV_EXT pDevExt, PIRP pIrp, PUSBSUP_URB pUrbInfo)
+{
+ NTSTATUS Status = STATUS_SUCCESS;
+ PVBOXUSB_URB_CONTEXT pContext = NULL;
+ PMDL pMdlBuf = NULL;
+ ULONG cbUrb;
+
+ Assert(pUrbInfo);
+ if (pUrbInfo->type == USBSUP_TRANSFER_TYPE_ISOC)
+ {
+ Assert(pUrbInfo->numIsoPkts <= 8);
+ cbUrb = GET_ISO_URB_SIZE(pUrbInfo->numIsoPkts);
+ }
+ else
+ cbUrb = sizeof (URB);
+
+ do
+ {
+ pContext = (PVBOXUSB_URB_CONTEXT)vboxUsbMemAllocZ(cbUrb + sizeof (VBOXUSB_URB_CONTEXT));
+ if (!pContext)
+ {
+ AssertMsgFailed((__FUNCTION__": vboxUsbMemAlloc failed\n"));
+ Status = STATUS_INSUFFICIENT_RESOURCES;
+ break;
+ }
+
+ PURB pUrb = (PURB)(pContext + 1);
+ HANDLE hPipe = NULL;
+ if (pUrbInfo->ep)
+ {
+ hPipe = vboxUsbRtGetPipeHandle(pDevExt, pUrbInfo->ep | ((pUrbInfo->dir == USBSUP_DIRECTION_IN) ? 0x80 : 0x00));
+ if (!hPipe)
+ {
+ AssertMsgFailed((__FUNCTION__": vboxUsbRtGetPipeHandle failed for endpoint (0x%x)\n", pUrbInfo->ep));
+ Status = STATUS_INVALID_PARAMETER;
+ break;
+ }
+ }
+
+ pMdlBuf = IoAllocateMdl(pUrbInfo->buf, (ULONG)pUrbInfo->len, FALSE, FALSE, NULL);
+ if (!pMdlBuf)
+ {
+ AssertMsgFailed((__FUNCTION__": IoAllocateMdl failed for buffer (0x%p) length (%d)\n", pUrbInfo->buf, pUrbInfo->len));
+ Status = STATUS_INSUFFICIENT_RESOURCES;
+ break;
+ }
+
+ __try
+ {
+ MmProbeAndLockPages(pMdlBuf, KernelMode, IoModifyAccess);
+ }
+ __except(EXCEPTION_EXECUTE_HANDLER)
+ {
+ Status = GetExceptionCode();
+ IoFreeMdl(pMdlBuf);
+ pMdlBuf = NULL;
+ AssertMsgFailed((__FUNCTION__": Exception Code (0x%x)\n", Status));
+ break;
+ }
+
+ /* For some reason, passing a MDL in the URB does not work reliably. Notably
+ * the iPhone when used with iTunes fails.
+ */
+ PVOID pBuffer = MmGetSystemAddressForMdlSafe(pMdlBuf, NormalPagePriority);
+ if (!pBuffer)
+ {
+ AssertMsgFailed((__FUNCTION__": MmGetSystemAddressForMdlSafe failed\n"));
+ Status = STATUS_INSUFFICIENT_RESOURCES;
+ break;
+ }
+
+ switch (pUrbInfo->type)
+ {
+ case USBSUP_TRANSFER_TYPE_MSG:
+ {
+ pUrb->UrbHeader.Function = URB_FUNCTION_CONTROL_TRANSFER;
+ pUrb->UrbHeader.Length = sizeof (struct _URB_CONTROL_TRANSFER);
+ pUrb->UrbControlTransfer.PipeHandle = hPipe;
+ pUrb->UrbControlTransfer.TransferBufferLength = (ULONG)pUrbInfo->len;
+ pUrb->UrbControlTransfer.TransferFlags = ((pUrbInfo->dir == USBSUP_DIRECTION_IN) ? USBD_TRANSFER_DIRECTION_IN : USBD_TRANSFER_DIRECTION_OUT);
+ pUrb->UrbControlTransfer.UrbLink = 0;
+
+ if (!hPipe)
+ pUrb->UrbControlTransfer.TransferFlags |= USBD_DEFAULT_PIPE_TRANSFER;
+
+ /* QUSB_TRANSFER_TYPE_MSG is a control transfer, but it is special
+ * the first 8 bytes of the buffer is the setup packet so the real
+ * data length is therefore pUrb->len - 8
+ */
+ //PVBOXUSB_SETUP pSetup = (PVBOXUSB_SETUP)pUrb->UrbControlTransfer.SetupPacket;
+ memcpy(pUrb->UrbControlTransfer.SetupPacket, pBuffer, min(sizeof (pUrb->UrbControlTransfer.SetupPacket), pUrbInfo->len));
+
+ if (pUrb->UrbControlTransfer.TransferBufferLength <= sizeof (pUrb->UrbControlTransfer.SetupPacket))
+ pUrb->UrbControlTransfer.TransferBufferLength = 0;
+ else
+ pUrb->UrbControlTransfer.TransferBufferLength -= sizeof (pUrb->UrbControlTransfer.SetupPacket);
+
+ pUrb->UrbControlTransfer.TransferBuffer = (uint8_t *)pBuffer + sizeof(pUrb->UrbControlTransfer.SetupPacket);
+ pUrb->UrbControlTransfer.TransferBufferMDL = 0;
+ pUrb->UrbControlTransfer.TransferFlags |= USBD_SHORT_TRANSFER_OK;
+ break;
+ }
+ case USBSUP_TRANSFER_TYPE_ISOC:
+ {
+ Assert(hPipe);
+ VBOXUSB_PIPE_INFO *pPipeInfo = vboxUsbRtGetPipeInfo(pDevExt, pUrbInfo->ep | ((pUrbInfo->dir == USBSUP_DIRECTION_IN) ? 0x80 : 0x00));
+ if (pPipeInfo == NULL)
+ {
+ /* Can happen if the isoc request comes in too early or late. */
+ AssertMsgFailed((__FUNCTION__": pPipeInfo not found\n"));
+ Status = STATUS_INVALID_PARAMETER;
+ break;
+ }
+
+ pUrb->UrbHeader.Function = URB_FUNCTION_ISOCH_TRANSFER;
+ pUrb->UrbHeader.Length = (USHORT)cbUrb;
+ pUrb->UrbIsochronousTransfer.PipeHandle = hPipe;
+ pUrb->UrbIsochronousTransfer.TransferBufferLength = (ULONG)pUrbInfo->len;
+ pUrb->UrbIsochronousTransfer.TransferBufferMDL = 0;
+ pUrb->UrbIsochronousTransfer.TransferBuffer = pBuffer;
+ pUrb->UrbIsochronousTransfer.TransferFlags = ((pUrbInfo->dir == USBSUP_DIRECTION_IN) ? USBD_TRANSFER_DIRECTION_IN : USBD_TRANSFER_DIRECTION_OUT);
+ pUrb->UrbIsochronousTransfer.TransferFlags |= USBD_SHORT_TRANSFER_OK; // May be implied already
+ pUrb->UrbIsochronousTransfer.NumberOfPackets = pUrbInfo->numIsoPkts;
+ pUrb->UrbIsochronousTransfer.ErrorCount = 0;
+ pUrb->UrbIsochronousTransfer.UrbLink = 0;
+
+ Assert(pUrbInfo->numIsoPkts == pUrb->UrbIsochronousTransfer.NumberOfPackets);
+ for (ULONG i = 0; i < pUrbInfo->numIsoPkts; ++i)
+ {
+ pUrb->UrbIsochronousTransfer.IsoPacket[i].Offset = pUrbInfo->aIsoPkts[i].off;
+ pUrb->UrbIsochronousTransfer.IsoPacket[i].Length = pUrbInfo->aIsoPkts[i].cb;
+ }
+
+ /* We have to schedule the URBs ourselves. There is an ASAP flag but
+ * that can only be reliably used after pipe creation/reset, ie. it's
+ * almost completely useless.
+ */
+ ULONG iFrame, iStartFrame;
+ VBoxUsbToolCurrentFrame(pDevExt->pLowerDO, pIrp, &iFrame);
+ iFrame += 2;
+ iStartFrame = pPipeInfo->NextScheduledFrame;
+ if ((iFrame < iStartFrame) || (iStartFrame > iFrame + 512))
+ iFrame = iStartFrame;
+ /* For full-speed devices, there must be one transfer per frame (Windows USB
+ * stack requirement), but URBs can contain multiple packets. For high-speed or
+ * faster transfers, we expect one URB per frame, regardless of the interval.
+ */
+ if (pDevExt->Rt.devdescr->bcdUSB < 0x300 && !pDevExt->Rt.fIsHighSpeed)
+ pPipeInfo->NextScheduledFrame = iFrame + pUrbInfo->numIsoPkts;
+ else
+ pPipeInfo->NextScheduledFrame = iFrame + 1;
+ pUrb->UrbIsochronousTransfer.StartFrame = iFrame;
+ break;
+ }
+ case USBSUP_TRANSFER_TYPE_BULK:
+ case USBSUP_TRANSFER_TYPE_INTR:
+ {
+ Assert(pUrbInfo->dir != USBSUP_DIRECTION_SETUP);
+ Assert(pUrbInfo->dir == USBSUP_DIRECTION_IN || pUrbInfo->type == USBSUP_TRANSFER_TYPE_BULK);
+ Assert(hPipe);
+
+ pUrb->UrbHeader.Function = URB_FUNCTION_BULK_OR_INTERRUPT_TRANSFER;
+ pUrb->UrbHeader.Length = sizeof (struct _URB_BULK_OR_INTERRUPT_TRANSFER);
+ pUrb->UrbBulkOrInterruptTransfer.PipeHandle = hPipe;
+ pUrb->UrbBulkOrInterruptTransfer.TransferBufferLength = (ULONG)pUrbInfo->len;
+ pUrb->UrbBulkOrInterruptTransfer.TransferBufferMDL = 0;
+ pUrb->UrbBulkOrInterruptTransfer.TransferBuffer = pBuffer;
+ pUrb->UrbBulkOrInterruptTransfer.TransferFlags = ((pUrbInfo->dir == USBSUP_DIRECTION_IN) ? USBD_TRANSFER_DIRECTION_IN : USBD_TRANSFER_DIRECTION_OUT);
+
+ if (pUrb->UrbBulkOrInterruptTransfer.TransferFlags & USBD_TRANSFER_DIRECTION_IN)
+ pUrb->UrbBulkOrInterruptTransfer.TransferFlags |= (USBD_SHORT_TRANSFER_OK);
+
+ pUrb->UrbBulkOrInterruptTransfer.UrbLink = 0;
+ break;
+ }
+ default:
+ {
+ AssertFailed();
+ Status = STATUS_INVALID_PARAMETER;
+ break;
+ }
+ }
+
+ if (!NT_SUCCESS(Status))
+ {
+ break;
+ }
+
+ pContext->pDevExt = pDevExt;
+ pContext->pMdlBuf = pMdlBuf;
+ pContext->pUrb = pUrb;
+ pContext->pOut = pUrbInfo;
+ pContext->ulTransferType = pUrbInfo->type;
+ pContext->ulMagic = VBOXUSB_MAGIC;
+
+ PIO_STACK_LOCATION pSl = IoGetNextIrpStackLocation(pIrp);
+ pSl->MajorFunction = IRP_MJ_INTERNAL_DEVICE_CONTROL;
+ pSl->Parameters.DeviceIoControl.IoControlCode = IOCTL_INTERNAL_USB_SUBMIT_URB;
+ pSl->Parameters.Others.Argument1 = pUrb;
+ pSl->Parameters.Others.Argument2 = NULL;
+
+ IoSetCompletionRoutine(pIrp, vboxUsbRtUrbSendCompletion, pContext, TRUE, TRUE, TRUE);
+ IoMarkIrpPending(pIrp);
+ Status = IoCallDriver(pDevExt->pLowerDO, pIrp);
+ AssertMsg(NT_SUCCESS(Status), (__FUNCTION__": IoCallDriver failed Status (0x%x)\n", Status));
+ return STATUS_PENDING;
+ } while (0);
+
+ Assert(!NT_SUCCESS(Status));
+
+ if (pMdlBuf)
+ {
+ if (pMdlBuf->MdlFlags & MDL_PAGES_LOCKED)
+ MmUnlockPages(pMdlBuf);
+
+ IoFreeMdl(pMdlBuf);
+ }
+
+ if (pContext)
+ vboxUsbMemFree(pContext);
+
+ VBoxDrvToolIoComplete(pIrp, Status, 0);
+ vboxUsbDdiStateRelease(pDevExt);
+ return Status;
+}
+
+static NTSTATUS vboxUsbRtDispatchSendUrb(PVBOXUSBDEV_EXT pDevExt, PIRP pIrp)
+{
+ PIO_STACK_LOCATION pSl = IoGetCurrentIrpStackLocation(pIrp);
+ PFILE_OBJECT pFObj = pSl->FileObject;
+ PUSBSUP_URB pUrbInfo = (PUSBSUP_URB)pIrp->AssociatedIrp.SystemBuffer;
+ NTSTATUS Status;
+
+ do
+ {
+ if (!pFObj)
+ {
+ AssertFailed();
+ Status = STATUS_INVALID_PARAMETER;
+ break;
+ }
+
+ if (!vboxUsbRtCtxIsOwner(pDevExt, pFObj))
+ {
+ AssertFailed();
+ Status = STATUS_ACCESS_DENIED;
+ break;
+ }
+
+ if ( !pUrbInfo
+ || pSl->Parameters.DeviceIoControl.InputBufferLength != sizeof (*pUrbInfo)
+ || pSl->Parameters.DeviceIoControl.OutputBufferLength != sizeof (*pUrbInfo))
+ {
+ AssertMsgFailed((__FUNCTION__": STATUS_INVALID_PARAMETER\n"));
+ Status = STATUS_INVALID_PARAMETER;
+ break;
+ }
+ return vboxUsbRtUrbSend(pDevExt, pIrp, pUrbInfo);
+ } while (0);
+
+ Assert(Status != STATUS_PENDING);
+ VBoxDrvToolIoComplete(pIrp, Status, 0);
+ vboxUsbDdiStateRelease(pDevExt);
+ return Status;
+}
+
+static NTSTATUS vboxUsbRtDispatchIsOperational(PVBOXUSBDEV_EXT pDevExt, PIRP pIrp)
+{
+ VBoxDrvToolIoComplete(pIrp, STATUS_SUCCESS, 0);
+ vboxUsbDdiStateRelease(pDevExt);
+ return STATUS_SUCCESS;
+}
+
+static NTSTATUS vboxUsbRtDispatchGetVersion(PVBOXUSBDEV_EXT pDevExt, PIRP pIrp)
+{
+ PIO_STACK_LOCATION pSl = IoGetCurrentIrpStackLocation(pIrp);
+ PUSBSUP_VERSION pVer= (PUSBSUP_VERSION)pIrp->AssociatedIrp.SystemBuffer;
+ NTSTATUS Status = STATUS_SUCCESS;
+
+ if ( pVer
+ && pSl->Parameters.DeviceIoControl.InputBufferLength == 0
+ && pSl->Parameters.DeviceIoControl.OutputBufferLength == sizeof(*pVer))
+ {
+ pVer->u32Major = USBDRV_MAJOR_VERSION;
+ pVer->u32Minor = USBDRV_MINOR_VERSION;
+ }
+ else
+ {
+ AssertMsgFailed((__FUNCTION__": STATUS_INVALID_PARAMETER\n"));
+ Status = STATUS_INVALID_PARAMETER;
+ }
+
+ Assert(Status != STATUS_PENDING);
+ VBoxDrvToolIoComplete(pIrp, Status, sizeof (*pVer));
+ vboxUsbDdiStateRelease(pDevExt);
+ return Status;
+}
+
+static NTSTATUS vboxUsbRtDispatchDefault(PVBOXUSBDEV_EXT pDevExt, PIRP pIrp)
+{
+ VBoxDrvToolIoComplete(pIrp, STATUS_INVALID_DEVICE_REQUEST, 0);
+ vboxUsbDdiStateRelease(pDevExt);
+ return STATUS_INVALID_DEVICE_REQUEST;
+}
+
+DECLHIDDEN(NTSTATUS) vboxUsbRtCreate(PVBOXUSBDEV_EXT pDevExt, PIRP pIrp)
+{
+ RT_NOREF1(pDevExt);
+ PIO_STACK_LOCATION pSl = IoGetCurrentIrpStackLocation(pIrp);
+ PFILE_OBJECT pFObj = pSl->FileObject;
+ AssertReturn(pFObj, STATUS_INVALID_PARAMETER);
+ return STATUS_SUCCESS;
+}
+
+DECLHIDDEN(NTSTATUS) vboxUsbRtClose(PVBOXUSBDEV_EXT pDevExt, PIRP pIrp)
+{
+ PIO_STACK_LOCATION pSl = IoGetCurrentIrpStackLocation(pIrp);
+ PFILE_OBJECT pFObj = pSl->FileObject;
+ Assert(pFObj);
+
+ vboxUsbRtCtxReleaseOwner(pDevExt, pFObj);
+
+ return STATUS_SUCCESS;
+}
+
+DECLHIDDEN(NTSTATUS) vboxUsbRtDispatch(PVBOXUSBDEV_EXT pDevExt, PIRP pIrp)
+{
+ PIO_STACK_LOCATION pSl = IoGetCurrentIrpStackLocation(pIrp);
+ switch (pSl->Parameters.DeviceIoControl.IoControlCode)
+ {
+ case SUPUSB_IOCTL_USB_CLAIM_DEVICE:
+ return vboxUsbRtDispatchClaimDevice(pDevExt, pIrp);
+
+ case SUPUSB_IOCTL_USB_RELEASE_DEVICE:
+ return vboxUsbRtDispatchReleaseDevice(pDevExt, pIrp);
+
+ case SUPUSB_IOCTL_GET_DEVICE:
+ return vboxUsbRtDispatchGetDevice(pDevExt, pIrp);
+
+ case SUPUSB_IOCTL_USB_RESET:
+ return vboxUsbRtDispatchUsbReset(pDevExt, pIrp);
+
+ case SUPUSB_IOCTL_USB_SET_CONFIG:
+ return vboxUsbRtDispatchUsbSetConfig(pDevExt, pIrp);
+
+ case SUPUSB_IOCTL_USB_SELECT_INTERFACE:
+ return vboxUsbRtDispatchUsbSelectInterface(pDevExt, pIrp);
+
+ case SUPUSB_IOCTL_USB_CLEAR_ENDPOINT:
+ return vboxUsbRtDispatchUsbClearEndpoint(pDevExt, pIrp);
+
+ case SUPUSB_IOCTL_USB_ABORT_ENDPOINT:
+ return vboxUsbRtDispatchUsbAbortEndpoint(pDevExt, pIrp);
+
+ case SUPUSB_IOCTL_SEND_URB:
+ return vboxUsbRtDispatchSendUrb(pDevExt, pIrp);
+
+ case SUPUSB_IOCTL_IS_OPERATIONAL:
+ return vboxUsbRtDispatchIsOperational(pDevExt, pIrp);
+
+ case SUPUSB_IOCTL_GET_VERSION:
+ return vboxUsbRtDispatchGetVersion(pDevExt, pIrp);
+
+ default:
+ return vboxUsbRtDispatchDefault(pDevExt, pIrp);
+ }
+}
diff --git a/src/VBox/HostDrivers/VBoxUSB/win/dev/VBoxUsbRt.h b/src/VBox/HostDrivers/VBoxUSB/win/dev/VBoxUsbRt.h
new file mode 100644
index 00000000..c835890d
--- /dev/null
+++ b/src/VBox/HostDrivers/VBoxUSB/win/dev/VBoxUsbRt.h
@@ -0,0 +1,97 @@
+/* $Id: VBoxUsbRt.h $ */
+/** @file
+ * VBox USB R0 runtime
+ */
+/*
+ * Copyright (C) 2011-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+#ifndef VBOX_INCLUDED_SRC_VBoxUSB_win_dev_VBoxUsbRt_h
+#define VBOX_INCLUDED_SRC_VBoxUSB_win_dev_VBoxUsbRt_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+#include "VBoxUsbCmn.h"
+#include "../cmn/VBoxUsbIdc.h"
+
+#define VBOXUSBRT_MAX_CFGS 4
+
+typedef struct VBOXUSB_PIPE_INFO {
+ UCHAR EndpointAddress;
+ ULONG NextScheduledFrame;
+} VBOXUSB_PIPE_INFO;
+
+typedef struct VBOXUSB_IFACE_INFO {
+ USBD_INTERFACE_INFORMATION *pInterfaceInfo;
+ VBOXUSB_PIPE_INFO *pPipeInfo;
+} VBOXUSB_IFACE_INFO;
+
+typedef struct VBOXUSB_RT
+{
+ UNICODE_STRING IfName;
+
+ HANDLE hPipe0;
+ HANDLE hConfiguration;
+ uint32_t uConfigValue;
+
+ uint32_t uNumInterfaces;
+ USB_DEVICE_DESCRIPTOR *devdescr;
+ USB_CONFIGURATION_DESCRIPTOR *cfgdescr[VBOXUSBRT_MAX_CFGS];
+
+ VBOXUSB_IFACE_INFO *pVBIfaceInfo;
+
+ uint16_t idVendor, idProduct, bcdDevice;
+ char szSerial[MAX_USB_SERIAL_STRING];
+ BOOLEAN fIsHighSpeed;
+
+ HVBOXUSBIDCDEV hMonDev;
+ PFILE_OBJECT pOwner;
+} VBOXUSB_RT, *PVBOXUSB_RT;
+
+typedef struct VBOXUSBRT_IDC
+{
+ PDEVICE_OBJECT pDevice;
+ PFILE_OBJECT pFile;
+} VBOXUSBRT_IDC, *PVBOXUSBRT_IDC;
+
+DECLHIDDEN(NTSTATUS) vboxUsbRtGlobalsInit();
+DECLHIDDEN(VOID) vboxUsbRtGlobalsTerm();
+
+DECLHIDDEN(NTSTATUS) vboxUsbRtInit(PVBOXUSBDEV_EXT pDevExt);
+DECLHIDDEN(VOID) vboxUsbRtClear(PVBOXUSBDEV_EXT pDevExt);
+DECLHIDDEN(NTSTATUS) vboxUsbRtRm(PVBOXUSBDEV_EXT pDevExt);
+DECLHIDDEN(NTSTATUS) vboxUsbRtStart(PVBOXUSBDEV_EXT pDevExt);
+
+DECLHIDDEN(NTSTATUS) vboxUsbRtDispatch(PVBOXUSBDEV_EXT pDevExt, PIRP pIrp);
+DECLHIDDEN(NTSTATUS) vboxUsbRtCreate(PVBOXUSBDEV_EXT pDevExt, PIRP pIrp);
+DECLHIDDEN(NTSTATUS) vboxUsbRtClose(PVBOXUSBDEV_EXT pDevExt, PIRP pIrp);
+
+#endif /* !VBOX_INCLUDED_SRC_VBoxUSB_win_dev_VBoxUsbRt_h */
diff --git a/src/VBox/HostDrivers/VBoxUSB/win/lib/Makefile.kup b/src/VBox/HostDrivers/VBoxUSB/win/lib/Makefile.kup
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/src/VBox/HostDrivers/VBoxUSB/win/lib/Makefile.kup
diff --git a/src/VBox/HostDrivers/VBoxUSB/win/lib/VBoxUsbLib-win.cpp b/src/VBox/HostDrivers/VBoxUSB/win/lib/VBoxUsbLib-win.cpp
new file mode 100644
index 00000000..817b26ed
--- /dev/null
+++ b/src/VBox/HostDrivers/VBoxUSB/win/lib/VBoxUsbLib-win.cpp
@@ -0,0 +1,2221 @@
+/* $Id: VBoxUsbLib-win.cpp $ */
+/** @file
+ * VBox USB ring-3 Driver Interface library, Windows.
+ */
+
+/*
+ * Copyright (C) 2011-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#define LOG_GROUP LOG_GROUP_DRV_USBPROXY
+#include <iprt/win/windows.h>
+
+#include <VBox/sup.h>
+#include <VBox/types.h>
+#include <VBox/err.h>
+#include <VBox/param.h>
+#include <iprt/path.h>
+#include <iprt/assert.h>
+#include <iprt/alloc.h>
+#include <iprt/string.h>
+#include <iprt/thread.h>
+#include <iprt/utf16.h>
+#include <VBox/log.h>
+#include <VBox/usblib.h>
+#include <VBox/usblib-win.h>
+#include <VBox/usb.h>
+#include <VBox/VBoxDrvCfg-win.h>
+#pragma warning (disable:4200) /* shuts up the empty array member warnings */
+#include <iprt/win/setupapi.h>
+#include <usbdi.h>
+#include <hidsdi.h>
+#include <Dbt.h>
+
+/* Defined in Windows 8 DDK (through usbdi.h) but we use Windows 7 DDK to build. */
+#define UsbSuperSpeed 3
+
+#ifdef VBOX_WITH_NEW_USB_ENUM
+# include <cfgmgr32.h>
+#endif
+
+
+/*********************************************************************************************************************************
+* Structures and Typedefs *
+*********************************************************************************************************************************/
+typedef struct _USB_INTERFACE_DESCRIPTOR2
+{
+ UCHAR bLength;
+ UCHAR bDescriptorType;
+ UCHAR bInterfaceNumber;
+ UCHAR bAlternateSetting;
+ UCHAR bNumEndpoints;
+ UCHAR bInterfaceClass;
+ UCHAR bInterfaceSubClass;
+ UCHAR bInterfaceProtocol;
+ UCHAR iInterface;
+ USHORT wNumClasses;
+} USB_INTERFACE_DESCRIPTOR2, *PUSB_INTERFACE_DESCRIPTOR2;
+
+typedef struct VBOXUSBGLOBALSTATE
+{
+ HANDLE hMonitor;
+ HANDLE hNotifyEvent;
+ HANDLE hInterruptEvent;
+ HANDLE hThread;
+ HWND hWnd;
+ HANDLE hTimerQueue;
+ HANDLE hTimer;
+} VBOXUSBGLOBALSTATE, *PVBOXUSBGLOBALSTATE;
+
+typedef struct VBOXUSB_STRING_DR_ENTRY
+{
+ struct VBOXUSB_STRING_DR_ENTRY *pNext;
+ UCHAR iDr;
+ USHORT idLang;
+ USB_STRING_DESCRIPTOR StrDr;
+} VBOXUSB_STRING_DR_ENTRY, *PVBOXUSB_STRING_DR_ENTRY;
+
+/**
+ * This represents VBoxUsb device instance
+ */
+typedef struct VBOXUSB_DEV
+{
+ struct VBOXUSB_DEV *pNext;
+ char szName[512];
+ char szDriverRegName[512];
+} VBOXUSB_DEV, *PVBOXUSB_DEV;
+
+
+/*********************************************************************************************************************************
+* Global Variables *
+*********************************************************************************************************************************/
+static VBOXUSBGLOBALSTATE g_VBoxUsbGlobal;
+
+
+static void usbLibVuFreeDevices(PVBOXUSB_DEV pDevInfos)
+{
+ while (pDevInfos)
+ {
+ PVBOXUSB_DEV pNext = pDevInfos->pNext;
+ RTMemFree(pDevInfos);
+ pDevInfos = pNext;
+ }
+}
+
+/* Check that a proxied device responds the way we expect it to. */
+static int usbLibVuDeviceValidate(PVBOXUSB_DEV pVuDev)
+{
+ HANDLE hOut = INVALID_HANDLE_VALUE;
+ DWORD dwErr;
+
+ hOut = CreateFile(pVuDev->szName, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_WRITE | FILE_SHARE_READ, NULL,
+ OPEN_EXISTING, FILE_ATTRIBUTE_SYSTEM, NULL);
+
+ if (hOut == INVALID_HANDLE_VALUE)
+ {
+ dwErr = GetLastError();
+ AssertFailed();
+ LogRelFunc(("Failed to open `%s' (dwErr=%u)!\n", pVuDev->szName, dwErr));
+ return VERR_GENERAL_FAILURE;
+ }
+
+ USBSUP_VERSION version = {0};
+ DWORD cbReturned = 0;
+ int rc = VERR_VERSION_MISMATCH;
+
+ do
+ {
+ if (!DeviceIoControl(hOut, SUPUSB_IOCTL_GET_VERSION, NULL, 0,&version, sizeof(version), &cbReturned, NULL))
+ {
+ dwErr = GetLastError();
+ AssertFailed();
+ LogRelFunc(("SUPUSB_IOCTL_GET_VERSION failed on `%s' (dwErr=%u)!\n", pVuDev->szName, dwErr));
+ break;
+ }
+
+ if ( version.u32Major != USBDRV_MAJOR_VERSION
+#if USBDRV_MINOR_VERSION != 0
+ || version.u32Minor < USBDRV_MINOR_VERSION
+#endif
+ )
+ {
+ AssertFailed();
+ LogRelFunc(("Invalid version %d:%d (%s) vs %d:%d (library)!\n", version.u32Major, version.u32Minor, pVuDev->szName, USBDRV_MAJOR_VERSION, USBDRV_MINOR_VERSION));
+ break;
+ }
+
+ if (!DeviceIoControl(hOut, SUPUSB_IOCTL_IS_OPERATIONAL, NULL, 0, NULL, NULL, &cbReturned, NULL))
+ {
+ dwErr = GetLastError();
+ AssertFailed();
+ LogRelFunc(("SUPUSB_IOCTL_IS_OPERATIONAL failed on `%s' (dwErr=%u)!\n", pVuDev->szName, dwErr));
+ break;
+ }
+
+ rc = VINF_SUCCESS;
+ } while (0);
+
+ CloseHandle(hOut);
+ return rc;
+}
+
+#ifndef VBOX_WITH_NEW_USB_ENUM
+static int usbLibVuDevicePopulate(PVBOXUSB_DEV pVuDev, HDEVINFO hDevInfo, PSP_DEVICE_INTERFACE_DATA pIfData)
+{
+ DWORD cbIfDetailData;
+ int rc = VINF_SUCCESS;
+
+ SetupDiGetDeviceInterfaceDetail(hDevInfo, pIfData,
+ NULL, /* OUT PSP_DEVICE_INTERFACE_DETAIL_DATA DeviceInterfaceDetailData */
+ 0, /* IN DWORD DeviceInterfaceDetailDataSize */
+ &cbIfDetailData,
+ NULL
+ );
+ Assert(GetLastError() == ERROR_INSUFFICIENT_BUFFER);
+
+ PSP_DEVICE_INTERFACE_DETAIL_DATA pIfDetailData = (PSP_DEVICE_INTERFACE_DETAIL_DATA)RTMemAllocZ(cbIfDetailData);
+ if (!pIfDetailData)
+ {
+ AssertMsgFailed(("RTMemAllocZ failed\n"));
+ return VERR_OUT_OF_RESOURCES;
+ }
+
+ DWORD cbDbgRequired;
+ SP_DEVINFO_DATA DevInfoData;
+ DevInfoData.cbSize = sizeof (DevInfoData);
+ /* the cbSize should contain the sizeof a fixed-size part according to the docs */
+ pIfDetailData->cbSize = sizeof (*pIfDetailData);
+ do
+ {
+ if (!SetupDiGetDeviceInterfaceDetail(hDevInfo, pIfData,
+ pIfDetailData,
+ cbIfDetailData,
+ &cbDbgRequired,
+ &DevInfoData))
+ {
+ DWORD dwErr = GetLastError(); NOREF(dwErr);
+ AssertMsgFailed(("SetupDiGetDeviceInterfaceDetail, cbRequired (%d), was (%d), dwErr (%d)\n", cbDbgRequired, cbIfDetailData, dwErr));
+ rc = VERR_GENERAL_FAILURE;
+ break;
+ }
+
+ strncpy(pVuDev->szName, pIfDetailData->DevicePath, sizeof (pVuDev->szName));
+
+ if (!SetupDiGetDeviceRegistryPropertyA(hDevInfo, &DevInfoData, SPDRP_DRIVER,
+ NULL, /* OUT PDWORD PropertyRegDataType */
+ (PBYTE)pVuDev->szDriverRegName,
+ sizeof (pVuDev->szDriverRegName),
+ &cbDbgRequired))
+ {
+ DWORD dwErr = GetLastError(); NOREF(dwErr);
+ AssertMsgFailed(("SetupDiGetDeviceRegistryPropertyA, cbRequired (%d), was (%d), dwErr (%d)\n", cbDbgRequired, sizeof (pVuDev->szDriverRegName), dwErr));
+ rc = VERR_GENERAL_FAILURE;
+ break;
+ }
+
+ rc = usbLibVuDeviceValidate(pVuDev);
+ LogRelFunc(("Found VBoxUSB on `%s' (rc=%d)\n", pVuDev->szName, rc));
+ AssertRC(rc);
+ } while (0);
+
+ RTMemFree(pIfDetailData);
+ return rc;
+}
+
+static int usbLibVuGetDevices(PVBOXUSB_DEV *ppVuDevs, uint32_t *pcVuDevs)
+{
+ *ppVuDevs = NULL;
+ *pcVuDevs = 0;
+
+ HDEVINFO hDevInfo = SetupDiGetClassDevs(&GUID_CLASS_VBOXUSB,
+ NULL, /* IN PCTSTR Enumerator */
+ NULL, /* IN HWND hwndParent */
+ (DIGCF_PRESENT | DIGCF_DEVICEINTERFACE) /* IN DWORD Flags */
+ );
+ if (hDevInfo == INVALID_HANDLE_VALUE)
+ {
+ DWORD dwErr = GetLastError(); NOREF(dwErr);
+ AssertMsgFailed(("SetupDiGetClassDevs, dwErr (%u)\n", dwErr));
+ return VERR_GENERAL_FAILURE;
+ }
+
+ for (int i = 0; ; ++i)
+ {
+ SP_DEVICE_INTERFACE_DATA IfData;
+ IfData.cbSize = sizeof (IfData);
+ if (!SetupDiEnumDeviceInterfaces(hDevInfo,
+ NULL, /* IN PSP_DEVINFO_DATA DeviceInfoData */
+ &GUID_CLASS_VBOXUSB, /* IN LPGUID InterfaceClassGuid */
+ i,
+ &IfData))
+ {
+ DWORD dwErr = GetLastError();
+ if (dwErr == ERROR_NO_MORE_ITEMS)
+ break;
+
+ AssertMsgFailed(("SetupDiEnumDeviceInterfaces, dwErr (%u), resuming\n", dwErr));
+ continue;
+ }
+
+ /* we've now got the IfData */
+ PVBOXUSB_DEV pVuDev = (PVBOXUSB_DEV)RTMemAllocZ(sizeof (*pVuDev));
+ if (!pVuDev)
+ {
+ AssertMsgFailed(("RTMemAllocZ failed, resuming\n"));
+ continue;
+ }
+
+ int rc = usbLibVuDevicePopulate(pVuDev, hDevInfo, &IfData);
+ if (!RT_SUCCESS(rc))
+ {
+ AssertMsgFailed(("usbLibVuDevicePopulate failed, rc (%d), resuming\n", rc));
+ continue;
+ }
+
+ pVuDev->pNext = *ppVuDevs;
+ *ppVuDevs = pVuDev;
+ ++*pcVuDevs;
+ }
+
+ SetupDiDestroyDeviceInfoList(hDevInfo);
+
+ return VINF_SUCCESS;
+}
+
+static int usbLibDevPopulate(PUSBDEVICE pDev, PUSB_NODE_CONNECTION_INFORMATION_EX pConInfo, ULONG iPort, LPCSTR lpszDrvKeyName, LPCSTR lpszHubName, PVBOXUSB_STRING_DR_ENTRY pDrList)
+{
+ pDev->bcdUSB = pConInfo->DeviceDescriptor.bcdUSB;
+ pDev->bDeviceClass = pConInfo->DeviceDescriptor.bDeviceClass;
+ pDev->bDeviceSubClass = pConInfo->DeviceDescriptor.bDeviceSubClass;
+ pDev->bDeviceProtocol = pConInfo->DeviceDescriptor.bDeviceProtocol;
+ pDev->idVendor = pConInfo->DeviceDescriptor.idVendor;
+ pDev->idProduct = pConInfo->DeviceDescriptor.idProduct;
+ pDev->bcdDevice = pConInfo->DeviceDescriptor.bcdDevice;
+ pDev->bBus = 0; /** @todo figure out bBus on windows... */
+ pDev->bPort = iPort;
+ /** @todo check which devices are used for primary input (keyboard & mouse) */
+ if (!lpszDrvKeyName || *lpszDrvKeyName == 0)
+ pDev->enmState = USBDEVICESTATE_UNUSED;
+ else
+ pDev->enmState = USBDEVICESTATE_USED_BY_HOST_CAPTURABLE;
+
+ /* Determine the speed the device is operating at. */
+ switch (pConInfo->Speed)
+ {
+ case UsbLowSpeed: pDev->enmSpeed = USBDEVICESPEED_LOW; break;
+ case UsbFullSpeed: pDev->enmSpeed = USBDEVICESPEED_FULL; break;
+ case UsbHighSpeed: pDev->enmSpeed = USBDEVICESPEED_HIGH; break;
+ default: /* If we don't know, most likely it's something new. */
+ case UsbSuperSpeed: pDev->enmSpeed = USBDEVICESPEED_SUPER; break;
+ }
+ /* Unfortunately USB_NODE_CONNECTION_INFORMATION_EX will not report UsbSuperSpeed, and
+ * it's not even defined in the Win7 DDK we use. So we go by the USB version, and
+ * luckily we know that USB3 must mean SuperSpeed. The USB3 spec guarantees this (9.6.1).
+ */
+ if (pDev->bcdUSB >= 0x0300)
+ pDev->enmSpeed = USBDEVICESPEED_SUPER;
+
+ int rc = RTStrAPrintf((char **)&pDev->pszAddress, "%s", lpszDrvKeyName);
+ if (rc < 0)
+ return VERR_NO_MEMORY;
+ pDev->pszBackend = RTStrDup("host");
+ if (!pDev->pszBackend)
+ {
+ RTStrFree((char *)pDev->pszAddress);
+ return VERR_NO_STR_MEMORY;
+ }
+ pDev->pszHubName = RTStrDup(lpszHubName);
+ pDev->bNumConfigurations = 0;
+ pDev->u64SerialHash = 0;
+
+ for (; pDrList; pDrList = pDrList->pNext)
+ {
+ char **ppszString = NULL;
+ if ( pConInfo->DeviceDescriptor.iManufacturer
+ && pDrList->iDr == pConInfo->DeviceDescriptor.iManufacturer)
+ ppszString = (char **)&pDev->pszManufacturer;
+ else if ( pConInfo->DeviceDescriptor.iProduct
+ && pDrList->iDr == pConInfo->DeviceDescriptor.iProduct)
+ ppszString = (char **)&pDev->pszProduct;
+ else if ( pConInfo->DeviceDescriptor.iSerialNumber
+ && pDrList->iDr == pConInfo->DeviceDescriptor.iSerialNumber)
+ ppszString = (char **)&pDev->pszSerialNumber;
+ if (ppszString)
+ {
+ rc = RTUtf16ToUtf8((PCRTUTF16)pDrList->StrDr.bString, ppszString);
+ if (RT_SUCCESS(rc))
+ {
+ Assert(*ppszString);
+ USBLibPurgeEncoding(*ppszString);
+
+ if (pDrList->iDr == pConInfo->DeviceDescriptor.iSerialNumber)
+ pDev->u64SerialHash = USBLibHashSerial(*ppszString);
+ }
+ else
+ {
+ AssertMsgFailed(("RTUtf16ToUtf8 failed, rc (%d), resuming\n", rc));
+ *ppszString = NULL;
+ }
+ }
+ }
+
+ return VINF_SUCCESS;
+}
+#else
+
+static PSP_DEVICE_INTERFACE_DETAIL_DATA usbLibGetDevDetail(HDEVINFO InfoSet, PSP_DEVICE_INTERFACE_DATA InterfaceData, PSP_DEVINFO_DATA DevInfoData);
+static void *usbLibGetRegistryProperty(HDEVINFO InfoSet, const PSP_DEVINFO_DATA DevData, DWORD Property);
+
+/* Populate the data for a single proxied USB device. */
+static int usbLibVUsbDevicePopulate(PVBOXUSB_DEV pVuDev, HDEVINFO InfoSet, PSP_DEVICE_INTERFACE_DATA InterfaceData)
+{
+ PSP_DEVICE_INTERFACE_DETAIL_DATA DetailData = NULL;
+ SP_DEVINFO_DATA DeviceData;
+ LPCSTR Location;
+ int rc = VINF_SUCCESS;
+
+ memset(&DeviceData, 0, sizeof(DeviceData));
+ DeviceData.cbSize = sizeof(DeviceData);
+ /* The interface detail includes the device path. */
+ DetailData = usbLibGetDevDetail(InfoSet, InterfaceData, &DeviceData);
+ if (DetailData)
+ {
+ strncpy(pVuDev->szName, DetailData->DevicePath, sizeof(pVuDev->szName));
+
+ /* The location is used as a unique identifier for cross-referencing the two lists. */
+ Location = (LPCSTR)usbLibGetRegistryProperty(InfoSet, &DeviceData, SPDRP_DRIVER);
+ if (Location)
+ {
+ strncpy(pVuDev->szDriverRegName, Location, sizeof(pVuDev->szDriverRegName));
+ rc = usbLibVuDeviceValidate(pVuDev);
+ LogRelFunc(("Found VBoxUSB on `%s' (rc=%d)\n", pVuDev->szName, rc));
+ AssertRC(rc);
+
+ RTMemFree((void *)Location);
+ }
+ else
+ {
+ /* Errors will be logged by usbLibGetRegistryProperty(). */
+ rc = VERR_GENERAL_FAILURE;
+ }
+
+ RTMemFree(DetailData);
+ }
+ else
+ {
+ /* Errors will be logged by usbLibGetDevDetail(). */
+ rc = VERR_GENERAL_FAILURE;
+ }
+
+
+ return rc;
+}
+
+/* Enumerate proxied USB devices (with VBoxUSB.sys loaded). */
+static int usbLibEnumVUsbDevices(PVBOXUSB_DEV *ppVuDevs, uint32_t *pcVuDevs)
+{
+ SP_DEVICE_INTERFACE_DATA InterfaceData;
+ HDEVINFO InfoSet;
+ DWORD DeviceIndex;
+ DWORD dwErr;
+
+ *ppVuDevs = NULL;
+ *pcVuDevs = 0;
+
+ /* Enumerate all present devices which support the GUID_CLASS_VBOXUSB interface. */
+ InfoSet = SetupDiGetClassDevs(&GUID_CLASS_VBOXUSB, NULL, NULL,
+ (DIGCF_PRESENT | DIGCF_DEVICEINTERFACE));
+ if (InfoSet == INVALID_HANDLE_VALUE)
+ {
+ DWORD dwErr = GetLastError();
+ LogRelFunc(("SetupDiGetClassDevs for GUID_CLASS_VBOXUSB failed (dwErr=%u)\n", dwErr));
+ AssertFailed();
+ return VERR_GENERAL_FAILURE;
+ }
+
+ memset(&InterfaceData, 0, sizeof(InterfaceData));
+ InterfaceData.cbSize = sizeof(InterfaceData);
+ DeviceIndex = 0;
+
+ /* Loop over the enumerated list. */
+ while (SetupDiEnumDeviceInterfaces(InfoSet, NULL, &GUID_CLASS_VBOXUSB, DeviceIndex, &InterfaceData))
+ {
+ /* we've now got the IfData */
+ PVBOXUSB_DEV pVuDev = (PVBOXUSB_DEV)RTMemAllocZ(sizeof (*pVuDev));
+ if (!pVuDev)
+ {
+ AssertFailed();
+ LogRelFunc(("RTMemAllocZ failed\n"));
+ break;
+ }
+
+ int rc = usbLibVUsbDevicePopulate(pVuDev, InfoSet, &InterfaceData);
+ if (RT_SUCCESS(rc))
+ {
+ pVuDev->pNext = *ppVuDevs;
+ *ppVuDevs = pVuDev;
+ ++*pcVuDevs;
+ }
+ else /* Skip this device but continue enumerating. */
+ AssertMsgFailed(("usbLibVuDevicePopulate failed, rc=%d\n", rc));
+
+ memset(&InterfaceData, 0, sizeof(InterfaceData));
+ InterfaceData.cbSize = sizeof(InterfaceData);
+ ++DeviceIndex;
+ }
+
+ /* Paranoia. */
+ dwErr = GetLastError();
+ if (dwErr != ERROR_NO_MORE_ITEMS)
+ {
+ LogRelFunc(("SetupDiEnumDeviceInterfaces failed (dwErr=%u)\n", dwErr));
+ AssertFailed();
+ }
+
+ SetupDiDestroyDeviceInfoList(InfoSet);
+
+ return VINF_SUCCESS;
+}
+
+static uint16_t usbLibParseHexNumU16(LPCSTR *ppStr)
+{
+ const char *pStr = *ppStr;
+ char c;
+ uint16_t num = 0;
+ unsigned u;
+
+ for (int i = 0; i < 4; ++i)
+ {
+ if (!*pStr) /* Just in case the string is too short. */
+ break;
+
+ c = *pStr;
+ u = c >= 'A' ? c - 'A' + 10 : c - '0'; /* Hex digit to number. */
+ num |= u << (12 - 4 * i);
+ pStr++;
+ }
+ *ppStr = pStr;
+
+ return num;
+}
+
+static int usbLibDevPopulate(PUSBDEVICE pDev, PUSB_NODE_CONNECTION_INFORMATION_EX pConInfo, ULONG iPort, LPCSTR lpszLocation, LPCSTR lpszDrvKeyName, LPCSTR lpszHubName, PVBOXUSB_STRING_DR_ENTRY pDrList)
+{
+ pDev->bcdUSB = pConInfo->DeviceDescriptor.bcdUSB;
+ pDev->bDeviceClass = pConInfo->DeviceDescriptor.bDeviceClass;
+ pDev->bDeviceSubClass = pConInfo->DeviceDescriptor.bDeviceSubClass;
+ pDev->bDeviceProtocol = pConInfo->DeviceDescriptor.bDeviceProtocol;
+ pDev->idVendor = pConInfo->DeviceDescriptor.idVendor;
+ pDev->idProduct = pConInfo->DeviceDescriptor.idProduct;
+ pDev->bcdDevice = pConInfo->DeviceDescriptor.bcdDevice;
+ pDev->bBus = 0; /* The hub numbering is not very useful on Windows. Skip it. */
+ pDev->bPort = iPort;
+
+ /* The port path/location uniquely identifies the port. */
+ pDev->pszPortPath = RTStrDup(lpszLocation);
+ if (!pDev->pszPortPath)
+ return VERR_NO_STR_MEMORY;
+
+ /* If there is no DriverKey, the device is unused because there's no driver. */
+ if (!lpszDrvKeyName || *lpszDrvKeyName == 0)
+ pDev->enmState = USBDEVICESTATE_UNUSED;
+ else
+ pDev->enmState = USBDEVICESTATE_USED_BY_HOST_CAPTURABLE;
+
+ /* Determine the speed the device is operating at. */
+ switch (pConInfo->Speed)
+ {
+ case UsbLowSpeed: pDev->enmSpeed = USBDEVICESPEED_LOW; break;
+ case UsbFullSpeed: pDev->enmSpeed = USBDEVICESPEED_FULL; break;
+ case UsbHighSpeed: pDev->enmSpeed = USBDEVICESPEED_HIGH; break;
+ default: /* If we don't know, most likely it's something new. */
+ case UsbSuperSpeed: pDev->enmSpeed = USBDEVICESPEED_SUPER; break;
+ }
+ /* Unfortunately USB_NODE_CONNECTION_INFORMATION_EX will not report UsbSuperSpeed, and
+ * it's not even defined in the Win7 DDK we use. So we go by the USB version, and
+ * luckily we know that USB3 must mean SuperSpeed. The USB3 spec guarantees this (9.6.1).
+ */
+ if (pDev->bcdUSB >= 0x0300)
+ pDev->enmSpeed = USBDEVICESPEED_SUPER;
+
+ /* If there's no DriverKey, jam in an empty string to avoid NULL pointers. */
+ if (!lpszDrvKeyName)
+ pDev->pszAddress = RTStrDup("");
+ else
+ pDev->pszAddress = RTStrDup(lpszDrvKeyName);
+
+ pDev->pszBackend = RTStrDup("host");
+ if (!pDev->pszBackend)
+ {
+ RTStrFree((char *)pDev->pszAddress);
+ return VERR_NO_STR_MEMORY;
+ }
+ pDev->pszHubName = RTStrDup(lpszHubName);
+ pDev->bNumConfigurations = 0;
+ pDev->u64SerialHash = 0;
+
+ for (; pDrList; pDrList = pDrList->pNext)
+ {
+ char **ppszString = NULL;
+ if ( pConInfo->DeviceDescriptor.iManufacturer
+ && pDrList->iDr == pConInfo->DeviceDescriptor.iManufacturer)
+ ppszString = (char **)&pDev->pszManufacturer;
+ else if ( pConInfo->DeviceDescriptor.iProduct
+ && pDrList->iDr == pConInfo->DeviceDescriptor.iProduct)
+ ppszString = (char **)&pDev->pszProduct;
+ else if ( pConInfo->DeviceDescriptor.iSerialNumber
+ && pDrList->iDr == pConInfo->DeviceDescriptor.iSerialNumber)
+ ppszString = (char **)&pDev->pszSerialNumber;
+ if (ppszString)
+ {
+ int rc = RTUtf16ToUtf8((PCRTUTF16)pDrList->StrDr.bString, ppszString);
+ if (RT_SUCCESS(rc))
+ {
+ Assert(*ppszString);
+ USBLibPurgeEncoding(*ppszString);
+
+ if (pDrList->iDr == pConInfo->DeviceDescriptor.iSerialNumber)
+ pDev->u64SerialHash = USBLibHashSerial(*ppszString);
+ }
+ else
+ {
+ AssertMsgFailed(("RTUtf16ToUtf8 failed, rc (%d), resuming\n", rc));
+ *ppszString = NULL;
+ }
+ }
+ }
+
+ return VINF_SUCCESS;
+}
+#endif
+
+static void usbLibDevStrFree(LPSTR lpszName)
+{
+ RTStrFree(lpszName);
+}
+
+#ifndef VBOX_WITH_NEW_USB_ENUM
+static int usbLibDevStrDriverKeyGet(HANDLE hHub, ULONG iPort, LPSTR* plpszName)
+{
+ USB_NODE_CONNECTION_DRIVERKEY_NAME Name;
+ DWORD cbReturned = 0;
+ Name.ConnectionIndex = iPort;
+ *plpszName = NULL;
+ if (!DeviceIoControl(hHub, IOCTL_USB_GET_NODE_CONNECTION_DRIVERKEY_NAME, &Name, sizeof (Name), &Name, sizeof (Name), &cbReturned, NULL))
+ {
+#ifdef VBOX_WITH_ANNOYING_USB_ASSERTIONS
+ AssertMsgFailed(("DeviceIoControl 1 fail dwErr (%u)\n", GetLastError()));
+#endif
+ return VERR_GENERAL_FAILURE;
+ }
+
+ if (Name.ActualLength < sizeof (Name))
+ {
+ AssertFailed();
+ return VERR_OUT_OF_RESOURCES;
+ }
+
+ PUSB_NODE_CONNECTION_DRIVERKEY_NAME pName = (PUSB_NODE_CONNECTION_DRIVERKEY_NAME)RTMemAllocZ(Name.ActualLength);
+ if (!pName)
+ {
+ AssertFailed();
+ return VERR_OUT_OF_RESOURCES;
+ }
+
+ int rc = VINF_SUCCESS;
+ pName->ConnectionIndex = iPort;
+ if (DeviceIoControl(hHub, IOCTL_USB_GET_NODE_CONNECTION_DRIVERKEY_NAME, pName, Name.ActualLength, pName, Name.ActualLength, &cbReturned, NULL))
+ {
+ rc = RTUtf16ToUtf8Ex((PCRTUTF16)pName->DriverKeyName, pName->ActualLength / sizeof (WCHAR), plpszName, 0, NULL);
+ AssertRC(rc);
+ if (RT_SUCCESS(rc))
+ rc = VINF_SUCCESS;
+ }
+ else
+ {
+ DWORD dwErr = GetLastError(); NOREF(dwErr);
+ AssertMsgFailed(("DeviceIoControl 2 fail dwErr (%u)\n", dwErr));
+ rc = VERR_GENERAL_FAILURE;
+ }
+ RTMemFree(pName);
+ return rc;
+}
+#endif
+
+static int usbLibDevStrHubNameGet(HANDLE hHub, ULONG iPort, LPSTR* plpszName)
+{
+ USB_NODE_CONNECTION_NAME Name;
+ DWORD cbReturned = 0;
+ Name.ConnectionIndex = iPort;
+ *plpszName = NULL;
+ if (!DeviceIoControl(hHub, IOCTL_USB_GET_NODE_CONNECTION_NAME, &Name, sizeof (Name), &Name, sizeof (Name), &cbReturned, NULL))
+ {
+ AssertFailed();
+ return VERR_GENERAL_FAILURE;
+ }
+
+ if (Name.ActualLength < sizeof (Name))
+ {
+ AssertFailed();
+ return VERR_OUT_OF_RESOURCES;
+ }
+
+ PUSB_NODE_CONNECTION_NAME pName = (PUSB_NODE_CONNECTION_NAME)RTMemAllocZ(Name.ActualLength);
+ if (!pName)
+ {
+ AssertFailed();
+ return VERR_OUT_OF_RESOURCES;
+ }
+
+ int rc = VINF_SUCCESS;
+ pName->ConnectionIndex = iPort;
+ if (DeviceIoControl(hHub, IOCTL_USB_GET_NODE_CONNECTION_NAME, pName, Name.ActualLength, pName, Name.ActualLength, &cbReturned, NULL))
+ {
+ rc = RTUtf16ToUtf8Ex((PCRTUTF16)pName->NodeName, pName->ActualLength / sizeof (WCHAR), plpszName, 0, NULL);
+ AssertRC(rc);
+ if (RT_SUCCESS(rc))
+ rc = VINF_SUCCESS;
+ }
+ else
+ {
+ AssertFailed();
+ rc = VERR_GENERAL_FAILURE;
+ }
+ RTMemFree(pName);
+ return rc;
+}
+
+static int usbLibDevStrRootHubNameGet(HANDLE hCtl, LPSTR* plpszName)
+{
+ USB_ROOT_HUB_NAME HubName;
+ DWORD cbReturned = 0;
+ *plpszName = NULL;
+ if (!DeviceIoControl(hCtl, IOCTL_USB_GET_ROOT_HUB_NAME, NULL, 0, &HubName, sizeof (HubName), &cbReturned, NULL))
+ {
+ return VERR_GENERAL_FAILURE;
+ }
+ PUSB_ROOT_HUB_NAME pHubName = (PUSB_ROOT_HUB_NAME)RTMemAllocZ(HubName.ActualLength);
+ if (!pHubName)
+ return VERR_OUT_OF_RESOURCES;
+
+ int rc = VINF_SUCCESS;
+ if (DeviceIoControl(hCtl, IOCTL_USB_GET_ROOT_HUB_NAME, NULL, 0, pHubName, HubName.ActualLength, &cbReturned, NULL))
+ {
+ rc = RTUtf16ToUtf8Ex((PCRTUTF16)pHubName->RootHubName, pHubName->ActualLength / sizeof (WCHAR), plpszName, 0, NULL);
+ AssertRC(rc);
+ if (RT_SUCCESS(rc))
+ rc = VINF_SUCCESS;
+ }
+ else
+ {
+ rc = VERR_GENERAL_FAILURE;
+ }
+ RTMemFree(pHubName);
+ return rc;
+}
+
+static int usbLibDevCfgDrGet(HANDLE hHub, LPCSTR lpcszHubName, ULONG iPort, ULONG iDr, PUSB_CONFIGURATION_DESCRIPTOR *ppDr)
+{
+ *ppDr = NULL;
+
+ char Buf[sizeof (USB_DESCRIPTOR_REQUEST) + sizeof (USB_CONFIGURATION_DESCRIPTOR)];
+ memset(&Buf, 0, sizeof (Buf));
+
+ PUSB_DESCRIPTOR_REQUEST pCfgDrRq = (PUSB_DESCRIPTOR_REQUEST)Buf;
+ PUSB_CONFIGURATION_DESCRIPTOR pCfgDr = (PUSB_CONFIGURATION_DESCRIPTOR)(Buf + sizeof (*pCfgDrRq));
+
+ pCfgDrRq->ConnectionIndex = iPort;
+ pCfgDrRq->SetupPacket.wValue = (USB_CONFIGURATION_DESCRIPTOR_TYPE << 8) | iDr;
+ pCfgDrRq->SetupPacket.wLength = (USHORT)(sizeof (USB_CONFIGURATION_DESCRIPTOR));
+ DWORD cbReturned = 0;
+ if (!DeviceIoControl(hHub, IOCTL_USB_GET_DESCRIPTOR_FROM_NODE_CONNECTION, pCfgDrRq, sizeof (Buf),
+ pCfgDrRq, sizeof (Buf),
+ &cbReturned, NULL))
+ {
+ DWORD dwErr = GetLastError();
+ LogRelFunc(("IOCTL_USB_GET_DESCRIPTOR_FROM_NODE_CONNECTION #1 failed (dwErr=%u) on hub %s port %d\n", dwErr, lpcszHubName, iPort));
+#ifdef VBOX_WITH_ANNOYING_USB_ASSERTIONS
+ AssertFailed();
+#endif
+ return VERR_GENERAL_FAILURE;
+ }
+
+ if (sizeof (Buf) != cbReturned)
+ {
+ AssertFailed();
+ return VERR_GENERAL_FAILURE;
+ }
+
+ if (pCfgDr->wTotalLength < sizeof (USB_CONFIGURATION_DESCRIPTOR))
+ {
+ AssertFailed();
+ return VERR_GENERAL_FAILURE;
+ }
+
+ DWORD cbRq = sizeof (USB_DESCRIPTOR_REQUEST) + pCfgDr->wTotalLength;
+ PUSB_DESCRIPTOR_REQUEST pRq = (PUSB_DESCRIPTOR_REQUEST)RTMemAllocZ(cbRq);
+ Assert(pRq);
+ if (!pRq)
+ return VERR_OUT_OF_RESOURCES;
+
+ int rc = VERR_GENERAL_FAILURE;
+ do
+ {
+ PUSB_CONFIGURATION_DESCRIPTOR pDr = (PUSB_CONFIGURATION_DESCRIPTOR)(pRq + 1);
+ pRq->ConnectionIndex = iPort;
+ pRq->SetupPacket.wValue = (USB_CONFIGURATION_DESCRIPTOR_TYPE << 8) | iDr;
+ pRq->SetupPacket.wLength = (USHORT)(cbRq - sizeof (USB_DESCRIPTOR_REQUEST));
+ if (!DeviceIoControl(hHub, IOCTL_USB_GET_DESCRIPTOR_FROM_NODE_CONNECTION, pRq, cbRq,
+ pRq, cbRq,
+ &cbReturned, NULL))
+ {
+ DWORD dwErr = GetLastError();
+ LogRelFunc(("IOCTL_USB_GET_DESCRIPTOR_FROM_NODE_CONNECTION #2 failed (dwErr=%u) on hub %s port %d\n", dwErr, lpcszHubName, iPort));
+#ifdef VBOX_WITH_ANNOYING_USB_ASSERTIONS
+ AssertFailed();
+#endif
+ break;
+ }
+
+ if (cbRq != cbReturned)
+ {
+ AssertFailed();
+ break;
+ }
+
+ if (pDr->wTotalLength != cbRq - sizeof (USB_DESCRIPTOR_REQUEST))
+ {
+ AssertFailed();
+ break;
+ }
+
+ *ppDr = pDr;
+ return VINF_SUCCESS;
+ } while (0);
+
+ RTMemFree(pRq);
+ return rc;
+}
+
+static void usbLibDevCfgDrFree(PUSB_CONFIGURATION_DESCRIPTOR pDr)
+{
+ Assert(pDr);
+ PUSB_DESCRIPTOR_REQUEST pRq = ((PUSB_DESCRIPTOR_REQUEST)pDr)-1;
+ RTMemFree(pRq);
+}
+
+static int usbLibDevStrDrEntryGet(HANDLE hHub, LPCSTR lpcszHubName, ULONG iPort, ULONG iDr, USHORT idLang, PVBOXUSB_STRING_DR_ENTRY *ppList)
+{
+ char szBuf[sizeof (USB_DESCRIPTOR_REQUEST) + MAXIMUM_USB_STRING_LENGTH];
+ RT_ZERO(szBuf);
+
+ PUSB_DESCRIPTOR_REQUEST pRq = (PUSB_DESCRIPTOR_REQUEST)szBuf;
+ PUSB_STRING_DESCRIPTOR pDr = (PUSB_STRING_DESCRIPTOR)(szBuf + sizeof (*pRq));
+ RT_BZERO(pDr, sizeof(USB_STRING_DESCRIPTOR));
+
+ pRq->ConnectionIndex = iPort;
+ pRq->SetupPacket.wValue = (USB_STRING_DESCRIPTOR_TYPE << 8) | iDr;
+ pRq->SetupPacket.wIndex = idLang;
+ pRq->SetupPacket.wLength = sizeof (szBuf) - sizeof (*pRq);
+
+ DWORD cbReturned = 0;
+ if (!DeviceIoControl(hHub, IOCTL_USB_GET_DESCRIPTOR_FROM_NODE_CONNECTION, pRq, sizeof (szBuf),
+ pRq, sizeof(szBuf),
+ &cbReturned, NULL))
+ {
+ DWORD dwErr = GetLastError();
+ LogRel(("Getting USB descriptor (id %u) failed (dwErr=%u) on hub %s port %d\n", iDr, dwErr, lpcszHubName, iPort));
+ return RTErrConvertFromWin32(dwErr);
+ }
+
+ /* Wrong descriptor type at the requested port index? Bail out. */
+ if (pDr->bDescriptorType != USB_STRING_DESCRIPTOR_TYPE)
+ return VERR_NOT_FOUND;
+
+ /* Some more sanity checks. */
+ if ( (cbReturned < sizeof (*pDr) + 2)
+ || (!!(pDr->bLength % 2))
+ || (pDr->bLength != cbReturned - sizeof(*pRq)))
+ {
+ AssertMsgFailed(("Sanity check failed for string descriptor: cbReturned=%RI32, cbDevReq=%zu, type=%RU8, len=%RU8, port=%RU32, index=%RU32, lang=%RU32\n",
+ cbReturned, sizeof(*pRq), pDr->bDescriptorType, pDr->bLength, iPort, iDr, idLang));
+ return VERR_INVALID_PARAMETER;
+ }
+
+ PVBOXUSB_STRING_DR_ENTRY pEntry =
+ (PVBOXUSB_STRING_DR_ENTRY)RTMemAllocZ(sizeof(VBOXUSB_STRING_DR_ENTRY) + pDr->bLength + 2);
+ AssertPtr(pEntry);
+ if (!pEntry)
+ return VERR_NO_MEMORY;
+
+ pEntry->pNext = *ppList;
+ pEntry->iDr = iDr;
+ pEntry->idLang = idLang;
+ memcpy(&pEntry->StrDr, pDr, pDr->bLength);
+
+ *ppList = pEntry;
+
+ return VINF_SUCCESS;
+}
+
+static void usbLibDevStrDrEntryFree(PVBOXUSB_STRING_DR_ENTRY pDr)
+{
+ RTMemFree(pDr);
+}
+
+static void usbLibDevStrDrEntryFreeList(PVBOXUSB_STRING_DR_ENTRY pDr)
+{
+ while (pDr)
+ {
+ PVBOXUSB_STRING_DR_ENTRY pNext = pDr->pNext;
+ usbLibDevStrDrEntryFree(pDr);
+ pDr = pNext;
+ }
+}
+
+static int usbLibDevStrDrEntryGetForLangs(HANDLE hHub, LPCSTR lpcszHubName, ULONG iPort, ULONG iDr, ULONG cIdLang, const USHORT *pIdLang, PVBOXUSB_STRING_DR_ENTRY *ppList)
+{
+ for (ULONG i = 0; i < cIdLang; ++i)
+ {
+ usbLibDevStrDrEntryGet(hHub, lpcszHubName, iPort, iDr, pIdLang[i], ppList);
+ }
+ return VINF_SUCCESS;
+}
+
+static int usbLibDevStrDrEntryGetAll(HANDLE hHub, LPCSTR lpcszHubName, ULONG iPort, PUSB_DEVICE_DESCRIPTOR pDevDr, PUSB_CONFIGURATION_DESCRIPTOR pCfgDr, PVBOXUSB_STRING_DR_ENTRY *ppList)
+{
+ /* Read string descriptor zero to determine what languages are available. */
+ int rc = usbLibDevStrDrEntryGet(hHub, lpcszHubName, iPort, 0, 0, ppList);
+ if (RT_FAILURE(rc))
+ return rc;
+
+ PUSB_STRING_DESCRIPTOR pLangStrDr = &(*ppList)->StrDr;
+ USHORT *pIdLang = pLangStrDr->bString;
+ ULONG cIdLang = (pLangStrDr->bLength - RT_OFFSETOF(USB_STRING_DESCRIPTOR, bString)) / sizeof (*pIdLang);
+
+ if (pDevDr->iManufacturer)
+ {
+ rc = usbLibDevStrDrEntryGetForLangs(hHub, lpcszHubName, iPort, pDevDr->iManufacturer, cIdLang, pIdLang, ppList);
+ AssertRC(rc);
+ }
+
+ if (pDevDr->iProduct)
+ {
+ rc = usbLibDevStrDrEntryGetForLangs(hHub, lpcszHubName, iPort, pDevDr->iProduct, cIdLang, pIdLang, ppList);
+ AssertRC(rc);
+ }
+
+ if (pDevDr->iSerialNumber)
+ {
+ rc = usbLibDevStrDrEntryGetForLangs(hHub, lpcszHubName, iPort, pDevDr->iSerialNumber, cIdLang, pIdLang, ppList);
+ AssertRC(rc);
+ }
+
+ PUCHAR pCur = (PUCHAR)pCfgDr;
+ PUCHAR pEnd = pCur + pCfgDr->wTotalLength;
+ while (pCur + sizeof (USB_COMMON_DESCRIPTOR) <= pEnd)
+ {
+ PUSB_COMMON_DESCRIPTOR pCmnDr = (PUSB_COMMON_DESCRIPTOR)pCur;
+ if (pCur + pCmnDr->bLength > pEnd)
+ {
+ AssertFailed();
+ break;
+ }
+
+ /* This is invalid but was seen with a TerraTec Aureon 7.1 USB sound card. */
+ if (!pCmnDr->bLength)
+ break;
+
+ switch (pCmnDr->bDescriptorType)
+ {
+ case USB_CONFIGURATION_DESCRIPTOR_TYPE:
+ {
+ if (pCmnDr->bLength != sizeof (USB_CONFIGURATION_DESCRIPTOR))
+ {
+ AssertFailed();
+ break;
+ }
+ PUSB_CONFIGURATION_DESCRIPTOR pCurCfgDr = (PUSB_CONFIGURATION_DESCRIPTOR)pCmnDr;
+ if (!pCurCfgDr->iConfiguration)
+ break;
+ rc = usbLibDevStrDrEntryGetForLangs(hHub, lpcszHubName, iPort, pCurCfgDr->iConfiguration, cIdLang, pIdLang, ppList);
+ AssertRC(rc);
+ break;
+ }
+ case USB_INTERFACE_DESCRIPTOR_TYPE:
+ {
+ if (pCmnDr->bLength != sizeof (USB_INTERFACE_DESCRIPTOR) && pCmnDr->bLength != sizeof (USB_INTERFACE_DESCRIPTOR2))
+ {
+ AssertFailed();
+ break;
+ }
+ PUSB_INTERFACE_DESCRIPTOR pCurIfDr = (PUSB_INTERFACE_DESCRIPTOR)pCmnDr;
+ if (!pCurIfDr->iInterface)
+ break;
+ rc = usbLibDevStrDrEntryGetForLangs(hHub, lpcszHubName, iPort, pCurIfDr->iInterface, cIdLang, pIdLang, ppList);
+ AssertRC(rc);
+ break;
+ }
+ default:
+ break;
+ }
+
+ pCur = pCur + pCmnDr->bLength;
+ }
+
+ return VINF_SUCCESS;
+}
+
+#ifndef VBOX_WITH_NEW_USB_ENUM
+static int usbLibDevGetHubDevices(LPCSTR lpszName, PUSBDEVICE *ppDevs, uint32_t *pcDevs);
+
+static int usbLibDevGetHubPortDevices(HANDLE hHub, LPCSTR lpcszHubName, ULONG iPort, PUSBDEVICE *ppDevs, uint32_t *pcDevs)
+{
+ int rc = VINF_SUCCESS;
+ char Buf[sizeof (USB_NODE_CONNECTION_INFORMATION_EX) + (sizeof (USB_PIPE_INFO) * 20)];
+ PUSB_NODE_CONNECTION_INFORMATION_EX pConInfo = (PUSB_NODE_CONNECTION_INFORMATION_EX)Buf;
+ //PUSB_PIPE_INFO paPipeInfo = (PUSB_PIPE_INFO)(Buf + sizeof (PUSB_NODE_CONNECTION_INFORMATION_EX));
+ DWORD cbReturned = 0;
+ memset(&Buf, 0, sizeof (Buf));
+ pConInfo->ConnectionIndex = iPort;
+ if (!DeviceIoControl(hHub, IOCTL_USB_GET_NODE_CONNECTION_INFORMATION_EX,
+ pConInfo, sizeof (Buf),
+ pConInfo, sizeof (Buf),
+ &cbReturned, NULL))
+ {
+ DWORD dwErr = GetLastError(); NOREF(dwErr);
+ LogRel(("Getting USB connection information failed (dwErr=%u) on hub %s\n", dwErr, lpcszHubName));
+ AssertMsg(dwErr == ERROR_DEVICE_NOT_CONNECTED, (__FUNCTION__": DeviceIoControl failed (dwErr=%u)\n", dwErr));
+ return VERR_GENERAL_FAILURE;
+ }
+
+ if (pConInfo->ConnectionStatus != DeviceConnected)
+ {
+ /* just ignore & return success */
+ return VWRN_INVALID_HANDLE;
+ }
+
+ if (pConInfo->DeviceIsHub)
+ {
+ LPSTR lpszChildHubName = NULL;
+ rc = usbLibDevStrHubNameGet(hHub, iPort, &lpszChildHubName);
+ AssertRC(rc);
+ if (RT_SUCCESS(rc))
+ {
+ rc = usbLibDevGetHubDevices(lpszChildHubName, ppDevs, pcDevs);
+ usbLibDevStrFree(lpszChildHubName);
+ AssertRC(rc);
+ return rc;
+ }
+ /* ignore this err */
+ return VINF_SUCCESS;
+ }
+
+ bool fFreeNameBuf = true;
+ char nameEmptyBuf = '\0';
+ LPSTR lpszName = NULL;
+ rc = usbLibDevStrDriverKeyGet(hHub, iPort, &lpszName);
+ Assert(!!lpszName == !!RT_SUCCESS(rc));
+ if (!lpszName)
+ {
+ LogRelFunc(("No DriverKey on hub %s port %d\n", lpcszHubName, iPort));
+ lpszName = &nameEmptyBuf;
+ fFreeNameBuf = false;
+ }
+
+ PUSB_CONFIGURATION_DESCRIPTOR pCfgDr = NULL;
+ PVBOXUSB_STRING_DR_ENTRY pList = NULL;
+ rc = usbLibDevCfgDrGet(hHub, lpcszHubName, iPort, 0, &pCfgDr);
+ if (pCfgDr)
+ {
+ rc = usbLibDevStrDrEntryGetAll(hHub, lpcszHubName, iPort, &pConInfo->DeviceDescriptor, pCfgDr, &pList);
+#ifdef VBOX_WITH_ANNOYING_USB_ASSERTIONS
+ AssertRC(rc); // this can fail if device suspended
+#endif
+ }
+
+ PUSBDEVICE pDev = (PUSBDEVICE)RTMemAllocZ(sizeof (*pDev));
+ if (RT_LIKELY(pDev))
+ {
+ rc = usbLibDevPopulate(pDev, pConInfo, iPort, lpszName, lpcszHubName, pList);
+ if (RT_SUCCESS(rc))
+ {
+ pDev->pNext = *ppDevs;
+ *ppDevs = pDev;
+ ++*pcDevs;
+ }
+ else
+ RTMemFree(pDev);
+ }
+ else
+ rc = VERR_NO_MEMORY;
+
+ if (pCfgDr)
+ usbLibDevCfgDrFree(pCfgDr);
+ if (fFreeNameBuf)
+ {
+ Assert(lpszName);
+ usbLibDevStrFree(lpszName);
+ }
+ if (pList)
+ usbLibDevStrDrEntryFreeList(pList);
+
+ return rc;
+}
+
+static int usbLibDevGetHubDevices(LPCSTR lpszName, PUSBDEVICE *ppDevs, uint32_t *pcDevs)
+{
+ LPSTR lpszDevName = (LPSTR)RTMemAllocZ(strlen(lpszName) + sizeof("\\\\.\\"));
+ HANDLE hDev = INVALID_HANDLE_VALUE;
+ Assert(lpszDevName);
+ if (!lpszDevName)
+ {
+ AssertFailed();
+ return VERR_OUT_OF_RESOURCES;
+ }
+
+ int rc = VINF_SUCCESS;
+ strcpy(lpszDevName, "\\\\.\\");
+ strcpy(lpszDevName + sizeof("\\\\.\\") - sizeof (lpszDevName[0]), lpszName);
+ do
+ {
+ DWORD cbReturned = 0;
+ hDev = CreateFile(lpszDevName, GENERIC_WRITE, FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL);
+ if (hDev == INVALID_HANDLE_VALUE)
+ {
+ AssertFailed();
+ break;
+ }
+
+ USB_NODE_INFORMATION NodeInfo;
+ memset(&NodeInfo, 0, sizeof (NodeInfo));
+ if (!DeviceIoControl(hDev, IOCTL_USB_GET_NODE_INFORMATION,
+ &NodeInfo, sizeof (NodeInfo),
+ &NodeInfo, sizeof (NodeInfo),
+ &cbReturned, NULL))
+ {
+ LogRel(("Getting USB node information failed (dwErr=%u) on hub %s\n", GetLastError(), lpszName));
+ AssertFailed();
+ break;
+ }
+
+ for (ULONG i = 1; i <= NodeInfo.u.HubInformation.HubDescriptor.bNumberOfPorts; ++i)
+ {
+ /* Just skip devices for which we failed to create the device structure. */
+ usbLibDevGetHubPortDevices(hDev, lpszName, i, ppDevs, pcDevs);
+ }
+ } while (0);
+
+ if (hDev != INVALID_HANDLE_VALUE)
+ CloseHandle(hDev);
+
+ RTMemFree(lpszDevName);
+
+ return rc;
+}
+#endif
+
+#ifdef VBOX_WITH_NEW_USB_ENUM
+
+/* Get a registry property for a device given its HDEVINFO + SP_DEVINFO_DATA. */
+static void *usbLibGetRegistryProperty(HDEVINFO InfoSet, const PSP_DEVINFO_DATA DevData, DWORD Property)
+{
+ BOOL rc;
+ DWORD dwReqLen;
+ void *PropertyData;
+
+ /* How large a buffer do we need? */
+ rc = SetupDiGetDeviceRegistryProperty(InfoSet, DevData, Property,
+ NULL, NULL, 0, &dwReqLen);
+ if (!rc && (GetLastError() != ERROR_INSUFFICIENT_BUFFER))
+ {
+ LogRelFunc(("Failed to query buffer size, error %ld\n", GetLastError()));
+ return NULL;
+ }
+
+ PropertyData = RTMemAlloc(dwReqLen);
+ if (!PropertyData)
+ return NULL;
+
+ /* Get the actual property data. */
+ rc = SetupDiGetDeviceRegistryProperty(InfoSet, DevData, Property,
+ NULL, (PBYTE)PropertyData, dwReqLen, &dwReqLen);
+ if (!rc)
+ {
+ LogRelFunc(("Failed to get property data, error %ld\n", GetLastError()));
+ RTMemFree(PropertyData);
+ return NULL;
+ }
+ return PropertyData;
+}
+
+/* Given a HDEVINFO and SP_DEVICE_INTERFACE_DATA, get the interface detail data and optionally device info data. */
+static PSP_DEVICE_INTERFACE_DETAIL_DATA usbLibGetDevDetail(HDEVINFO InfoSet, PSP_DEVICE_INTERFACE_DATA InterfaceData, PSP_DEVINFO_DATA DevInfoData)
+{
+ BOOL rc;
+ DWORD dwReqLen;
+ PSP_DEVICE_INTERFACE_DETAIL_DATA DetailData;
+
+ rc = SetupDiGetDeviceInterfaceDetail(InfoSet, InterfaceData, NULL, 0, &dwReqLen, DevInfoData);
+ if (!rc && (GetLastError() != ERROR_INSUFFICIENT_BUFFER))
+ {
+ LogRelFunc(("Failed to get interface detail size, error %ld\n", GetLastError()));
+ return NULL;
+ }
+
+ DetailData = (PSP_DEVICE_INTERFACE_DETAIL_DATA)RTMemAlloc(dwReqLen);
+ if (!DetailData)
+ return NULL;
+
+ memset(DetailData, 0, dwReqLen);
+ DetailData->cbSize = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA);
+
+ rc = SetupDiGetDeviceInterfaceDetail(InfoSet, InterfaceData, DetailData, dwReqLen, &dwReqLen, DevInfoData);
+ if (!rc)
+ {
+ LogRelFunc(("Failed to get interface detail, error %ld\n", GetLastError()));
+ RTMemFree(DetailData);
+ }
+
+ return DetailData;
+}
+
+/* Given a hub's PnP device instance, find its device path (file name). */
+static LPCSTR usbLibGetHubPathFromInstanceID(LPCSTR InstanceID)
+{
+ HDEVINFO InfoSet;
+ SP_DEVICE_INTERFACE_DATA InterfaceData;
+ PSP_DEVICE_INTERFACE_DETAIL_DATA DetailData;
+ BOOL rc;
+ LPSTR DevicePath = NULL;
+
+ /* Enumerate the DevInst's USB hub interface. */
+ InfoSet = SetupDiGetClassDevs(&GUID_DEVINTERFACE_USB_HUB, InstanceID, NULL,
+ DIGCF_DEVICEINTERFACE | DIGCF_PRESENT);
+ if (InfoSet == INVALID_HANDLE_VALUE)
+ {
+ LogRelFunc(("Failed to get interface for InstID %se, error %ld\n", InstanceID, GetLastError()));
+ return NULL;
+ }
+
+ memset(&InterfaceData, 0, sizeof(InterfaceData));
+ InterfaceData.cbSize = sizeof(InterfaceData);
+ rc = SetupDiEnumDeviceInterfaces(InfoSet, 0, &GUID_DEVINTERFACE_USB_HUB, 0, &InterfaceData);
+ if (!rc)
+ {
+ DWORD dwErr = GetLastError();
+
+ /* The parent device might not be a hub; that is valid, ignore such errors. */
+ if (dwErr != ERROR_NO_MORE_ITEMS)
+ LogRelFunc(("Failed to get interface data for InstID %s, error %ld\n", InstanceID, dwErr));
+ SetupDiDestroyDeviceInfoList(InfoSet);
+ return NULL;
+ }
+
+ DetailData = usbLibGetDevDetail(InfoSet, &InterfaceData, NULL);
+ if (!DetailData)
+ {
+ SetupDiDestroyDeviceInfoList(InfoSet);
+ return NULL;
+ }
+
+ /* Copy the device path out of the interface detail. */
+ DevicePath = RTStrDup(DetailData->DevicePath);
+ RTMemFree(DetailData);
+ SetupDiDestroyDeviceInfoList(InfoSet);
+
+ return DevicePath;
+}
+
+
+/* Use the Configuration Manager (CM) to get a devices's parent given its DEVINST and
+ * turn it into a PnP device instance ID string.
+ */
+static LPCSTR usbLibGetParentInstanceID(DEVINST DevInst)
+{
+ LPSTR InstanceID;
+ DEVINST ParentInst;
+ ULONG ulReqChars;
+ ULONG ulReqBytes;
+ CONFIGRET cr;
+
+ /* First get the parent DEVINST. */
+ cr = CM_Get_Parent(&ParentInst, DevInst, 0);
+ if (cr != CR_SUCCESS)
+ {
+ LogRelFunc(("Failed to get parent instance, error %ld\n", GetLastError()));
+ return NULL;
+ }
+
+ /* Then convert it to the instance ID string. */
+ cr = CM_Get_Device_ID_Size(&ulReqChars, ParentInst, 0);
+ if (cr != CR_SUCCESS)
+ {
+ LogRelFunc(("Failed to get device ID size (DevInst=%X), error %ld\n", DevInst, GetLastError()));
+ return NULL;
+ }
+
+ /* CM_Get_Device_ID_Size gives us the size in characters without terminating null. */
+ ulReqBytes = (ulReqChars + 1) * sizeof(char);
+ InstanceID = (LPSTR)RTMemAlloc(ulReqBytes);
+ if (!InstanceID)
+ return NULL;
+
+ cr = CM_Get_Device_ID(ParentInst, InstanceID, ulReqBytes, 0);
+ if (cr != CR_SUCCESS)
+ {
+ LogRelFunc(("Failed to get device ID (DevInst=%X), error %ld\n", DevInst, GetLastError()));
+ RTMemFree(InstanceID);
+ return NULL;
+ }
+
+ return InstanceID;
+}
+
+/* Process a single USB device that's being enumerated and grab its hub-specific data. */
+static int usbLibDevGetDevice(LPCSTR lpcszHubFile, ULONG iPort, LPCSTR lpcszLocation, LPCSTR lpcszDriverKey, PUSBDEVICE *ppDevs, uint32_t *pcDevs)
+{
+ HANDLE HubDevice;
+ BYTE abConBuf[sizeof(USB_NODE_CONNECTION_INFORMATION_EX)];
+ PUSB_NODE_CONNECTION_INFORMATION_EX pConInfo = PUSB_NODE_CONNECTION_INFORMATION_EX(abConBuf);
+ int rc = VINF_SUCCESS;
+ DWORD cbReturned = 0;
+
+ /* Validate inputs. */
+ if ((iPort < 1) || (iPort > 255))
+ {
+ LogRelFunc(("Port index out of range (%u)\n", iPort));
+ return VERR_INVALID_PARAMETER;
+ }
+ if (!lpcszHubFile)
+ {
+ LogRelFunc(("Hub path is NULL!\n"));
+ return VERR_INVALID_PARAMETER;
+ }
+ if (!lpcszLocation)
+ {
+ LogRelFunc(("Location NULL!\n"));
+ return VERR_INVALID_PARAMETER;
+ }
+
+ /* Try opening the hub file so we can send IOCTLs to it. */
+ HubDevice = CreateFile(lpcszHubFile, GENERIC_WRITE, FILE_SHARE_WRITE,
+ NULL, OPEN_EXISTING, 0, NULL);
+ if (HubDevice == INVALID_HANDLE_VALUE)
+ {
+ LogRelFunc(("Failed to open hub `%s' (dwErr=%u)\n", lpcszHubFile, GetLastError()));
+ return VERR_FILE_NOT_FOUND;
+ }
+
+ /* The shenanigans with abConBuf are due to USB_NODE_CONNECTION_INFORMATION_EX
+ * containing a zero-sized array, triggering compiler warnings.
+ */
+ memset(pConInfo, 0, sizeof(abConBuf));
+ pConInfo->ConnectionIndex = iPort;
+
+ /* We expect that IOCTL_USB_GET_NODE_CONNECTION_INFORMATION_EX is always available
+ * on any supported Windows version and hardware.
+ * NB: IOCTL_USB_GET_NODE_CONNECTION_INFORMATION_EX_V2 is Win8 and later only.
+ */
+ if (!DeviceIoControl(HubDevice, IOCTL_USB_GET_NODE_CONNECTION_INFORMATION_EX,
+ pConInfo, sizeof(abConBuf), pConInfo, sizeof(abConBuf),
+ &cbReturned, NULL))
+ {
+ DWORD dwErr = GetLastError(); NOREF(dwErr);
+ LogRel(("IOCTL_USB_GET_NODE_CONNECTION_INFORMATION_EX failed (dwErr=%u) on hub %s, port %d\n", dwErr, lpcszHubFile, iPort));
+ AssertMsg(dwErr == ERROR_DEVICE_NOT_CONNECTED, (__FUNCTION__": DeviceIoControl failed dwErr (%u)\n", dwErr));
+ CloseHandle(HubDevice);
+ return VERR_GENERAL_FAILURE;
+ }
+
+ if (pConInfo->ConnectionStatus != DeviceConnected)
+ {
+ /* Ignore this, can't do anything with it. */
+ LogFunc(("Device is not connected, skipping.\n"));
+ CloseHandle(HubDevice);
+ return VINF_SUCCESS;
+ }
+
+ if (pConInfo->DeviceIsHub)
+ {
+ /* We're ignoring hubs, just skip this. */
+ LogFunc(("Device is a hub, skipping.\n"));
+ CloseHandle(HubDevice);
+ return VINF_SUCCESS;
+ }
+
+ PUSB_CONFIGURATION_DESCRIPTOR pCfgDr = NULL;
+ PVBOXUSB_STRING_DR_ENTRY pList = NULL;
+ rc = usbLibDevCfgDrGet(HubDevice, lpcszHubFile, iPort, 0, &pCfgDr);
+ if (pCfgDr)
+ {
+ rc = usbLibDevStrDrEntryGetAll(HubDevice, lpcszHubFile, iPort, &pConInfo->DeviceDescriptor, pCfgDr, &pList);
+#ifdef VBOX_WITH_ANNOYING_USB_ASSERTIONS
+ AssertRC(rc); // this can fail if device suspended
+#endif
+ }
+
+ /* At this point we're done with the hub device. */
+ CloseHandle(HubDevice);
+
+ PUSBDEVICE pDev = (PUSBDEVICE)RTMemAllocZ(sizeof (*pDev));
+ if (RT_LIKELY(pDev))
+ {
+ rc = usbLibDevPopulate(pDev, pConInfo, iPort, lpcszLocation, lpcszDriverKey, lpcszHubFile, pList);
+ if (RT_SUCCESS(rc))
+ {
+ pDev->pNext = *ppDevs;
+ *ppDevs = pDev;
+ ++*pcDevs;
+ }
+ else
+ RTMemFree(pDev);
+ }
+ else
+ rc = VERR_NO_MEMORY;
+
+ if (pCfgDr)
+ usbLibDevCfgDrFree(pCfgDr);
+ if (pList)
+ usbLibDevStrDrEntryFreeList(pList);
+
+ return rc;
+}
+
+
+/*
+ * Enumerate the USB devices in the host system. Since we do not care about the hierarchical
+ * structure of root hubs, other hubs, and devices, we just ask the USB PnP enumerator to
+ * give us all it has. This includes hubs (though not root hubs), as well as multiple child
+ * interfaces of multi-interface USB devices, which we filter out. It also includes USB
+ * devices with no driver, which is notably something we cannot get by enumerating via
+ * GUID_DEVINTERFACE_USB_DEVICE.
+ *
+ * This approach also saves us some trouble relative to enumerating devices via hub IOCTLs and
+ * then hunting through the PnP manager to find them. Instead, we look up the device's parent
+ * which (for devices we're interested in) is always a hub, and that allows us to obtain
+ * USB-specific data (descriptors, speeds, etc.) when combined with the devices PnP "address"
+ * (USB port on parent hub).
+ *
+ * NB: Every USB device known to the Windows PnP Manager will have a device instance ID. Typically
+ * it also has a DriverKey but only if it has a driver installed. Hence we ignore the DriverKey, at
+ * least prior to capturing (once VBoxUSB.sys is installed, a DriverKey must by definition be
+ * present). Also note that the device instance ID changes for captured devices since we change
+ * their USB VID/PID, though it is unique at any given point.
+ *
+ * The location information should be a reliable way of identifying a device and does not change
+ * with driver installs, capturing, etc. USB device location information is only available on
+ * Windows Vista and later; earlier Windows version had no reliable way of cross-referencing the
+ * USB IOCTL and PnP Manager data.
+ */
+static int usbLibEnumDevices(PUSBDEVICE *ppDevs, uint32_t *pcDevs)
+{
+ HDEVINFO InfoSet;
+ DWORD DeviceIndex;
+ LPDWORD Address;
+ SP_DEVINFO_DATA DeviceData;
+ LPCSTR ParentInstID;
+ LPCSTR HubPath = NULL;
+ LPCSTR Location;
+ LPCSTR DriverKey;
+
+ /* Ask the USB PnP enumerator for all it has. */
+ InfoSet = SetupDiGetClassDevs(NULL, "USB", NULL, DIGCF_ALLCLASSES | DIGCF_PRESENT);
+
+ memset(&DeviceData, 0, sizeof(DeviceData));
+ DeviceData.cbSize = sizeof(DeviceData);
+ DeviceIndex = 0;
+
+ /* Enumerate everything in the info set. */
+ while (SetupDiEnumDeviceInfo(InfoSet, DeviceIndex, &DeviceData))
+ {
+ /* Use the CM API to get the parent instance ID. */
+ ParentInstID = usbLibGetParentInstanceID(DeviceData.DevInst);
+
+ /* Now figure out the hub's file path fron the instance ID, if there is one. */
+ if (ParentInstID)
+ HubPath = usbLibGetHubPathFromInstanceID(ParentInstID);
+
+ /* If there's no hub interface on the parent, then this might be a child
+ * device of a multi-interface device. Either way, we're not interested.
+ */
+ if (HubPath)
+ {
+ /* The location information uniquely identifies the USB device, (hub/port). */
+ Location = (LPCSTR)usbLibGetRegistryProperty(InfoSet, &DeviceData, SPDRP_LOCATION_PATHS);
+
+ /* The software key aka DriverKey. This will be NULL for devices with no driver
+ * and allows us to distinguish between 'busy' (driver installed) and 'available'
+ * (no driver) devices.
+ */
+ DriverKey = (LPCSTR)usbLibGetRegistryProperty(InfoSet, &DeviceData, SPDRP_DRIVER);
+
+ /* The device's PnP Manager "address" is the port number on the parent hub. */
+ Address = (LPDWORD)usbLibGetRegistryProperty(InfoSet, &DeviceData, SPDRP_ADDRESS);
+ if (Address && Location) /* NB: DriverKey may be NULL! */
+ {
+ usbLibDevGetDevice(HubPath, *Address, Location, DriverKey, ppDevs, pcDevs);
+ }
+ RTMemFree((void *)HubPath);
+
+ if (Location)
+ RTMemFree((void *)Location);
+ if (DriverKey)
+ RTMemFree((void *)DriverKey);
+ if (Address)
+ RTMemFree((void *)Address);
+ }
+
+ /* Clean up after this device. */
+ if (ParentInstID)
+ RTMemFree((void *)ParentInstID);
+
+ ++DeviceIndex;
+ memset(&DeviceData, 0, sizeof(DeviceData));
+ DeviceData.cbSize = sizeof(DeviceData);
+ }
+
+ if (InfoSet)
+ SetupDiDestroyDeviceInfoList(InfoSet);
+
+ return VINF_SUCCESS;
+}
+
+#else
+static int usbLibDevGetDevices(PUSBDEVICE *ppDevs, uint32_t *pcDevs)
+{
+ char CtlName[16];
+ int rc = VINF_SUCCESS;
+
+ for (int i = 0; i < 10; ++i)
+ {
+ RTStrPrintf(CtlName, sizeof(CtlName), "\\\\.\\HCD%d", i);
+ HANDLE hCtl = CreateFile(CtlName, GENERIC_WRITE, FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL);
+ if (hCtl != INVALID_HANDLE_VALUE)
+ {
+ char* lpszName;
+ rc = usbLibDevStrRootHubNameGet(hCtl, &lpszName);
+ AssertRC(rc);
+ if (RT_SUCCESS(rc))
+ {
+ rc = usbLibDevGetHubDevices(lpszName, ppDevs, pcDevs);
+ AssertRC(rc);
+ usbLibDevStrFree(lpszName);
+ }
+ CloseHandle(hCtl);
+ if (RT_FAILURE(rc))
+ break;
+ }
+ }
+ return VINF_SUCCESS;
+}
+#endif
+
+static int usbLibMonDevicesCmp(PUSBDEVICE pDev, PVBOXUSB_DEV pDevInfo)
+{
+ int iDiff;
+ iDiff = strcmp(pDev->pszAddress, pDevInfo->szDriverRegName);
+ return iDiff;
+}
+
+static int usbLibMonDevicesUpdate(PVBOXUSBGLOBALSTATE pGlobal, PUSBDEVICE pDevs, PVBOXUSB_DEV pDevInfos)
+{
+
+ PUSBDEVICE pDevsHead = pDevs;
+ for (; pDevInfos; pDevInfos = pDevInfos->pNext)
+ {
+ for (pDevs = pDevsHead; pDevs; pDevs = pDevs->pNext)
+ {
+ if (usbLibMonDevicesCmp(pDevs, pDevInfos))
+ continue;
+
+ if (!pDevInfos->szDriverRegName[0])
+ {
+ AssertFailed();
+ break;
+ }
+
+ USBSUP_GETDEV Dev = {0};
+ HANDLE hDev = CreateFile(pDevInfos->szName, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_WRITE | FILE_SHARE_READ, NULL,
+ OPEN_EXISTING, FILE_ATTRIBUTE_SYSTEM, NULL);
+ if (hDev == INVALID_HANDLE_VALUE)
+ {
+ AssertFailed();
+ break;
+ }
+
+ DWORD cbReturned = 0;
+ if (!DeviceIoControl(hDev, SUPUSB_IOCTL_GET_DEVICE, &Dev, sizeof (Dev), &Dev, sizeof (Dev), &cbReturned, NULL))
+ {
+ DWORD dwErr = GetLastError();
+#ifdef VBOX_WITH_ANNOYING_USB_ASSERTIONS
+ /* ERROR_DEVICE_NOT_CONNECTED -> device was removed just now */
+ AsserFailed();
+#endif
+ LogRelFunc(("SUPUSB_IOCTL_GET_DEVICE failed on '%s' (dwErr=%u)!\n", pDevInfos->szName, dwErr));
+ CloseHandle(hDev);
+ break;
+ }
+
+ /* we must not close the handle until we request for the device state from the monitor to ensure
+ * the device handle returned by the device driver does not disappear */
+ Assert(Dev.hDevice);
+ USBSUP_GETDEV_MON MonInfo;
+ HVBOXUSBDEVUSR hDevice = Dev.hDevice;
+ if (!DeviceIoControl(pGlobal->hMonitor, SUPUSBFLT_IOCTL_GET_DEVICE, &hDevice, sizeof (hDevice), &MonInfo, sizeof (MonInfo), &cbReturned, NULL))
+ {
+ DWORD dwErr = GetLastError();
+ /* ERROR_DEVICE_NOT_CONNECTED -> device was removed just now */
+ AssertFailed();
+ LogRelFunc(("SUPUSBFLT_IOCTL_GET_DEVICE failed for '%s' (hDevice=%p, dwErr=%u)!\n", pDevInfos->szName, hDevice, dwErr));
+ CloseHandle(hDev);
+ break;
+ }
+
+ CloseHandle(hDev);
+
+ /* success!! update device info */
+ /* ensure the state returned is valid */
+ Assert( MonInfo.enmState == USBDEVICESTATE_USED_BY_HOST
+ || MonInfo.enmState == USBDEVICESTATE_USED_BY_HOST_CAPTURABLE
+ || MonInfo.enmState == USBDEVICESTATE_UNUSED
+ || MonInfo.enmState == USBDEVICESTATE_HELD_BY_PROXY
+ || MonInfo.enmState == USBDEVICESTATE_USED_BY_GUEST);
+ pDevs->enmState = MonInfo.enmState;
+
+ if (pDevs->enmState != USBDEVICESTATE_USED_BY_HOST)
+ {
+ /* only set the interface name if device can be grabbed */
+ RTStrFree(pDevs->pszAltAddress);
+ pDevs->pszAltAddress = (char*)pDevs->pszAddress;
+ pDevs->pszAddress = RTStrDup(pDevInfos->szName);
+ }
+#ifdef VBOX_WITH_ANNOYING_USB_ASSERTIONS
+ else
+ {
+ /* dbg breakpoint */
+ AssertFailed();
+ }
+#endif
+
+ /* we've found the device, break in any way */
+ break;
+ }
+ }
+
+ return VINF_SUCCESS;
+}
+
+static int usbLibGetDevices(PVBOXUSBGLOBALSTATE pGlobal, PUSBDEVICE *ppDevs, uint32_t *pcDevs)
+{
+ *ppDevs = NULL;
+ *pcDevs = 0;
+
+ LogRelFunc(("Starting USB device enumeration\n"));
+#ifdef VBOX_WITH_NEW_USB_ENUM
+ int rc = usbLibEnumDevices(ppDevs, pcDevs);
+#else
+ int rc = usbLibDevGetDevices(ppDevs, pcDevs);
+#endif
+ AssertRC(rc);
+ if (RT_SUCCESS(rc))
+ {
+ PVBOXUSB_DEV pDevInfos = NULL;
+ uint32_t cDevInfos = 0;
+#ifdef VBOX_WITH_NEW_USB_ENUM
+ rc = usbLibEnumVUsbDevices(&pDevInfos, &cDevInfos);
+#else
+ rc = usbLibVuGetDevices(&pDevInfos, &cDevInfos);
+#endif
+ AssertRC(rc);
+ if (RT_SUCCESS(rc))
+ {
+ rc = usbLibMonDevicesUpdate(pGlobal, *ppDevs, pDevInfos);
+ AssertRC(rc);
+ usbLibVuFreeDevices(pDevInfos);
+ }
+
+ LogRelFunc(("Found %u USB devices, %u captured\n", *pcDevs, cDevInfos));
+ return VINF_SUCCESS;
+ }
+ return rc;
+}
+
+AssertCompile(INFINITE == RT_INDEFINITE_WAIT);
+static int usbLibStateWaitChange(PVBOXUSBGLOBALSTATE pGlobal, RTMSINTERVAL cMillies)
+{
+ HANDLE ahEvents[] = {pGlobal->hNotifyEvent, pGlobal->hInterruptEvent};
+ DWORD dwResult = WaitForMultipleObjects(RT_ELEMENTS(ahEvents), ahEvents,
+ FALSE, /* BOOL bWaitAll */
+ cMillies);
+
+ switch (dwResult)
+ {
+ case WAIT_OBJECT_0:
+ return VINF_SUCCESS;
+ case WAIT_OBJECT_0 + 1:
+ return VERR_INTERRUPTED;
+ case WAIT_TIMEOUT:
+ return VERR_TIMEOUT;
+ default:
+ {
+ DWORD dwErr = GetLastError(); NOREF(dwErr);
+ AssertMsgFailed(("WaitForMultipleObjects failed, dwErr (%u)\n", dwErr));
+ return VERR_GENERAL_FAILURE;
+ }
+ }
+}
+
+AssertCompile(RT_INDEFINITE_WAIT == INFINITE);
+AssertCompile(sizeof (RTMSINTERVAL) == sizeof (DWORD));
+USBLIB_DECL(int) USBLibWaitChange(RTMSINTERVAL msWaitTimeout)
+{
+ return usbLibStateWaitChange(&g_VBoxUsbGlobal, msWaitTimeout);
+}
+
+static int usbLibInterruptWaitChange(PVBOXUSBGLOBALSTATE pGlobal)
+{
+ BOOL fRc = SetEvent(pGlobal->hInterruptEvent);
+ if (!fRc)
+ {
+ DWORD dwErr = GetLastError(); NOREF(dwErr);
+ AssertMsgFailed(("SetEvent failed, dwErr (%u)\n", dwErr));
+ return VERR_GENERAL_FAILURE;
+ }
+ return VINF_SUCCESS;
+}
+
+USBLIB_DECL(int) USBLibInterruptWaitChange(void)
+{
+ return usbLibInterruptWaitChange(&g_VBoxUsbGlobal);
+}
+
+/*
+USBLIB_DECL(bool) USBLibHasPendingDeviceChanges(void)
+{
+ int rc = USBLibWaitChange(0);
+ return rc == VINF_SUCCESS;
+}
+*/
+
+USBLIB_DECL(int) USBLibGetDevices(PUSBDEVICE *ppDevices, uint32_t *pcbNumDevices)
+{
+ Assert(g_VBoxUsbGlobal.hMonitor != INVALID_HANDLE_VALUE);
+ return usbLibGetDevices(&g_VBoxUsbGlobal, ppDevices, pcbNumDevices);
+}
+
+USBLIB_DECL(void *) USBLibAddFilter(PCUSBFILTER pFilter)
+{
+ USBSUP_FLTADDOUT FltAddRc;
+ DWORD cbReturned = 0;
+
+ if (g_VBoxUsbGlobal.hMonitor == INVALID_HANDLE_VALUE)
+ {
+#ifdef VBOX_WITH_ANNOYING_USB_ASSERTIONS
+ AssertFailed();
+#endif
+ return NULL;
+ }
+
+ Log(("usblibInsertFilter: Manufacturer=%s Product=%s Serial=%s\n",
+ USBFilterGetString(pFilter, USBFILTERIDX_MANUFACTURER_STR) ? USBFilterGetString(pFilter, USBFILTERIDX_MANUFACTURER_STR) : "<null>",
+ USBFilterGetString(pFilter, USBFILTERIDX_PRODUCT_STR) ? USBFilterGetString(pFilter, USBFILTERIDX_PRODUCT_STR) : "<null>",
+ USBFilterGetString(pFilter, USBFILTERIDX_SERIAL_NUMBER_STR) ? USBFilterGetString(pFilter, USBFILTERIDX_SERIAL_NUMBER_STR) : "<null>"));
+
+ if (!DeviceIoControl(g_VBoxUsbGlobal.hMonitor, SUPUSBFLT_IOCTL_ADD_FILTER,
+ (LPVOID)pFilter, sizeof(*pFilter),
+ &FltAddRc, sizeof(FltAddRc),
+ &cbReturned, NULL))
+ {
+ DWORD dwErr = GetLastError();
+ AssertFailed();
+ LogRelFunc(("SUPUSBFLT_IOCTL_ADD_FILTER failed (dwErr=%u)!\n", dwErr));
+ return NULL;
+ }
+
+ if (RT_FAILURE(FltAddRc.rc))
+ {
+ AssertFailed();
+ LogRelFunc(("Adding a USB filter failed with rc=%d!\n", FltAddRc.rc));
+ return NULL;
+ }
+
+ LogRel(("Added USB filter (ID=%u, type=%d) for device %04X:%04X rev %04X, c/s/p %02X/%02X/%02X, Manufacturer=`%s' Product=`%s' Serial=`%s'\n", FltAddRc.uId, USBFilterGetFilterType(pFilter),
+ USBFilterGetNum(pFilter, USBFILTERIDX_VENDOR_ID), USBFilterGetNum(pFilter, USBFILTERIDX_PRODUCT_ID), USBFilterGetNum(pFilter, USBFILTERIDX_DEVICE_REV),
+ USBFilterGetNum(pFilter, USBFILTERIDX_DEVICE_CLASS), USBFilterGetNum(pFilter, USBFILTERIDX_DEVICE_SUB_CLASS), USBFilterGetNum(pFilter, USBFILTERIDX_DEVICE_PROTOCOL),
+ USBFilterGetString(pFilter, USBFILTERIDX_MANUFACTURER_STR) ? USBFilterGetString(pFilter, USBFILTERIDX_MANUFACTURER_STR) : "<null>",
+ USBFilterGetString(pFilter, USBFILTERIDX_PRODUCT_STR) ? USBFilterGetString(pFilter, USBFILTERIDX_PRODUCT_STR) : "<null>",
+ USBFilterGetString(pFilter, USBFILTERIDX_SERIAL_NUMBER_STR) ? USBFilterGetString(pFilter, USBFILTERIDX_SERIAL_NUMBER_STR) : "<null>"));
+
+ return (void *)FltAddRc.uId;
+}
+
+
+USBLIB_DECL(void) USBLibRemoveFilter(void *pvId)
+{
+ uintptr_t uId;
+ DWORD cbReturned = 0;
+
+ if (g_VBoxUsbGlobal.hMonitor == INVALID_HANDLE_VALUE)
+ {
+#ifdef VBOX_WITH_ANNOYING_USB_ASSERTIONS
+ AssertFailed();
+#endif
+ return;
+ }
+
+ Log(("usblibRemoveFilter %p\n", pvId));
+
+ uId = (uintptr_t)pvId;
+ if (!DeviceIoControl(g_VBoxUsbGlobal.hMonitor, SUPUSBFLT_IOCTL_REMOVE_FILTER, &uId, sizeof(uId), NULL, 0,&cbReturned, NULL))
+ {
+ DWORD dwErr = GetLastError();
+ AssertFailed();
+ LogRelFunc(("SUPUSBFLT_IOCTL_REMOVE_FILTER failed (dwErr=%u)!\n", dwErr));
+ }
+ else
+ LogRel(("Removed USB filter ID=%u\n", uId));
+}
+
+USBLIB_DECL(int) USBLibRunFilters(void)
+{
+ DWORD cbReturned = 0;
+
+ Assert(g_VBoxUsbGlobal.hMonitor != INVALID_HANDLE_VALUE);
+
+ if (!DeviceIoControl(g_VBoxUsbGlobal.hMonitor, SUPUSBFLT_IOCTL_RUN_FILTERS,
+ NULL, 0,
+ NULL, 0,
+ &cbReturned, NULL))
+ {
+ DWORD dwErr = GetLastError();
+ AssertFailed();
+ LogRelFunc(("SUPUSBFLT_IOCTL_RUN_FILTERS failed (dwErr=%u)!\n", dwErr));
+ return RTErrConvertFromWin32(dwErr);
+ }
+
+ return VINF_SUCCESS;
+}
+
+
+static VOID CALLBACK usbLibTimerCallback(__in PVOID lpParameter, __in BOOLEAN TimerOrWaitFired) RT_NOTHROW_DEF
+{
+ RT_NOREF2(lpParameter, TimerOrWaitFired);
+ SetEvent(g_VBoxUsbGlobal.hNotifyEvent);
+}
+
+static void usbLibOnDeviceChange(void)
+{
+ /* we're getting series of events like that especially on device re-attach
+ * (i.e. first for device detach and then for device attach)
+ * unfortunately the event does not tell us what actually happened.
+ * To avoid extra notifications, we delay the SetEvent via a timer
+ * and update the timer if additional notification comes before the timer fires
+ * */
+ if (g_VBoxUsbGlobal.hTimer)
+ {
+ if (!DeleteTimerQueueTimer(g_VBoxUsbGlobal.hTimerQueue, g_VBoxUsbGlobal.hTimer, NULL))
+ {
+ DWORD dwErr = GetLastError(); NOREF(dwErr);
+ AssertMsg(dwErr == ERROR_IO_PENDING, ("DeleteTimerQueueTimer failed, dwErr (%u)\n", dwErr));
+ }
+ }
+
+ if (!CreateTimerQueueTimer(&g_VBoxUsbGlobal.hTimer, g_VBoxUsbGlobal.hTimerQueue,
+ usbLibTimerCallback,
+ NULL,
+ 500, /* ms*/
+ 0,
+ WT_EXECUTEONLYONCE))
+ {
+ DWORD dwErr = GetLastError(); NOREF(dwErr);
+ AssertMsgFailed(("CreateTimerQueueTimer failed, dwErr (%u)\n", dwErr));
+
+ /* call it directly */
+ usbLibTimerCallback(NULL, FALSE);
+ }
+}
+
+static LRESULT CALLBACK usbLibWndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
+{
+ switch (uMsg)
+ {
+ case WM_DEVICECHANGE:
+ if (wParam == DBT_DEVNODES_CHANGED)
+ {
+ /* we notify change any device arivals/removals on the system
+ * and let the client decide whether the usb change actually happened
+ * so far this is more clean than reporting events from the Monitor
+ * because monitor sees only PDO arrivals/removals,
+ * and by the time PDO is created, device can not
+ * be yet started and fully functional,
+ * so usblib won't be able to pick it up
+ * */
+
+ usbLibOnDeviceChange();
+ }
+ break;
+ case WM_DESTROY:
+ {
+ PostQuitMessage(0);
+ return 0;
+ }
+ }
+ return DefWindowProc (hwnd, uMsg, wParam, lParam);
+}
+
+/** @todo r=bird: Use an IPRT thread!! */
+static DWORD WINAPI usbLibMsgThreadProc(__in LPVOID lpParameter) RT_NOTHROW_DEF
+{
+ static LPCSTR s_szVBoxUsbWndClassName = "VBoxUsbLibClass";
+ const HINSTANCE hInstance = (HINSTANCE)GetModuleHandle(NULL);
+ RT_NOREF1(lpParameter);
+
+ Assert(g_VBoxUsbGlobal.hWnd == NULL);
+ g_VBoxUsbGlobal.hWnd = NULL;
+
+ /*
+ * Register the Window Class and create the hidden window.
+ */
+ WNDCLASS wc;
+ wc.style = 0;
+ wc.lpfnWndProc = usbLibWndProc;
+ wc.cbClsExtra = 0;
+ wc.cbWndExtra = sizeof(void *);
+ wc.hInstance = hInstance;
+ wc.hIcon = NULL;
+ wc.hCursor = NULL;
+ wc.hbrBackground = (HBRUSH)(COLOR_BACKGROUND + 1);
+ wc.lpszMenuName = NULL;
+ wc.lpszClassName = s_szVBoxUsbWndClassName;
+ ATOM atomWindowClass = RegisterClass(&wc);
+ if (atomWindowClass != 0)
+ g_VBoxUsbGlobal.hWnd = CreateWindowEx(WS_EX_TOOLWINDOW | WS_EX_TRANSPARENT | WS_EX_TOPMOST,
+ s_szVBoxUsbWndClassName, s_szVBoxUsbWndClassName,
+ WS_POPUPWINDOW,
+ -200, -200, 100, 100, NULL, NULL, hInstance, NULL);
+ else
+ AssertMsgFailed(("RegisterClass failed, last error %u\n", GetLastError()));
+
+ /*
+ * Signal the creator thread.
+ */
+ ASMCompilerBarrier();
+ SetEvent(g_VBoxUsbGlobal.hNotifyEvent);
+
+ if (g_VBoxUsbGlobal.hWnd)
+ {
+ /* Make sure it's really hidden. */
+ SetWindowPos(g_VBoxUsbGlobal.hWnd, HWND_TOPMOST, -200, -200, 0, 0,
+ SWP_NOACTIVATE | SWP_HIDEWINDOW | SWP_NOCOPYBITS | SWP_NOREDRAW | SWP_NOSIZE);
+
+ /*
+ * The message pump.
+ */
+ MSG msg;
+ BOOL fRet;
+ while ((fRet = GetMessage(&msg, NULL, 0, 0)) > 0)
+ {
+ TranslateMessage(&msg);
+ DispatchMessage(&msg);
+ }
+ Assert(fRet >= 0);
+ }
+
+ if (atomWindowClass != NULL)
+ UnregisterClass(s_szVBoxUsbWndClassName, hInstance);
+
+ return 0;
+}
+
+
+/**
+ * Initialize the USB library
+ *
+ * @returns VBox status code.
+ */
+USBLIB_DECL(int) USBLibInit(void)
+{
+ int rc = VERR_GENERAL_FAILURE;
+
+ Log(("usbproxy: usbLibInit\n"));
+
+ RT_ZERO(g_VBoxUsbGlobal);
+ g_VBoxUsbGlobal.hMonitor = INVALID_HANDLE_VALUE;
+
+ /*
+ * Create the notification and interrupt event before opening the device.
+ */
+ g_VBoxUsbGlobal.hNotifyEvent = CreateEvent(NULL, /* LPSECURITY_ATTRIBUTES lpEventAttributes */
+ FALSE, /* BOOL bManualReset */
+ FALSE, /* set to false since it will be initially used for notification thread startup sync */
+ NULL /* LPCTSTR lpName */);
+ if (g_VBoxUsbGlobal.hNotifyEvent)
+ {
+ g_VBoxUsbGlobal.hInterruptEvent = CreateEvent(NULL, /* LPSECURITY_ATTRIBUTES lpEventAttributes */
+ FALSE, /* BOOL bManualReset */
+ FALSE, /* BOOL bInitialState */
+ NULL /* LPCTSTR lpName */);
+ if (g_VBoxUsbGlobal.hInterruptEvent)
+ {
+ /*
+ * Open the USB monitor device, starting if needed.
+ */
+ g_VBoxUsbGlobal.hMonitor = CreateFile(USBMON_DEVICE_NAME,
+ GENERIC_READ | GENERIC_WRITE,
+ FILE_SHARE_READ | FILE_SHARE_WRITE,
+ NULL,
+ OPEN_EXISTING,
+ FILE_ATTRIBUTE_SYSTEM,
+ NULL);
+
+ if (g_VBoxUsbGlobal.hMonitor == INVALID_HANDLE_VALUE)
+ {
+ HRESULT hr = VBoxDrvCfgSvcStart(USBMON_SERVICE_NAME_W);
+ if (hr == S_OK)
+ {
+ g_VBoxUsbGlobal.hMonitor = CreateFile(USBMON_DEVICE_NAME,
+ GENERIC_READ | GENERIC_WRITE,
+ FILE_SHARE_READ | FILE_SHARE_WRITE,
+ NULL,
+ OPEN_EXISTING,
+ FILE_ATTRIBUTE_SYSTEM,
+ NULL);
+ if (g_VBoxUsbGlobal.hMonitor == INVALID_HANDLE_VALUE)
+ {
+ DWORD dwErr = GetLastError();
+ LogRelFunc(("CreateFile failed (dwErr=%u) for `%s'\n", dwErr, USBMON_DEVICE_NAME));
+ rc = VERR_FILE_NOT_FOUND;
+ }
+ }
+ }
+
+ if (g_VBoxUsbGlobal.hMonitor != INVALID_HANDLE_VALUE)
+ {
+ /*
+ * Check the USB monitor version.
+ *
+ * Drivers are backwards compatible within the same major
+ * number. We consider the minor version number this library
+ * is compiled with to be the minimum required by the driver.
+ * This is by reasoning that the library uses the full feature
+ * set of the driver it's written for.
+ */
+ USBSUP_VERSION Version = {0};
+ DWORD cbReturned = 0;
+ if (DeviceIoControl(g_VBoxUsbGlobal.hMonitor, SUPUSBFLT_IOCTL_GET_VERSION,
+ NULL, 0,
+ &Version, sizeof (Version),
+ &cbReturned, NULL))
+ {
+ if ( Version.u32Major == USBMON_MAJOR_VERSION
+#if USBMON_MINOR_VERSION != 0
+ && Version.u32Minor >= USBMON_MINOR_VERSION
+#endif
+ )
+ {
+ /*
+ * We can not use USB Mon for reliable device add/remove tracking
+ * since once USB Mon is notified about PDO creation and/or IRP_MN_START_DEVICE,
+ * the function device driver may still do some initialization, which might result in
+ * notifying too early.
+ * Instead we use WM_DEVICECHANGE + DBT_DEVNODES_CHANGED to make Windows notify us about
+ * device arivals/removals.
+ * Since WM_DEVICECHANGE is a window message, create a dedicated thread to be used for WndProc and stuff.
+ * The thread would create a window, track windows messages and call usbLibOnDeviceChange on WM_DEVICECHANGE arrival.
+ * See comments in usbLibOnDeviceChange function for detail about using the timer queue.
+ */
+ g_VBoxUsbGlobal.hTimerQueue = CreateTimerQueue();
+ if (g_VBoxUsbGlobal.hTimerQueue)
+ {
+/** @todo r=bird: Which lunatic used CreateThread here?!?
+ * Only the CRT uses CreateThread. */
+ g_VBoxUsbGlobal.hThread = CreateThread(
+ NULL, /*__in_opt LPSECURITY_ATTRIBUTES lpThreadAttributes, */
+ 0, /*__in SIZE_T dwStackSize, */
+ usbLibMsgThreadProc, /*__in LPTHREAD_START_ROUTINE lpStartAddress,*/
+ NULL, /*__in_opt LPVOID lpParameter,*/
+ 0, /*__in DWORD dwCreationFlags,*/
+ NULL /*__out_opt LPDWORD lpThreadId*/
+ );
+ if (g_VBoxUsbGlobal.hThread)
+ {
+ DWORD dwResult = WaitForSingleObject(g_VBoxUsbGlobal.hNotifyEvent, INFINITE);
+ Assert(dwResult == WAIT_OBJECT_0);
+ if (g_VBoxUsbGlobal.hWnd)
+ {
+ /*
+ * We're DONE!
+ *
+ * Just ensure that the event is set so the
+ * first "wait change" request is processed.
+ */
+ SetEvent(g_VBoxUsbGlobal.hNotifyEvent);
+ return VINF_SUCCESS;
+ }
+
+ dwResult = WaitForSingleObject(g_VBoxUsbGlobal.hThread, INFINITE);
+ Assert(dwResult == WAIT_OBJECT_0);
+ BOOL fRc = CloseHandle(g_VBoxUsbGlobal.hThread); NOREF(fRc);
+ DWORD dwErr = GetLastError(); NOREF(dwErr);
+ AssertMsg(fRc, ("CloseHandle for hThread failed (dwErr=%u)\n", dwErr));
+ g_VBoxUsbGlobal.hThread = INVALID_HANDLE_VALUE;
+ }
+ else
+ {
+ DWORD dwErr = GetLastError(); NOREF(dwErr);
+ AssertMsgFailed(("CreateThread failed, (dwErr=%u)\n", dwErr));
+ rc = VERR_GENERAL_FAILURE;
+ }
+
+ DeleteTimerQueueEx(g_VBoxUsbGlobal.hTimerQueue, INVALID_HANDLE_VALUE /* see term */);
+ g_VBoxUsbGlobal.hTimerQueue = NULL;
+ }
+ else
+ {
+ DWORD dwErr = GetLastError(); NOREF(dwErr);
+ AssertMsgFailed(("CreateTimerQueue failed (dwErr=%u)\n", dwErr));
+ }
+ }
+ else
+ {
+ LogRelFunc(("USB Monitor driver version mismatch! driver=%u.%u library=%u.%u\n",
+ Version.u32Major, Version.u32Minor, USBMON_MAJOR_VERSION, USBMON_MINOR_VERSION));
+#ifdef VBOX_WITH_ANNOYING_USB_ASSERTIONS
+ AssertFailed();
+#endif
+ rc = VERR_VERSION_MISMATCH;
+ }
+ }
+ else
+ {
+ DWORD dwErr = GetLastError(); NOREF(dwErr);
+ LogRelFunc(("SUPUSBFLT_IOCTL_GET_VERSION failed (dwErr=%u)\n", dwErr));
+ AssertFailed();
+ rc = VERR_VERSION_MISMATCH;
+ }
+
+ CloseHandle(g_VBoxUsbGlobal.hMonitor);
+ g_VBoxUsbGlobal.hMonitor = INVALID_HANDLE_VALUE;
+ }
+ else
+ {
+ LogRelFunc(("USB Service not found\n"));
+#ifdef VBOX_WITH_ANNOYING_USB_ASSERTIONS
+ AssertFailed();
+#endif
+ rc = VERR_FILE_NOT_FOUND;
+ }
+
+ CloseHandle(g_VBoxUsbGlobal.hInterruptEvent);
+ g_VBoxUsbGlobal.hInterruptEvent = NULL;
+ }
+ else
+ {
+ AssertMsgFailed(("CreateEvent for InterruptEvent failed (dwErr=%u)\n", GetLastError()));
+ rc = VERR_GENERAL_FAILURE;
+ }
+
+ CloseHandle(g_VBoxUsbGlobal.hNotifyEvent);
+ g_VBoxUsbGlobal.hNotifyEvent = NULL;
+ }
+ else
+ {
+ AssertMsgFailed(("CreateEvent for NotifyEvent failed (dwErr=%u)\n", GetLastError()));
+ rc = VERR_GENERAL_FAILURE;
+ }
+
+ /* since main calls us even if USBLibInit fails,
+ * we use hMonitor == INVALID_HANDLE_VALUE as a marker to indicate whether the lib is inited */
+
+ Assert(RT_FAILURE(rc));
+ return rc;
+}
+
+
+/**
+ * Terminate the USB library
+ *
+ * @returns VBox status code.
+ */
+USBLIB_DECL(int) USBLibTerm(void)
+{
+ if (g_VBoxUsbGlobal.hMonitor == INVALID_HANDLE_VALUE)
+ {
+ Assert(g_VBoxUsbGlobal.hInterruptEvent == NULL);
+ Assert(g_VBoxUsbGlobal.hNotifyEvent == NULL);
+ return VINF_SUCCESS;
+ }
+
+ BOOL fRc;
+ fRc = PostMessage(g_VBoxUsbGlobal.hWnd, WM_CLOSE, 0, 0);
+ AssertMsg(fRc, ("PostMessage for hWnd failed (dwErr=%u)\n", GetLastError()));
+
+ if (g_VBoxUsbGlobal.hThread != NULL)
+ {
+ DWORD dwResult = WaitForSingleObject(g_VBoxUsbGlobal.hThread, INFINITE);
+ Assert(dwResult == WAIT_OBJECT_0); NOREF(dwResult);
+ fRc = CloseHandle(g_VBoxUsbGlobal.hThread);
+ AssertMsg(fRc, ("CloseHandle for hThread failed (dwErr=%u)\n", GetLastError()));
+ }
+
+ if (g_VBoxUsbGlobal.hTimer)
+ {
+ fRc = DeleteTimerQueueTimer(g_VBoxUsbGlobal.hTimerQueue, g_VBoxUsbGlobal.hTimer,
+ INVALID_HANDLE_VALUE); /* <-- to block until the timer is completed */
+ AssertMsg(fRc, ("DeleteTimerQueueTimer failed (dwErr=%u)\n", GetLastError()));
+ }
+
+ if (g_VBoxUsbGlobal.hTimerQueue)
+ {
+ fRc = DeleteTimerQueueEx(g_VBoxUsbGlobal.hTimerQueue,
+ INVALID_HANDLE_VALUE); /* <-- to block until all timers are completed */
+ AssertMsg(fRc, ("DeleteTimerQueueEx failed (dwErr=%u)\n", GetLastError()));
+ }
+
+ fRc = CloseHandle(g_VBoxUsbGlobal.hMonitor);
+ AssertMsg(fRc, ("CloseHandle for hMonitor failed (dwErr=%u)\n", GetLastError()));
+ g_VBoxUsbGlobal.hMonitor = INVALID_HANDLE_VALUE;
+
+ fRc = CloseHandle(g_VBoxUsbGlobal.hInterruptEvent);
+ AssertMsg(fRc, ("CloseHandle for hInterruptEvent failed (dwErr=%u)\n", GetLastError()));
+ g_VBoxUsbGlobal.hInterruptEvent = NULL;
+
+ fRc = CloseHandle(g_VBoxUsbGlobal.hNotifyEvent);
+ AssertMsg(fRc, ("CloseHandle for hNotifyEvent failed (dwErr=%u)\n", GetLastError()));
+ g_VBoxUsbGlobal.hNotifyEvent = NULL;
+
+ return VINF_SUCCESS;
+}
+
diff --git a/src/VBox/HostDrivers/VBoxUSB/win/mon/Makefile.kup b/src/VBox/HostDrivers/VBoxUSB/win/mon/Makefile.kup
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/src/VBox/HostDrivers/VBoxUSB/win/mon/Makefile.kup
diff --git a/src/VBox/HostDrivers/VBoxUSB/win/mon/VBoxUSBMon.inf b/src/VBox/HostDrivers/VBoxUSB/win/mon/VBoxUSBMon.inf
new file mode 100644
index 00000000..d54bb101
--- /dev/null
+++ b/src/VBox/HostDrivers/VBoxUSB/win/mon/VBoxUSBMon.inf
@@ -0,0 +1,98 @@
+; $Id: VBoxUSBMon.inf $
+;; @file
+; VBox USB Monitor driver - Installation file
+;
+
+;
+; Copyright (C) 2011-2023 Oracle and/or its affiliates.
+;
+; This file is part of VirtualBox base platform packages, as
+; available from https://www.virtualbox.org.
+;
+; This program is free software; you can redistribute it and/or
+; modify it under the terms of the GNU General Public License
+; as published by the Free Software Foundation, in version 3 of the
+; License.
+;
+; This program is distributed in the hope that it will be useful, but
+; WITHOUT ANY WARRANTY; without even the implied warranty of
+; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+; General Public License for more details.
+;
+; You should have received a copy of the GNU General Public License
+; along with this program; if not, see <https://www.gnu.org/licenses>.
+;
+; The contents of this file may alternatively be used under the terms
+; of the Common Development and Distribution License Version 1.0
+; (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+; in the VirtualBox distribution, in which case the provisions of the
+; CDDL are applicable instead of those of the GPL.
+;
+; You may elect to license modified versions of this file under the
+; terms and conditions of either the GPL or the CDDL or both.
+;
+; SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+;
+
+[Version]
+Signature="$Windows NT$"
+Class=System
+ClassGuid={4d36e97d-e325-11ce-bfc1-08002be10318}
+Provider=%ORACLE%
+;edit-DriverVer=08/26/2008,2.00.0000
+DriverPackageType=KernelService
+;cat CatalogFile=VBoxUSBMon.cat
+
+[DestinationDirs]
+DefaultDestDir = 12
+
+[DefaultInstall@DOT-NT-ARCH@]
+CopyFiles=VBoxUSBMon_CopyFiles
+
+[DefaultInstall@DOT-NT-ARCH@.Services]
+AddService=VBoxUSBMon,0x00000002,VBoxUSBMon_Service,VBoxUSBMon_AddEventLog
+
+;; Cannot get this to work (same works fine for VBoxDrv):
+;; [Manufacturer]
+;; %ORACLE%=VBoxUSBMon@COMMA-NT-ARCH@
+;;
+;; ; Models section (referenced by [Manufacturer]).
+;; [VBoxUSBMon@DOT-NT-ARCH@]
+;; %VBoxUSBMon.DRVDESC%=VBoxUSBMon_Install,root\VBoxUSBMon
+;;
+;; [VBoxUSBMon_Install@DOT-NT-ARCH@]
+;; CopyFiles=VBoxUSBMon_CopyFiles
+;;
+;; [VBoxUSBMon_Install@DOT-NT-ARCH@.Services]
+;; AddService=VBoxUSBMon,0x00000002,VBoxUSBMon_Service,VBoxUSBMon_AddEventLog
+
+[SourceDisksFiles]
+VBoxUSBMon.sys=1
+
+[SourceDisksNames]
+1=%VBoxUSBMon.DSKDESC%,
+
+[VBoxUSBMon_CopyFiles]
+VBoxUSBMon.sys
+
+[VBoxUSBMon_Service]
+DisplayName = %VBoxUSBMon.SVCDESC%
+ServiceType = 1 ; SERVICE_KERNEL_DRIVER
+;StartType = 3 ; SERVICE_DEMAND_START
+StartType = 1 ; autostart to fix Vista problem
+ErrorControl = 1 ; SERVICE_ERROR_NORMAL
+ServiceBinary = %12%\VBoxUSBMon.sys
+
+[VBoxUSBMon_AddEventLog]
+AddReg = VBoxUSBMon_AddEventLogRegistry
+
+[VBoxUSBMon_AddEventLogRegistry]
+HKR,,EventMessageFile,0x00020000,"%%SystemRoot%%\System32\IoLogMsg.dll"
+HKR,,TypesSupported,0x00010001,7
+
+[Strings]
+ORACLE = "Oracle Corporation"
+VBoxUSBMon.SVCDESC = "VirtualBox USB Monitor Service"
+VBoxUSBMon.DRVDESC = "VirtualBox USB Monitor Driver"
+VBoxUSBMon.DSKDESC = "VirtualBox USB Monitor Driver Installation Disk"
+
diff --git a/src/VBox/HostDrivers/VBoxUSB/win/mon/VBoxUsbFlt.cpp b/src/VBox/HostDrivers/VBoxUSB/win/mon/VBoxUsbFlt.cpp
new file mode 100644
index 00000000..23d4d088
--- /dev/null
+++ b/src/VBox/HostDrivers/VBoxUSB/win/mon/VBoxUsbFlt.cpp
@@ -0,0 +1,1763 @@
+/* $Id: VBoxUsbFlt.cpp $ */
+/** @file
+ * VBox USB Monitor Device Filtering functionality
+ */
+/*
+ * Copyright (C) 2011-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include "VBoxUsbMon.h"
+#include "../cmn/VBoxUsbTool.h"
+
+#include <VBox/cdefs.h>
+#include <VBox/types.h>
+#include <iprt/process.h>
+#include <iprt/assert.h>
+#include <iprt/errcore.h>
+
+#include <iprt/assert.h>
+
+#pragma warning(disable : 4200)
+#include "usbdi.h"
+#pragma warning(default : 4200)
+#include "usbdlib.h"
+#include "VBoxUSBFilterMgr.h"
+#include <VBox/usblib.h>
+#include <devguid.h>
+#include <devpkey.h>
+
+
+/* We should be including ntifs.h but that's not as easy as it sounds. */
+extern "C" {
+NTKERNELAPI PDEVICE_OBJECT IoGetDeviceAttachmentBaseRef(__in PDEVICE_OBJECT DeviceObject);
+}
+
+/*
+ * state transitions:
+ *
+ * (we are not filtering this device )
+ * ADDED --> UNCAPTURED ------------------------------->-
+ * | |
+ * | (we are filtering this device, | (the device is being
+ * | waiting for our device driver | re-plugged to perform
+ * | to pick it up) | capture-uncapture transition)
+ * |-> CAPTURING -------------------------------->|---> REPLUGGING -----
+ * ^ | (device driver picked | |
+ * | | up the device) | (remove cased | (device is removed
+ * | ->---> CAPTURED ---------------------->| by "real" removal | the device info is removed form the list)
+ * | | |------------------->->--> REMOVED
+ * | | |
+ * |-----------<->---> USED_BY_GUEST ------->|
+ * | |
+ * |------------------------<-
+ *
+ * NOTE: the order of enums DOES MATTER!!
+ * Do not blindly modify!! as the code assumes the state is ordered this way.
+ */
+typedef enum
+{
+ VBOXUSBFLT_DEVSTATE_UNKNOWN = 0,
+ VBOXUSBFLT_DEVSTATE_REMOVED,
+ VBOXUSBFLT_DEVSTATE_REPLUGGING,
+ VBOXUSBFLT_DEVSTATE_ADDED,
+ VBOXUSBFLT_DEVSTATE_UNCAPTURED,
+ VBOXUSBFLT_DEVSTATE_CAPTURING,
+ VBOXUSBFLT_DEVSTATE_CAPTURED,
+ VBOXUSBFLT_DEVSTATE_USED_BY_GUEST,
+ VBOXUSBFLT_DEVSTATE_32BIT_HACK = 0x7fffffff
+} VBOXUSBFLT_DEVSTATE;
+
+typedef struct VBOXUSBFLT_DEVICE
+{
+ LIST_ENTRY GlobalLe;
+ /* auxiliary list to be used for gathering devices to be re-plugged
+ * only thread that puts the device to the REPLUGGING state can use this list */
+ LIST_ENTRY RepluggingLe;
+ /* Owning session. Each matched device has an owning session. */
+ struct VBOXUSBFLTCTX *pOwner;
+ /* filter id - if NULL AND device has an owner - the filter is destroyed */
+ uintptr_t uFltId;
+ /* true iff device is filtered with a one-shot filter */
+ bool fIsFilterOneShot;
+ /* true if descriptors could not be read and only inferred from PnP Manager data */
+ bool fInferredDesc;
+ /* The device state. If the non-owner session is requesting the state while the device is grabbed,
+ * the USBDEVICESTATE_USED_BY_HOST is returned. */
+ VBOXUSBFLT_DEVSTATE enmState;
+ volatile uint32_t cRefs;
+ PDEVICE_OBJECT Pdo;
+ uint16_t idVendor;
+ uint16_t idProduct;
+ uint16_t bcdDevice;
+ uint16_t bPort;
+ uint8_t bClass;
+ uint8_t bSubClass;
+ uint8_t bProtocol;
+ char szSerial[MAX_USB_SERIAL_STRING];
+ char szMfgName[MAX_USB_SERIAL_STRING];
+ char szProduct[MAX_USB_SERIAL_STRING];
+ WCHAR szLocationPath[768];
+#if 0
+ char szDrvKeyName[512];
+ BOOLEAN fHighSpeed;
+#endif
+} VBOXUSBFLT_DEVICE, *PVBOXUSBFLT_DEVICE;
+
+#define PVBOXUSBFLT_DEVICE_FROM_LE(_pLe) ( (PVBOXUSBFLT_DEVICE)( ((uint8_t*)(_pLe)) - RT_OFFSETOF(VBOXUSBFLT_DEVICE, GlobalLe) ) )
+#define PVBOXUSBFLT_DEVICE_FROM_REPLUGGINGLE(_pLe) ( (PVBOXUSBFLT_DEVICE)( ((uint8_t*)(_pLe)) - RT_OFFSETOF(VBOXUSBFLT_DEVICE, RepluggingLe) ) )
+#define PVBOXUSBFLTCTX_FROM_LE(_pLe) ( (PVBOXUSBFLTCTX)( ((uint8_t*)(_pLe)) - RT_OFFSETOF(VBOXUSBFLTCTX, ListEntry) ) )
+
+typedef struct VBOXUSBFLT_LOCK
+{
+ KSPIN_LOCK Lock;
+ KIRQL OldIrql;
+} VBOXUSBFLT_LOCK, *PVBOXUSBFLT_LOCK;
+
+#define VBOXUSBFLT_LOCK_INIT() \
+ KeInitializeSpinLock(&g_VBoxUsbFltGlobals.Lock.Lock)
+#define VBOXUSBFLT_LOCK_TERM() do { } while (0)
+#define VBOXUSBFLT_LOCK_ACQUIRE() \
+ KeAcquireSpinLock(&g_VBoxUsbFltGlobals.Lock.Lock, &g_VBoxUsbFltGlobals.Lock.OldIrql);
+#define VBOXUSBFLT_LOCK_RELEASE() \
+ KeReleaseSpinLock(&g_VBoxUsbFltGlobals.Lock.Lock, g_VBoxUsbFltGlobals.Lock.OldIrql);
+
+
+typedef struct VBOXUSBFLT_BLDEV
+{
+ LIST_ENTRY ListEntry;
+ uint16_t idVendor;
+ uint16_t idProduct;
+ uint16_t bcdDevice;
+} VBOXUSBFLT_BLDEV, *PVBOXUSBFLT_BLDEV;
+
+#define PVBOXUSBFLT_BLDEV_FROM_LE(_pLe) ( (PVBOXUSBFLT_BLDEV)( ((uint8_t*)(_pLe)) - RT_OFFSETOF(VBOXUSBFLT_BLDEV, ListEntry) ) )
+
+typedef struct VBOXUSBFLTGLOBALS
+{
+ LIST_ENTRY DeviceList;
+ LIST_ENTRY ContextList;
+ /* devices known to misbehave */
+ LIST_ENTRY BlackDeviceList;
+ VBOXUSBFLT_LOCK Lock;
+ /** Flag whether to force replugging a device we can't query descirptors from.
+ * Short term workaround for @bugref{9479}. */
+ ULONG dwForceReplugWhenDevPopulateFails;
+} VBOXUSBFLTGLOBALS, *PVBOXUSBFLTGLOBALS;
+static VBOXUSBFLTGLOBALS g_VBoxUsbFltGlobals;
+
+static bool vboxUsbFltBlDevMatchLocked(uint16_t idVendor, uint16_t idProduct, uint16_t bcdDevice)
+{
+ for (PLIST_ENTRY pEntry = g_VBoxUsbFltGlobals.BlackDeviceList.Flink;
+ pEntry != &g_VBoxUsbFltGlobals.BlackDeviceList;
+ pEntry = pEntry->Flink)
+ {
+ PVBOXUSBFLT_BLDEV pDev = PVBOXUSBFLT_BLDEV_FROM_LE(pEntry);
+ if (pDev->idVendor != idVendor)
+ continue;
+ if (pDev->idProduct != idProduct)
+ continue;
+ if (pDev->bcdDevice != bcdDevice)
+ continue;
+
+ return true;
+ }
+ return false;
+}
+
+static NTSTATUS vboxUsbFltBlDevAddLocked(uint16_t idVendor, uint16_t idProduct, uint16_t bcdDevice)
+{
+ if (vboxUsbFltBlDevMatchLocked(idVendor, idProduct, bcdDevice))
+ return STATUS_SUCCESS;
+ PVBOXUSBFLT_BLDEV pDev = (PVBOXUSBFLT_BLDEV)VBoxUsbMonMemAllocZ(sizeof (*pDev));
+ if (!pDev)
+ {
+ AssertFailed();
+ return STATUS_INSUFFICIENT_RESOURCES;
+ }
+
+ pDev->idVendor = idVendor;
+ pDev->idProduct = idProduct;
+ pDev->bcdDevice = bcdDevice;
+ InsertHeadList(&g_VBoxUsbFltGlobals.BlackDeviceList, &pDev->ListEntry);
+ return STATUS_SUCCESS;
+}
+
+static void vboxUsbFltBlDevClearLocked()
+{
+ PLIST_ENTRY pNext;
+ for (PLIST_ENTRY pEntry = g_VBoxUsbFltGlobals.BlackDeviceList.Flink;
+ pEntry != &g_VBoxUsbFltGlobals.BlackDeviceList;
+ pEntry = pNext)
+ {
+ pNext = pEntry->Flink;
+ VBoxUsbMonMemFree(pEntry);
+ }
+}
+
+static void vboxUsbFltBlDevPopulateWithKnownLocked()
+{
+ /* this one halts when trying to get string descriptors from it */
+ vboxUsbFltBlDevAddLocked(0x5ac, 0x921c, 0x115);
+}
+
+
+DECLINLINE(void) vboxUsbFltDevRetain(PVBOXUSBFLT_DEVICE pDevice)
+{
+ Assert(pDevice->cRefs);
+ ASMAtomicIncU32(&pDevice->cRefs);
+}
+
+static void vboxUsbFltDevDestroy(PVBOXUSBFLT_DEVICE pDevice)
+{
+ Assert(!pDevice->cRefs);
+ Assert(pDevice->enmState == VBOXUSBFLT_DEVSTATE_REMOVED);
+ VBoxUsbMonMemFree(pDevice);
+}
+
+DECLINLINE(void) vboxUsbFltDevRelease(PVBOXUSBFLT_DEVICE pDevice)
+{
+ uint32_t cRefs = ASMAtomicDecU32(&pDevice->cRefs);
+ Assert(cRefs < UINT32_MAX/2);
+ if (!cRefs)
+ {
+ vboxUsbFltDevDestroy(pDevice);
+ }
+}
+
+static void vboxUsbFltDevOwnerSetLocked(PVBOXUSBFLT_DEVICE pDevice, PVBOXUSBFLTCTX pContext, uintptr_t uFltId, bool fIsOneShot)
+{
+ ASSERT_WARN(!pDevice->pOwner, ("device 0x%p has an owner(0x%p)", pDevice, pDevice->pOwner));
+ ++pContext->cActiveFilters;
+ pDevice->pOwner = pContext;
+ pDevice->uFltId = uFltId;
+ pDevice->fIsFilterOneShot = fIsOneShot;
+}
+
+static void vboxUsbFltDevOwnerClearLocked(PVBOXUSBFLT_DEVICE pDevice)
+{
+ ASSERT_WARN(pDevice->pOwner, ("no owner for device 0x%p", pDevice));
+ --pDevice->pOwner->cActiveFilters;
+ ASSERT_WARN(pDevice->pOwner->cActiveFilters < UINT32_MAX/2, ("cActiveFilters (%d)", pDevice->pOwner->cActiveFilters));
+ pDevice->pOwner = NULL;
+ pDevice->uFltId = 0;
+}
+
+static void vboxUsbFltDevOwnerUpdateLocked(PVBOXUSBFLT_DEVICE pDevice, PVBOXUSBFLTCTX pContext, uintptr_t uFltId, bool fIsOneShot)
+{
+ if (pDevice->pOwner != pContext)
+ {
+ if (pDevice->pOwner)
+ vboxUsbFltDevOwnerClearLocked(pDevice);
+ if (pContext)
+ vboxUsbFltDevOwnerSetLocked(pDevice, pContext, uFltId, fIsOneShot);
+ }
+ else if (pContext)
+ {
+ pDevice->uFltId = uFltId;
+ pDevice->fIsFilterOneShot = fIsOneShot;
+ }
+}
+
+static PVBOXUSBFLT_DEVICE vboxUsbFltDevGetLocked(PDEVICE_OBJECT pPdo)
+{
+#ifdef VBOX_USB_WITH_VERBOSE_LOGGING
+ for (PLIST_ENTRY pEntry = g_VBoxUsbFltGlobals.DeviceList.Flink;
+ pEntry != &g_VBoxUsbFltGlobals.DeviceList;
+ pEntry = pEntry->Flink)
+ {
+ PVBOXUSBFLT_DEVICE pDevice = PVBOXUSBFLT_DEVICE_FROM_LE(pEntry);
+ for (PLIST_ENTRY pEntry2 = pEntry->Flink;
+ pEntry2 != &g_VBoxUsbFltGlobals.DeviceList;
+ pEntry2 = pEntry2->Flink)
+ {
+ PVBOXUSBFLT_DEVICE pDevice2 = PVBOXUSBFLT_DEVICE_FROM_LE(pEntry2);
+ ASSERT_WARN( pDevice->idVendor != pDevice2->idVendor
+ || pDevice->idProduct != pDevice2->idProduct
+ || pDevice->bcdDevice != pDevice2->bcdDevice, ("duplicate devices in a list!!"));
+ }
+ }
+#endif
+ for (PLIST_ENTRY pEntry = g_VBoxUsbFltGlobals.DeviceList.Flink;
+ pEntry != &g_VBoxUsbFltGlobals.DeviceList;
+ pEntry = pEntry->Flink)
+ {
+ PVBOXUSBFLT_DEVICE pDevice = PVBOXUSBFLT_DEVICE_FROM_LE(pEntry);
+ ASSERT_WARN( pDevice->enmState == VBOXUSBFLT_DEVSTATE_REPLUGGING
+ || pDevice->enmState == VBOXUSBFLT_DEVSTATE_UNCAPTURED
+ || pDevice->enmState == VBOXUSBFLT_DEVSTATE_CAPTURING
+ || pDevice->enmState == VBOXUSBFLT_DEVSTATE_CAPTURED
+ || pDevice->enmState == VBOXUSBFLT_DEVSTATE_USED_BY_GUEST,
+ ("Invalid device state(%d) for device(0x%p) PDO(0x%p)", pDevice->enmState, pDevice, pDevice->Pdo));
+ if (pDevice->Pdo == pPdo)
+ return pDevice;
+ }
+ return NULL;
+}
+
+static NTSTATUS vboxUsbFltPdoReplug(PDEVICE_OBJECT pDo)
+{
+ LOG(("Replugging PDO(0x%p)", pDo));
+ NTSTATUS Status = VBoxUsbToolIoInternalCtlSendSync(pDo, IOCTL_INTERNAL_USB_CYCLE_PORT, NULL, NULL);
+ ASSERT_WARN(Status == STATUS_SUCCESS, ("replugging PDO(0x%p) failed Status(0x%x)", pDo, Status));
+ LOG(("Replugging PDO(0x%p) done with Status(0x%x)", pDo, Status));
+ return Status;
+}
+
+static bool vboxUsbFltDevCanBeCaptured(PVBOXUSBFLT_DEVICE pDevice)
+{
+ if (pDevice->bClass == USB_DEVICE_CLASS_HUB)
+ {
+ LOG(("device (0x%p), pdo (0x%p) is a hub, can not be captured", pDevice, pDevice->Pdo));
+ return false;
+ }
+ return true;
+}
+
+static PVBOXUSBFLTCTX vboxUsbFltDevMatchLocked(PVBOXUSBFLT_DEVICE pDevice, uintptr_t *puId, bool fRemoveFltIfOneShot, bool *pfFilter, bool *pfIsOneShot)
+{
+ *puId = 0;
+ *pfFilter = false;
+ *pfIsOneShot = false;
+ if (!vboxUsbFltDevCanBeCaptured(pDevice))
+ {
+ LOG(("vboxUsbFltDevCanBeCaptured returned false"));
+ return NULL;
+ }
+
+ USBFILTER DevFlt;
+ USBFilterInit(&DevFlt, USBFILTERTYPE_CAPTURE);
+ USBFilterSetNumExact(&DevFlt, USBFILTERIDX_VENDOR_ID, pDevice->idVendor, true);
+ USBFilterSetNumExact(&DevFlt, USBFILTERIDX_PRODUCT_ID, pDevice->idProduct, true);
+ USBFilterSetNumExact(&DevFlt, USBFILTERIDX_DEVICE_REV, pDevice->bcdDevice, true);
+
+ /* If we could not read a string descriptor, don't set the filter item at all. */
+ if (pDevice->szMfgName[0])
+ USBFilterSetStringExact(&DevFlt, USBFILTERIDX_MANUFACTURER_STR, pDevice->szMfgName, true /*fMustBePresent*/, true /*fPurge*/);
+ if (pDevice->szProduct[0])
+ USBFilterSetStringExact(&DevFlt, USBFILTERIDX_PRODUCT_STR, pDevice->szProduct, true /*fMustBePresent*/, true /*fPurge*/);
+ if (pDevice->szSerial[0])
+ USBFilterSetStringExact(&DevFlt, USBFILTERIDX_SERIAL_NUMBER_STR, pDevice->szSerial, true /*fMustBePresent*/, true /*fPurge*/);
+
+ /* If device descriptor had to be inferred from PnP Manager data, the class/subclass/protocol may be wrong.
+ * When Windows reports CompatibleIDs 'USB\Class_03&SubClass_00&Prot_00', the device descriptor might be
+ * reporting class 3 (HID), *or* the device descriptor might be reporting class 0 (specified by interface)
+ * and the device's interface reporting class 3. Ignore the class/subclass/protocol in such case, since
+ * we are more or less guaranteed to rely on VID/PID anyway.
+ * See @bugref{9479}.
+ */
+ if (pDevice->fInferredDesc)
+ {
+ LOG(("Device descriptor was not read, only inferred; ignoring class/subclass/protocol!"));
+ }
+ else
+ {
+ LOG(("Setting filter class/subclass/protocol %02X/%02X/%02X\n", pDevice->bClass, pDevice->bSubClass, pDevice->bProtocol));
+ USBFilterSetNumExact(&DevFlt, USBFILTERIDX_DEVICE_CLASS, pDevice->bClass, true);
+ USBFilterSetNumExact(&DevFlt, USBFILTERIDX_DEVICE_SUB_CLASS, pDevice->bSubClass, true);
+ USBFilterSetNumExact(&DevFlt, USBFILTERIDX_DEVICE_PROTOCOL, pDevice->bProtocol, true);
+ }
+
+ /* If the port number looks valid, add it to the filter. */
+ if (pDevice->bPort)
+ {
+ LOG(("Setting filter port %04X\n", pDevice->bPort));
+ USBFilterSetNumExact(&DevFlt, USBFILTERIDX_PORT, pDevice->bPort, true);
+ }
+ else
+ LOG(("Port number not known, ignoring!"));
+
+ /* Run filters on the thing. */
+ PVBOXUSBFLTCTX pOwner = VBoxUSBFilterMatchEx(&DevFlt, puId, fRemoveFltIfOneShot, pfFilter, pfIsOneShot);
+ USBFilterDelete(&DevFlt);
+ return pOwner;
+}
+
+static void vboxUsbFltDevStateMarkReplugLocked(PVBOXUSBFLT_DEVICE pDevice)
+{
+ vboxUsbFltDevOwnerUpdateLocked(pDevice, NULL, 0, false);
+ pDevice->enmState = VBOXUSBFLT_DEVSTATE_REPLUGGING;
+}
+
+static bool vboxUsbFltDevStateIsNotFiltered(PVBOXUSBFLT_DEVICE pDevice)
+{
+ return pDevice->enmState == VBOXUSBFLT_DEVSTATE_UNCAPTURED;
+}
+
+static bool vboxUsbFltDevStateIsFiltered(PVBOXUSBFLT_DEVICE pDevice)
+{
+ return pDevice->enmState >= VBOXUSBFLT_DEVSTATE_CAPTURING;
+}
+
+static uint16_t vboxUsbParseHexNumU16(WCHAR **ppStr)
+{
+ WCHAR *pStr = *ppStr;
+ WCHAR wc;
+ uint16_t num = 0;
+ unsigned u;
+
+ for (int i = 0; i < 4; ++i)
+ {
+ if (!*pStr) /* Just in case the string is too short. */
+ break;
+
+ wc = *pStr;
+ u = wc >= 'A' ? wc - 'A' + 10 : wc - '0'; /* Hex digit to number. */
+ num |= u << (12 - 4 * i);
+ pStr++;
+ }
+ *ppStr = pStr;
+
+ return num;
+}
+
+static uint8_t vboxUsbParseHexNumU8(WCHAR **ppStr)
+{
+ WCHAR *pStr = *ppStr;
+ WCHAR wc;
+ uint16_t num = 0;
+ unsigned u;
+
+ for (int i = 0; i < 2; ++i)
+ {
+ if (!*pStr) /* Just in case the string is too short. */
+ break;
+
+ wc = *pStr;
+ u = wc >= 'A' ? wc - 'A' + 10 : wc - '0'; /* Hex digit to number. */
+ num |= u << (4 - 4 * i);
+ pStr++;
+ }
+ *ppStr = pStr;
+
+ return num;
+}
+
+static bool vboxUsbParseHardwareID(WCHAR *pchIdStr, uint16_t *pVid, uint16_t *pPid, uint16_t *pRev)
+{
+#define VID_PREFIX L"USB\\VID_"
+#define PID_PREFIX L"&PID_"
+#define REV_PREFIX L"&REV_"
+
+ *pVid = *pPid = *pRev = 0xFFFF;
+
+ /* The Hardware ID is in the format USB\VID_xxxx&PID_xxxx&REV_xxxx, with 'xxxx'
+ * being 16-bit hexadecimal numbers. The string is coming from the
+ * Windows PnP manager so OEMs should have no opportunity to mess it up.
+ */
+
+ if (wcsncmp(pchIdStr, VID_PREFIX, wcslen(VID_PREFIX)))
+ return false;
+ /* Point to the start of the vendor ID number and parse it. */
+ pchIdStr += wcslen(VID_PREFIX);
+ *pVid = vboxUsbParseHexNumU16(&pchIdStr);
+
+ if (wcsncmp(pchIdStr, PID_PREFIX, wcslen(PID_PREFIX)))
+ return false;
+ /* Point to the start of the product ID number and parse it. */
+ pchIdStr += wcslen(PID_PREFIX);
+ *pPid = vboxUsbParseHexNumU16(&pchIdStr);
+
+ /* The revision might not be there; the Windows documentation is not
+ * entirely clear if it will be always present for USB devices or not.
+ * If it's not there, still consider this a success. */
+ if (wcsncmp(pchIdStr, REV_PREFIX, wcslen(REV_PREFIX)))
+ return true;
+
+ /* Point to the start of the revision number and parse it. */
+ pchIdStr += wcslen(REV_PREFIX);
+ *pRev = vboxUsbParseHexNumU16(&pchIdStr);
+
+ return true;
+#undef VID_PREFIX
+#undef PID_PREFIX
+#undef REV_PREFIX
+}
+
+static bool vboxUsbParseCompatibleIDs(WCHAR *pchIdStr, uint8_t *pClass, uint8_t *pSubClass, uint8_t *pProt)
+{
+#define CLS_PREFIX L"USB\\Class_"
+#define SUB_PREFIX L"&SubClass_"
+#define PRO_PREFIX L"&Prot_"
+
+ *pClass = *pSubClass = *pProt = 0xFF;
+
+ /* The Compatible IDs string is in the format USB\Class_xx&SubClass_xx&Prot_xx,
+ * with 'xx' being 8-bit hexadecimal numbers. Since this string is provided by the
+ * PnP manager and USB devices always report these as part of the basic USB device
+ * descriptor, we assume all three must be present.
+ */
+
+ if (wcsncmp(pchIdStr, CLS_PREFIX, wcslen(CLS_PREFIX)))
+ return false;
+ /* Point to the start of the device class and parse it. */
+ pchIdStr += wcslen(CLS_PREFIX);
+ *pClass = vboxUsbParseHexNumU8(&pchIdStr);
+
+ if (wcsncmp(pchIdStr, SUB_PREFIX, wcslen(SUB_PREFIX)))
+ return false;
+
+ /* Point to the start of the subclass and parse it. */
+ pchIdStr += wcslen(SUB_PREFIX);
+ *pSubClass = vboxUsbParseHexNumU8(&pchIdStr);
+
+ if (wcsncmp(pchIdStr, PRO_PREFIX, wcslen(PRO_PREFIX)))
+ return false;
+
+ /* Point to the start of the protocol and parse it. */
+ pchIdStr += wcslen(PRO_PREFIX);
+ *pProt = vboxUsbParseHexNumU8(&pchIdStr);
+
+ return true;
+#undef CLS_PREFIX
+#undef SUB_PREFIX
+#undef PRO_PREFIX
+}
+
+#define VBOXUSBMON_POPULATE_REQUEST_TIMEOUT_MS 10000
+
+static NTSTATUS vboxUsbFltDevPopulate(PVBOXUSBFLT_DEVICE pDevice, PDEVICE_OBJECT pDo /*, BOOLEAN bPopulateNonFilterProps*/)
+{
+ NTSTATUS Status;
+ USB_TOPOLOGY_ADDRESS TopoAddr;
+ PUSB_DEVICE_DESCRIPTOR pDevDr = 0;
+ ULONG ulResultLen;
+ DEVPROPTYPE type;
+ WCHAR wchPropBuf[256];
+ uint16_t port;
+ bool rc;
+
+ pDevice->Pdo = pDo;
+
+ LOG(("Populating Device(0x%p) for PDO(0x%p)", pDevice, pDo));
+
+ pDevDr = (PUSB_DEVICE_DESCRIPTOR)VBoxUsbMonMemAllocZ(sizeof(*pDevDr));
+ if (pDevDr == NULL)
+ {
+ WARN(("Failed to alloc mem for urb"));
+ return STATUS_INSUFFICIENT_RESOURCES;
+ }
+
+ do
+ {
+ pDevice->fInferredDesc = false;
+ Status = VBoxUsbToolGetDescriptor(pDo, pDevDr, sizeof(*pDevDr), USB_DEVICE_DESCRIPTOR_TYPE, 0, 0, VBOXUSBMON_POPULATE_REQUEST_TIMEOUT_MS);
+ if (!NT_SUCCESS(Status))
+ {
+ uint16_t vid, pid, rev;
+ uint8_t cls, sub, prt;
+
+ WARN(("getting device descriptor failed, Status (0x%x); falling back to IoGetDeviceProperty", Status));
+
+ /* Try falling back to IoGetDevicePropertyData. */
+ Status = IoGetDevicePropertyData(pDo, &DEVPKEY_Device_HardwareIds, LOCALE_NEUTRAL, 0, sizeof(wchPropBuf), wchPropBuf, &ulResultLen, &type);
+ if (!NT_SUCCESS(Status))
+ {
+ /* This just isn't our day. We have no idea what the device is. */
+ WARN(("IoGetDevicePropertyData failed for DEVPKEY_Device_HardwareIds, Status (0x%x)", Status));
+ break;
+ }
+ rc = vboxUsbParseHardwareID(wchPropBuf, &vid, &pid, &rev);
+ if (!rc)
+ {
+ /* This *really* should not happen. */
+ WARN(("Failed to parse Hardware ID"));
+ break;
+ }
+
+ /* Now grab the Compatible IDs to get the class/subclass/protocol. */
+ Status = IoGetDevicePropertyData(pDo, &DEVPKEY_Device_CompatibleIds, LOCALE_NEUTRAL, 0, sizeof(wchPropBuf), wchPropBuf, &ulResultLen, &type);
+ if (!NT_SUCCESS(Status))
+ {
+ /* We really kind of need these. */
+ WARN(("IoGetDevicePropertyData failed for DEVPKEY_Device_CompatibleIds, Status (0x%x)", Status));
+ break;
+ }
+ rc = vboxUsbParseCompatibleIDs(wchPropBuf, &cls, &sub, &prt);
+ if (!rc)
+ {
+ /* This *really* should not happen. */
+ WARN(("Failed to parse Hardware ID"));
+ break;
+ }
+
+ LOG(("Parsed HardwareID: vid=%04X, pid=%04X, rev=%04X, class=%02X, subcls=%02X, prot=%02X", vid, pid, rev, cls, sub, prt));
+ if (vid == 0xFFFF || pid == 0xFFFF)
+ break;
+
+ LOG(("Successfully fell back to IoGetDeviceProperty result"));
+ pDevDr->idVendor = vid;
+ pDevDr->idProduct = pid;
+ pDevDr->bcdDevice = rev;
+ pDevDr->bDeviceClass = cls;
+ pDevDr->bDeviceSubClass = sub;
+ pDevDr->bDeviceProtocol = prt;
+
+ /* The USB device class/subclass/protocol may not be accurate. We have to be careful when comparing
+ * and not take mismatches too seriously.
+ */
+ pDevice->fInferredDesc = true;
+ }
+
+ /* Query the location path. The path is purely a function of the physical device location
+ * and does not change if the device changes, and also does not change depending on
+ * whether the device is captured or not.
+ * NB: We ignore any additional strings and only look at the first one.
+ */
+ Status = IoGetDevicePropertyData(pDo, &DEVPKEY_Device_LocationPaths, LOCALE_NEUTRAL, 0, sizeof(pDevice->szLocationPath), pDevice->szLocationPath, &ulResultLen, &type);
+ if (!NT_SUCCESS(Status))
+ {
+ /* We do need this, but not critically. On Windows 7, we may get STATUS_OBJECT_NAME_NOT_FOUND. */
+ WARN(("IoGetDevicePropertyData failed for DEVPKEY_Device_LocationPaths, Status (0x%x)", Status));
+ }
+ else
+ {
+ LOG_STRW(pDevice->szLocationPath);
+ }
+
+ // Disabled, but could be used as a fallback instead of IoGetDevicePropertyData; it should work even
+ // when this code is entered from the PnP IRP processing path.
+#if 0
+ {
+ HUB_DEVICE_CONFIG_INFO HubInfo;
+
+ memset(&HubInfo, 0, sizeof(HubInfo));
+ HubInfo.Version = 1;
+ HubInfo.Length = sizeof(HubInfo);
+
+ NTSTATUS Status = VBoxUsbToolIoInternalCtlSendSync(pDo, IOCTL_INTERNAL_USB_GET_DEVICE_CONFIG_INFO, &HubInfo, NULL);
+ ASSERT_WARN(Status == STATUS_SUCCESS, ("GET_DEVICE_CONFIG_INFO for PDO(0x%p) failed Status(0x%x)", pDo, Status));
+ LOG(("Querying hub device config info for PDO(0x%p) done with Status(0x%x)", pDo, Status));
+
+ if (Status == STATUS_SUCCESS)
+ {
+ uint16_t vid, pid, rev;
+ uint8_t cls, sub, prt;
+
+ LOG(("Hub flags: %X\n", HubInfo.HubFlags));
+ LOG_STRW(HubInfo.HardwareIds.Buffer);
+ LOG_STRW(HubInfo.CompatibleIds.Buffer);
+ if (HubInfo.DeviceDescription.Buffer)
+ LOG_STRW(HubInfo.DeviceDescription.Buffer);
+
+ rc = vboxUsbParseHardwareID(HubInfo.HardwareIds.Buffer, &pid, &vid, &rev);
+ if (!rc)
+ {
+ /* This *really* should not happen. */
+ WARN(("Failed to parse Hardware ID"));
+ }
+
+ /* The CompatibleID the IOCTL gives is not always the same as what the PnP Manager uses
+ * (thanks, Microsoft). It might look like "USB\DevClass_00&SubClass_00&Prot_00" or like
+ * "USB\USB30_HUB". In such cases, we must consider the class/subclass/protocol
+ * information simply unavailable.
+ */
+ rc = vboxUsbParseCompatibleIDs(HubInfo.CompatibleIds.Buffer, &cls, &sub, &prt);
+ if (!rc)
+ {
+ /* This is unfortunate but not fatal. */
+ WARN(("Failed to parse Compatible ID"));
+ }
+ LOG(("Parsed HardwareID from IOCTL: vid=%04X, pid=%04X, rev=%04X, class=%02X, subcls=%02X, prot=%02X", vid, pid, rev, cls, sub, prt));
+
+ ExFreePool(HubInfo.HardwareIds.Buffer);
+ ExFreePool(HubInfo.CompatibleIds.Buffer);
+ if (HubInfo.DeviceDescription.Buffer)
+ ExFreePool(HubInfo.DeviceDescription.Buffer);
+ }
+ }
+#endif
+
+ /* Query the topology address from the hub driver. This is not trivial to translate to the location
+ * path, but at least we can get the port number this way.
+ */
+ memset(&TopoAddr, 0, sizeof(TopoAddr));
+ Status = VBoxUsbToolIoInternalCtlSendSync(pDo, IOCTL_INTERNAL_USB_GET_TOPOLOGY_ADDRESS, &TopoAddr, NULL);
+ ASSERT_WARN(Status == STATUS_SUCCESS, ("GET_TOPOLOGY_ADDRESS for PDO(0x%p) failed Status(0x%x)", pDo, Status));
+ LOG(("Querying topology address for PDO(0x%p) done with Status(0x%x)", pDo, Status));
+
+ port = 0;
+ if (Status == STATUS_SUCCESS)
+ {
+ uint16_t *pPort = &TopoAddr.RootHubPortNumber;
+
+ /* The last non-zero port number is the one we're looking for. It might be on the
+ * root hub directly, or on some downstream hub.
+ */
+ for (int i = 0; i < RT_ELEMENTS(TopoAddr.HubPortNumber) + 1; ++i) {
+ if (*pPort)
+ port = *pPort;
+ pPort++;
+ }
+ LOG(("PCI bus/dev/fn: %02X:%02X:%02X, parsed port: %u\n", TopoAddr.PciBusNumber, TopoAddr.PciDeviceNumber, TopoAddr.PciFunctionNumber, port));
+ LOG(("RH port: %u, hub ports: %u/%u/%u/%u/%u/%u\n", TopoAddr.RootHubPortNumber, TopoAddr.HubPortNumber[0],
+ TopoAddr.HubPortNumber[1], TopoAddr.HubPortNumber[2], TopoAddr.HubPortNumber[3], TopoAddr.HubPortNumber[4], TopoAddr.HubPortNumber[5]));
+
+ /* In the extremely unlikely case that the port number does not fit into 8 bits, force
+ * it to zero to indicate that we can't use it.
+ */
+ if (port > 255)
+ port = 0;
+ }
+
+ if (vboxUsbFltBlDevMatchLocked(pDevDr->idVendor, pDevDr->idProduct, pDevDr->bcdDevice))
+ {
+ WARN(("found a known black list device, vid(0x%x), pid(0x%x), rev(0x%x)", pDevDr->idVendor, pDevDr->idProduct, pDevDr->bcdDevice));
+ Status = STATUS_UNSUCCESSFUL;
+ break;
+ }
+
+ LOG(("Device pid=%x vid=%x rev=%x port=%x", pDevDr->idVendor, pDevDr->idProduct, pDevDr->bcdDevice, port));
+ pDevice->bPort = port;
+ pDevice->idVendor = pDevDr->idVendor;
+ pDevice->idProduct = pDevDr->idProduct;
+ pDevice->bcdDevice = pDevDr->bcdDevice;
+ pDevice->bClass = pDevDr->bDeviceClass;
+ pDevice->bSubClass = pDevDr->bDeviceSubClass;
+ pDevice->bProtocol = pDevDr->bDeviceProtocol;
+ pDevice->szSerial[0] = 0;
+ pDevice->szMfgName[0] = 0;
+ pDevice->szProduct[0] = 0;
+
+ /* If there are no strings, don't even try to get any string descriptors. */
+ if (pDevDr->iSerialNumber || pDevDr->iManufacturer || pDevDr->iProduct)
+ {
+ int langId;
+
+ Status = VBoxUsbToolGetLangID(pDo, &langId, VBOXUSBMON_POPULATE_REQUEST_TIMEOUT_MS);
+ if (!NT_SUCCESS(Status))
+ {
+ WARN(("reading language ID failed"));
+ if (Status == STATUS_CANCELLED)
+ {
+ WARN(("found a new black list device, vid(0x%x), pid(0x%x), rev(0x%x)", pDevDr->idVendor, pDevDr->idProduct, pDevDr->bcdDevice));
+ vboxUsbFltBlDevAddLocked(pDevDr->idVendor, pDevDr->idProduct, pDevDr->bcdDevice);
+ Status = STATUS_UNSUCCESSFUL;
+ }
+ break;
+ }
+
+ if (pDevDr->iSerialNumber)
+ {
+ Status = VBoxUsbToolGetStringDescriptor(pDo, pDevice->szSerial, sizeof (pDevice->szSerial), pDevDr->iSerialNumber, langId, VBOXUSBMON_POPULATE_REQUEST_TIMEOUT_MS);
+ if (!NT_SUCCESS(Status))
+ {
+ WARN(("reading serial number failed"));
+ ASSERT_WARN(pDevice->szSerial[0] == '\0', ("serial is not zero!!"));
+ if (Status == STATUS_CANCELLED)
+ {
+ WARN(("found a new black list device, vid(0x%x), pid(0x%x), rev(0x%x)", pDevDr->idVendor, pDevDr->idProduct, pDevDr->bcdDevice));
+ vboxUsbFltBlDevAddLocked(pDevDr->idVendor, pDevDr->idProduct, pDevDr->bcdDevice);
+ Status = STATUS_UNSUCCESSFUL;
+ break;
+ }
+ LOG(("pretending success.."));
+ Status = STATUS_SUCCESS;
+ }
+ }
+
+ if (pDevDr->iManufacturer)
+ {
+ Status = VBoxUsbToolGetStringDescriptor(pDo, pDevice->szMfgName, sizeof (pDevice->szMfgName), pDevDr->iManufacturer, langId, VBOXUSBMON_POPULATE_REQUEST_TIMEOUT_MS);
+ if (!NT_SUCCESS(Status))
+ {
+ WARN(("reading manufacturer name failed"));
+ ASSERT_WARN(pDevice->szMfgName[0] == '\0', ("szMfgName is not zero!!"));
+ if (Status == STATUS_CANCELLED)
+ {
+ WARN(("found a new black list device, vid(0x%x), pid(0x%x), rev(0x%x)", pDevDr->idVendor, pDevDr->idProduct, pDevDr->bcdDevice));
+ vboxUsbFltBlDevAddLocked(pDevDr->idVendor, pDevDr->idProduct, pDevDr->bcdDevice);
+ Status = STATUS_UNSUCCESSFUL;
+ break;
+ }
+ LOG(("pretending success.."));
+ Status = STATUS_SUCCESS;
+ }
+ }
+
+ if (pDevDr->iProduct)
+ {
+ Status = VBoxUsbToolGetStringDescriptor(pDo, pDevice->szProduct, sizeof (pDevice->szProduct), pDevDr->iProduct, langId, VBOXUSBMON_POPULATE_REQUEST_TIMEOUT_MS);
+ if (!NT_SUCCESS(Status))
+ {
+ WARN(("reading product name failed"));
+ ASSERT_WARN(pDevice->szProduct[0] == '\0', ("szProduct is not zero!!"));
+ if (Status == STATUS_CANCELLED)
+ {
+ WARN(("found a new black list device, vid(0x%x), pid(0x%x), rev(0x%x)", pDevDr->idVendor, pDevDr->idProduct, pDevDr->bcdDevice));
+ vboxUsbFltBlDevAddLocked(pDevDr->idVendor, pDevDr->idProduct, pDevDr->bcdDevice);
+ Status = STATUS_UNSUCCESSFUL;
+ break;
+ }
+ LOG(("pretending success.."));
+ Status = STATUS_SUCCESS;
+ }
+ }
+
+ LOG((": strings: '%s':'%s':'%s' (lang ID %x)",
+ pDevice->szMfgName, pDevice->szProduct, pDevice->szSerial, langId));
+ }
+
+ LOG(("Populating Device(0x%p) for PDO(0x%p) Succeeded", pDevice, pDo));
+ Status = STATUS_SUCCESS;
+ } while (0);
+
+ VBoxUsbMonMemFree(pDevDr);
+ LOG(("Populating Device(0x%p) for PDO(0x%p) Done, Status (0x%x)", pDevice, pDo, Status));
+ return Status;
+}
+
+static bool vboxUsbFltDevCheckReplugLocked(PVBOXUSBFLT_DEVICE pDevice, PVBOXUSBFLTCTX pContext)
+{
+ ASSERT_WARN(pContext, ("context is NULL!"));
+
+ LOG(("Current context is (0x%p)", pContext));
+ LOG(("Current Device owner is (0x%p)", pDevice->pOwner));
+
+ /* check if device is already replugging */
+ if (pDevice->enmState <= VBOXUSBFLT_DEVSTATE_ADDED)
+ {
+ LOG(("Device (0x%p) is already replugging, return..", pDevice));
+ /* it is, do nothing */
+ ASSERT_WARN(pDevice->enmState == VBOXUSBFLT_DEVSTATE_REPLUGGING,
+ ("Device (0x%p) state is NOT REPLUGGING (%d)", pDevice, pDevice->enmState));
+ return false;
+ }
+
+ if (pDevice->pOwner && pContext != pDevice->pOwner)
+ {
+ LOG(("Device (0x%p) is owned by another context(0x%p), current is(0x%p)", pDevice, pDevice->pOwner, pContext));
+ /* this device is owned by another context, we're not allowed to do anything */
+ return false;
+ }
+
+ uintptr_t uId = 0;
+ bool bNeedReplug = false;
+ bool fFilter = false;
+ bool fIsOneShot = false;
+ PVBOXUSBFLTCTX pNewOwner = vboxUsbFltDevMatchLocked(pDevice, &uId,
+ false, /* do not remove a one-shot filter */
+ &fFilter, &fIsOneShot);
+ LOG(("Matching Info: Filter (0x%p), NewOwner(0x%p), fFilter(%d), fIsOneShot(%d)", uId, pNewOwner, (int)fFilter, (int)fIsOneShot));
+ if (pDevice->pOwner && pNewOwner && pDevice->pOwner != pNewOwner)
+ {
+ LOG(("Matching: Device (0x%p) is requested another owner(0x%p), current is(0x%p)", pDevice, pNewOwner, pDevice->pOwner));
+ /* the device is owned by another owner, we can not change the owner here */
+ return false;
+ }
+
+ if (!fFilter)
+ {
+ LOG(("Matching: Device (0x%p) should NOT be filtered", pDevice));
+ /* the device should NOT be filtered, check the current state */
+ if (vboxUsbFltDevStateIsNotFiltered(pDevice))
+ {
+ LOG(("Device (0x%p) is NOT filtered", pDevice));
+ /* no changes */
+ if (fIsOneShot)
+ {
+ ASSERT_WARN(pNewOwner, ("no new owner"));
+ LOG(("Matching: This is a one-shot filter (0x%p), removing..", uId));
+ /* remove a one-shot filter and keep the original filter data */
+ int tmpRc = VBoxUSBFilterRemove(pNewOwner, uId);
+ ASSERT_WARN(RT_SUCCESS(tmpRc), ("remove filter failed, rc (%d)", tmpRc));
+ if (!pDevice->pOwner)
+ {
+ LOG(("Matching: updating the one-shot owner to (0x%p), fltId(0x%p)", pNewOwner, uId));
+ /* update owner for one-shot if the owner is changed (i.e. assigned) */
+ vboxUsbFltDevOwnerUpdateLocked(pDevice, pNewOwner, uId, true);
+ }
+ else
+ {
+ LOG(("Matching: device already has owner (0x%p) assigned", pDevice->pOwner));
+ }
+ }
+ else
+ {
+ LOG(("Matching: This is NOT a one-shot filter (0x%p), newOwner(0x%p)", uId, pNewOwner));
+ if (pNewOwner)
+ {
+ vboxUsbFltDevOwnerUpdateLocked(pDevice, pNewOwner, uId, false);
+ }
+ }
+ }
+ else
+ {
+ LOG(("Device (0x%p) IS filtered", pDevice));
+ /* the device is currently filtered, we should release it only if
+ * 1. device does not have an owner
+ * or
+ * 2. it should be released bue to a one-shot filter
+ * or
+ * 3. it is NOT grabbed by a one-shot filter */
+ if (!pDevice->pOwner || fIsOneShot || !pDevice->fIsFilterOneShot)
+ {
+ LOG(("Matching: Need replug"));
+ bNeedReplug = true;
+ }
+ }
+ }
+ else
+ {
+ LOG(("Matching: Device (0x%p) SHOULD be filtered", pDevice));
+ /* the device should be filtered, check the current state */
+ ASSERT_WARN(uId, ("zero uid"));
+ ASSERT_WARN(pNewOwner, ("zero pNewOwner"));
+ if (vboxUsbFltDevStateIsFiltered(pDevice))
+ {
+ LOG(("Device (0x%p) IS filtered", pDevice));
+ /* the device is filtered */
+ if (pNewOwner == pDevice->pOwner)
+ {
+ LOG(("Device owner match"));
+ /* no changes */
+ if (fIsOneShot)
+ {
+ LOG(("Matching: This is a one-shot filter (0x%p), removing..", uId));
+ /* remove a one-shot filter and keep the original filter data */
+ int tmpRc = VBoxUSBFilterRemove(pNewOwner, uId);
+ ASSERT_WARN(RT_SUCCESS(tmpRc), ("remove filter failed, rc (%d)", tmpRc));
+ }
+ else
+ {
+ LOG(("Matching: This is NOT a one-shot filter (0x%p), Owner(0x%p)", uId, pDevice->pOwner));
+ vboxUsbFltDevOwnerUpdateLocked(pDevice, pDevice->pOwner, uId, false);
+ }
+ }
+ else
+ {
+ ASSERT_WARN(!pDevice->pOwner, ("device should NOT have owner"));
+ LOG(("Matching: Need replug"));
+ /* the device needs to be filtered, but the owner changes, replug needed */
+ bNeedReplug = true;
+ }
+ }
+ else
+ {
+ /* the device is currently NOT filtered,
+ * we should replug it only if
+ * 1. device does not have an owner
+ * or
+ * 2. it should be captured due to a one-shot filter
+ * or
+ * 3. it is NOT released by a one-shot filter */
+ if (!pDevice->pOwner || fIsOneShot || !pDevice->fIsFilterOneShot)
+ {
+ bNeedReplug = true;
+ LOG(("Matching: Need replug"));
+ }
+ }
+ }
+
+ if (bNeedReplug)
+ {
+ LOG(("Matching: Device needs replugging, marking as such"));
+ vboxUsbFltDevStateMarkReplugLocked(pDevice);
+ }
+ else
+ {
+ LOG(("Matching: Device does NOT need replugging"));
+ }
+
+ return bNeedReplug;
+}
+
+static void vboxUsbFltReplugList(PLIST_ENTRY pList)
+{
+ PLIST_ENTRY pNext;
+ for (PLIST_ENTRY pEntry = pList->Flink;
+ pEntry != pList;
+ pEntry = pNext)
+ {
+ pNext = pEntry->Flink;
+ PVBOXUSBFLT_DEVICE pDevice = PVBOXUSBFLT_DEVICE_FROM_REPLUGGINGLE(pEntry);
+ LOG(("replugging matched PDO(0x%p), pDevice(0x%p)", pDevice->Pdo, pDevice));
+ ASSERT_WARN(pDevice->enmState == VBOXUSBFLT_DEVSTATE_REPLUGGING
+ || pDevice->enmState == VBOXUSBFLT_DEVSTATE_REMOVED,
+ ("invalid state(0x%x) for device(0x%p)", pDevice->enmState, pDevice));
+
+ vboxUsbFltPdoReplug(pDevice->Pdo);
+ ObDereferenceObject(pDevice->Pdo);
+ vboxUsbFltDevRelease(pDevice);
+ }
+}
+
+typedef struct VBOXUSBFLTCHECKWALKER
+{
+ PVBOXUSBFLTCTX pContext;
+} VBOXUSBFLTCHECKWALKER, *PVBOXUSBFLTCHECKWALKER;
+
+static DECLCALLBACK(BOOLEAN) vboxUsbFltFilterCheckWalker(PFILE_OBJECT pHubFile,
+ PDEVICE_OBJECT pHubDo, PVOID pvContext)
+{
+ PVBOXUSBFLTCHECKWALKER pData = (PVBOXUSBFLTCHECKWALKER)pvContext;
+ PVBOXUSBFLTCTX pContext = pData->pContext;
+
+ LOG(("Visiting pHubFile(0x%p), pHubDo(0x%p), oContext(0x%p)", pHubFile, pHubDo, pContext));
+ KIRQL Irql = KeGetCurrentIrql();
+ ASSERT_WARN(Irql == PASSIVE_LEVEL, ("unexpected IRQL (%d)", Irql));
+
+ PDEVICE_RELATIONS pDevRelations = NULL;
+
+ NTSTATUS Status = VBoxUsbMonQueryBusRelations(pHubDo, pHubFile, &pDevRelations);
+ if (Status == STATUS_SUCCESS && pDevRelations)
+ {
+ ULONG cReplugPdos = pDevRelations->Count;
+ LIST_ENTRY ReplugDevList;
+ InitializeListHead(&ReplugDevList);
+ for (ULONG k = 0; k < pDevRelations->Count; ++k)
+ {
+ PDEVICE_OBJECT pDevObj;
+
+ /* Grab the PDO+reference. We won't need the upper layer device object
+ * anymore, so dereference that right here, and drop the PDO ref later.
+ */
+ pDevObj = IoGetDeviceAttachmentBaseRef(pDevRelations->Objects[k]);
+ LOG(("DevObj=%p, PDO=%p\n", pDevRelations->Objects[k], pDevObj));
+ ObDereferenceObject(pDevRelations->Objects[k]);
+ pDevRelations->Objects[k] = pDevObj;
+
+ LOG(("Found existing USB PDO 0x%p", pDevObj));
+ VBOXUSBFLT_LOCK_ACQUIRE();
+ PVBOXUSBFLT_DEVICE pDevice = vboxUsbFltDevGetLocked(pDevObj);
+ if (pDevice)
+ {
+ LOG(("Found existing device info (0x%p) for PDO 0x%p", pDevice, pDevObj));
+ bool bReplug = vboxUsbFltDevCheckReplugLocked(pDevice, pContext);
+ if (bReplug)
+ {
+ LOG(("Replug needed for device (0x%p)", pDevice));
+ InsertHeadList(&ReplugDevList, &pDevice->RepluggingLe);
+ vboxUsbFltDevRetain(pDevice);
+ /* do not dereference object since we will use it later */
+ }
+ else
+ {
+ LOG(("Replug NOT needed for device (0x%p)", pDevice));
+ ObDereferenceObject(pDevObj);
+ }
+
+ VBOXUSBFLT_LOCK_RELEASE();
+
+ pDevRelations->Objects[k] = NULL;
+ --cReplugPdos;
+ ASSERT_WARN((uint32_t)cReplugPdos < UINT32_MAX/2, ("cReplugPdos(%d) state broken", cReplugPdos));
+ continue;
+ }
+ VBOXUSBFLT_LOCK_RELEASE();
+
+ LOG(("NO device info found for PDO 0x%p", pDevObj));
+ VBOXUSBFLT_DEVICE Device;
+ Status = vboxUsbFltDevPopulate(&Device, pDevObj /*, FALSE /* only need filter properties */);
+ if (NT_SUCCESS(Status))
+ {
+ uintptr_t uId = 0;
+ bool fFilter = false;
+ bool fIsOneShot = false;
+ VBOXUSBFLT_LOCK_ACQUIRE();
+ PVBOXUSBFLTCTX pCtx = vboxUsbFltDevMatchLocked(&Device, &uId,
+ false, /* do not remove a one-shot filter */
+ &fFilter, &fIsOneShot);
+ VBOXUSBFLT_LOCK_RELEASE();
+ NOREF(pCtx);
+ LOG(("Matching Info: Filter (0x%p), pCtx(0x%p), fFilter(%d), fIsOneShot(%d)", uId, pCtx, (int)fFilter, (int)fIsOneShot));
+ if (fFilter)
+ {
+ LOG(("Matching: This device SHOULD be filtered"));
+ /* this device needs to be filtered, but it's not,
+ * leave the PDO in array to issue a replug request for it
+ * later on */
+ continue;
+ }
+ }
+ else
+ {
+ WARN(("vboxUsbFltDevPopulate for PDO 0x%p failed with Status 0x%x", pDevObj, Status));
+ if ( Status == STATUS_CANCELLED
+ && g_VBoxUsbFltGlobals.dwForceReplugWhenDevPopulateFails)
+ {
+ /*
+ * This can happen if the device got suspended and is in D3 state where we can't query any strings.
+ * There is no known way to set the power state of the device, especially if there is no driver attached yet.
+ * The sledgehammer approach is to just replug the device to force it out of suspend, see bugref @{9479}.
+ */
+ continue;
+ }
+ }
+
+ LOG(("Matching: This device should NOT be filtered"));
+ /* this device should not be filtered, and it's not */
+ ObDereferenceObject(pDevObj);
+ pDevRelations->Objects[k] = NULL;
+ --cReplugPdos;
+ ASSERT_WARN((uint32_t)cReplugPdos < UINT32_MAX/2, ("cReplugPdos is %d", cReplugPdos));
+ }
+
+ LOG(("(%d) non-matched PDOs to be replugged", cReplugPdos));
+
+ if (cReplugPdos)
+ {
+ for (ULONG k = 0; k < pDevRelations->Count; ++k)
+ {
+ if (!pDevRelations->Objects[k])
+ continue;
+
+ Status = vboxUsbFltPdoReplug(pDevRelations->Objects[k]);
+ ASSERT_WARN(Status == STATUS_SUCCESS, ("vboxUsbFltPdoReplug failed! Status(0x%x)", Status));
+ ObDereferenceObject(pDevRelations->Objects[k]);
+ if (!--cReplugPdos)
+ break;
+ }
+
+ ASSERT_WARN(!cReplugPdos, ("cReplugPdos reached zero!"));
+ }
+
+ vboxUsbFltReplugList(&ReplugDevList);
+
+ ExFreePool(pDevRelations);
+ }
+ else
+ {
+ WARN(("VBoxUsbMonQueryBusRelations failed for hub DO(0x%p), Status(0x%x), pDevRelations(0x%p)",
+ pHubDo, Status, pDevRelations));
+ }
+
+ LOG(("Done Visiting pHubFile(0x%p), pHubDo(0x%p), oContext(0x%p)", pHubFile, pHubDo, pContext));
+
+ return TRUE;
+}
+
+NTSTATUS VBoxUsbFltFilterCheck(PVBOXUSBFLTCTX pContext)
+{
+ KIRQL Irql = KeGetCurrentIrql();
+ ASSERT_WARN(Irql == PASSIVE_LEVEL, ("unexpected IRQL (%d)", Irql));
+
+ LOG(("Running filters, Context (0x%p)..", pContext));
+
+ VBOXUSBFLTCHECKWALKER Data;
+ Data.pContext = pContext;
+ vboxUsbMonHubDevWalk(vboxUsbFltFilterCheckWalker, &Data);
+
+ LOG(("DONE Running filters, Context (0x%p)", pContext));
+
+ return STATUS_SUCCESS;
+}
+
+NTSTATUS VBoxUsbFltClose(PVBOXUSBFLTCTX pContext)
+{
+ LOG(("Closing context(0x%p)", pContext));
+ LIST_ENTRY ReplugDevList;
+ InitializeListHead(&ReplugDevList);
+
+ ASSERT_WARN(pContext, ("null context"));
+
+ KIRQL Irql = KeGetCurrentIrql();
+ ASSERT_WARN(Irql == PASSIVE_LEVEL, ("irql==(%d)", Irql));
+
+ VBOXUSBFLT_LOCK_ACQUIRE();
+
+ pContext->bRemoved = TRUE;
+ RemoveEntryList(&pContext->ListEntry);
+
+ LOG(("removing owner filters"));
+ /* now re-arrange the filters */
+ /* 1. remove filters */
+ VBoxUSBFilterRemoveOwner(pContext);
+
+ LOG(("enumerating devices.."));
+ /* 2. check if there are devices owned */
+ for (PLIST_ENTRY pEntry = g_VBoxUsbFltGlobals.DeviceList.Flink;
+ pEntry != &g_VBoxUsbFltGlobals.DeviceList;
+ pEntry = pEntry->Flink)
+ {
+ PVBOXUSBFLT_DEVICE pDevice = PVBOXUSBFLT_DEVICE_FROM_LE(pEntry);
+ if (pDevice->pOwner != pContext)
+ continue;
+
+ LOG(("found device(0x%p), pdo(0x%p), state(%d), filter id(0x%p), oneshot(%d)",
+ pDevice, pDevice->Pdo, pDevice->enmState, pDevice->uFltId, (int)pDevice->fIsFilterOneShot));
+ ASSERT_WARN(pDevice->enmState != VBOXUSBFLT_DEVSTATE_ADDED, ("VBOXUSBFLT_DEVSTATE_ADDED state for device(0x%p)", pDevice));
+ ASSERT_WARN(pDevice->enmState != VBOXUSBFLT_DEVSTATE_REMOVED, ("VBOXUSBFLT_DEVSTATE_REMOVED state for device(0x%p)", pDevice));
+
+ vboxUsbFltDevOwnerClearLocked(pDevice);
+
+ if (vboxUsbFltDevCheckReplugLocked(pDevice, pContext))
+ {
+ LOG(("device needs replug"));
+ InsertHeadList(&ReplugDevList, &pDevice->RepluggingLe);
+ /* retain to ensure the device is not removed before we issue a replug */
+ vboxUsbFltDevRetain(pDevice);
+ /* keep the PDO alive */
+ ObReferenceObject(pDevice->Pdo);
+ }
+ else
+ {
+ LOG(("device does NOT need replug"));
+ }
+ }
+
+ VBOXUSBFLT_LOCK_RELEASE();
+
+ /* this should replug all devices that were either skipped or grabbed due to the context's */
+ vboxUsbFltReplugList(&ReplugDevList);
+
+ LOG(("SUCCESS done context(0x%p)", pContext));
+ return STATUS_SUCCESS;
+}
+
+NTSTATUS VBoxUsbFltCreate(PVBOXUSBFLTCTX pContext)
+{
+ LOG(("Creating context(0x%p)", pContext));
+ memset(pContext, 0, sizeof (*pContext));
+ pContext->Process = RTProcSelf();
+ VBOXUSBFLT_LOCK_ACQUIRE();
+ InsertHeadList(&g_VBoxUsbFltGlobals.ContextList, &pContext->ListEntry);
+ VBOXUSBFLT_LOCK_RELEASE();
+ LOG(("SUCCESS context(0x%p)", pContext));
+ return STATUS_SUCCESS;
+}
+
+int VBoxUsbFltAdd(PVBOXUSBFLTCTX pContext, PUSBFILTER pFilter, uintptr_t *pId)
+{
+ LOG(("adding filter, Context (0x%p)..", pContext));
+ *pId = 0;
+ /* LOG the filter details. */
+ LOG((__FUNCTION__": %s %s %s",
+ USBFilterGetString(pFilter, USBFILTERIDX_MANUFACTURER_STR) ? USBFilterGetString(pFilter, USBFILTERIDX_MANUFACTURER_STR) : "<null>",
+ USBFilterGetString(pFilter, USBFILTERIDX_PRODUCT_STR) ? USBFilterGetString(pFilter, USBFILTERIDX_PRODUCT_STR) : "<null>",
+ USBFilterGetString(pFilter, USBFILTERIDX_SERIAL_NUMBER_STR) ? USBFilterGetString(pFilter, USBFILTERIDX_SERIAL_NUMBER_STR) : "<null>"));
+#ifdef VBOX_USB_WITH_VERBOSE_LOGGING
+ LOG(("VBoxUSBClient::addFilter: idVendor=%#x idProduct=%#x bcdDevice=%#x bDeviceClass=%#x bDeviceSubClass=%#x bDeviceProtocol=%#x bBus=%#x bPort=%#x Type%#x",
+ USBFilterGetNum(pFilter, USBFILTERIDX_VENDOR_ID),
+ USBFilterGetNum(pFilter, USBFILTERIDX_PRODUCT_ID),
+ USBFilterGetNum(pFilter, USBFILTERIDX_DEVICE_REV),
+ USBFilterGetNum(pFilter, USBFILTERIDX_DEVICE_CLASS),
+ USBFilterGetNum(pFilter, USBFILTERIDX_DEVICE_SUB_CLASS),
+ USBFilterGetNum(pFilter, USBFILTERIDX_DEVICE_PROTOCOL),
+ USBFilterGetNum(pFilter, USBFILTERIDX_BUS),
+ USBFilterGetNum(pFilter, USBFILTERIDX_PORT),
+ USBFilterGetFilterType(pFilter)));
+#endif
+
+ /* We can't get the bus/port numbers. Ignore them while matching. */
+ USBFilterSetMustBePresent(pFilter, USBFILTERIDX_BUS, false);
+ USBFilterSetMustBePresent(pFilter, USBFILTERIDX_PORT, false);
+
+ /* We may not be able to reconstruct the class/subclass/protocol if we aren't able to
+ * read the device descriptor. Don't require these to be present. See also the fInferredDesc flag.
+ */
+ USBFilterSetMustBePresent(pFilter, USBFILTERIDX_DEVICE_CLASS, false);
+ USBFilterSetMustBePresent(pFilter, USBFILTERIDX_DEVICE_SUB_CLASS, false);
+ USBFilterSetMustBePresent(pFilter, USBFILTERIDX_DEVICE_PROTOCOL, false);
+
+ /* We may also be unable to read string descriptors. Often the userland can't read the
+ * string descriptors either because the device is in a low-power state, but it can happen
+ * that the userland gets lucky and reads the strings, but by the time we get to read them
+ * they're inaccessible due to power management. So, don't require the strings to be present.
+ */
+ USBFilterSetMustBePresent(pFilter, USBFILTERIDX_MANUFACTURER_STR, false);
+ USBFilterSetMustBePresent(pFilter, USBFILTERIDX_PRODUCT_STR, false);
+ USBFilterSetMustBePresent(pFilter, USBFILTERIDX_SERIAL_NUMBER_STR, false);
+
+ uintptr_t uId = 0;
+ VBOXUSBFLT_LOCK_ACQUIRE();
+ /* Add the filter. */
+ int rc = VBoxUSBFilterAdd(pFilter, pContext, &uId);
+ VBOXUSBFLT_LOCK_RELEASE();
+ if (RT_SUCCESS(rc))
+ {
+ LOG(("ADDED filter id 0x%p", uId));
+ ASSERT_WARN(uId, ("uid is NULL"));
+#ifdef VBOX_USBMON_WITH_FILTER_AUTOAPPLY
+ VBoxUsbFltFilterCheck();
+#endif
+ }
+ else
+ {
+ WARN(("VBoxUSBFilterAdd failed rc (%d)", rc));
+ ASSERT_WARN(!uId, ("uid is not NULL"));
+ }
+
+ *pId = uId;
+ return rc;
+}
+
+int VBoxUsbFltRemove(PVBOXUSBFLTCTX pContext, uintptr_t uId)
+{
+ LOG(("removing filter id(0x%p), Context (0x%p)..", pContext, uId));
+ Assert(uId);
+
+ VBOXUSBFLT_LOCK_ACQUIRE();
+ int rc = VBoxUSBFilterRemove(pContext, uId);
+ if (!RT_SUCCESS(rc))
+ {
+ WARN(("VBoxUSBFilterRemove failed rc (%d)", rc));
+ VBOXUSBFLT_LOCK_RELEASE();
+ return rc;
+ }
+
+ LOG(("enumerating devices.."));
+ for (PLIST_ENTRY pEntry = g_VBoxUsbFltGlobals.DeviceList.Flink;
+ pEntry != &g_VBoxUsbFltGlobals.DeviceList;
+ pEntry = pEntry->Flink)
+ {
+ PVBOXUSBFLT_DEVICE pDevice = PVBOXUSBFLT_DEVICE_FROM_LE(pEntry);
+ if (pDevice->fIsFilterOneShot)
+ {
+ ASSERT_WARN(!pDevice->uFltId, ("oneshot filter on device(0x%p): unexpected uFltId(%d)", pDevice, pDevice->uFltId));
+ }
+
+ if (pDevice->uFltId != uId)
+ continue;
+
+ ASSERT_WARN(pDevice->pOwner == pContext, ("Device(0x%p) owner(0x%p) not match to (0x%p)", pDevice, pDevice->pOwner, pContext));
+ if (pDevice->pOwner != pContext)
+ continue;
+
+ LOG(("found device(0x%p), pdo(0x%p), state(%d), filter id(0x%p), oneshot(%d)",
+ pDevice, pDevice->Pdo, pDevice->enmState, pDevice->uFltId, (int)pDevice->fIsFilterOneShot));
+ ASSERT_WARN(!pDevice->fIsFilterOneShot, ("device(0x%p) is filtered with a oneshot filter", pDevice));
+ pDevice->uFltId = 0;
+ /* clear the fIsFilterOneShot flag to ensure the device is replugged on the next VBoxUsbFltFilterCheck call */
+ pDevice->fIsFilterOneShot = false;
+ }
+ VBOXUSBFLT_LOCK_RELEASE();
+
+ LOG(("done enumerating devices"));
+
+ if (RT_SUCCESS(rc))
+ {
+#ifdef VBOX_USBMON_WITH_FILTER_AUTOAPPLY
+ VBoxUsbFltFilterCheck();
+#endif
+ }
+ return rc;
+}
+
+static USBDEVICESTATE vboxUsbDevGetUserState(PVBOXUSBFLTCTX pContext, PVBOXUSBFLT_DEVICE pDevice)
+{
+ if (vboxUsbFltDevStateIsNotFiltered(pDevice))
+ return USBDEVICESTATE_USED_BY_HOST_CAPTURABLE;
+
+ /* the device is filtered, or replugging */
+ if (pDevice->enmState == VBOXUSBFLT_DEVSTATE_REPLUGGING)
+ {
+ ASSERT_WARN(!pDevice->pOwner, ("replugging device(0x%p) still has an owner(0x%p)", pDevice, pDevice->pOwner));
+ ASSERT_WARN(!pDevice->uFltId, ("replugging device(0x%p) still has filter(0x%p)", pDevice, pDevice->uFltId));
+ /* no user state for this, we should not return it tu the user */
+ return USBDEVICESTATE_USED_BY_HOST;
+ }
+
+ /* the device is filtered, if owner differs from the context, return as USED_BY_HOST */
+ ASSERT_WARN(pDevice->pOwner, ("device(0x%p) has noowner", pDevice));
+ /* the id can be null if a filter is removed */
+// Assert(pDevice->uFltId);
+
+ if (pDevice->pOwner != pContext)
+ {
+ LOG(("Device owner differs from the current context, returning used by host"));
+ return USBDEVICESTATE_USED_BY_HOST;
+ }
+
+ switch (pDevice->enmState)
+ {
+ case VBOXUSBFLT_DEVSTATE_UNCAPTURED:
+ case VBOXUSBFLT_DEVSTATE_CAPTURING:
+ return USBDEVICESTATE_USED_BY_HOST_CAPTURABLE;
+ case VBOXUSBFLT_DEVSTATE_CAPTURED:
+ return USBDEVICESTATE_HELD_BY_PROXY;
+ case VBOXUSBFLT_DEVSTATE_USED_BY_GUEST:
+ return USBDEVICESTATE_USED_BY_GUEST;
+ default:
+ WARN(("unexpected device state(%d) for device(0x%p)", pDevice->enmState, pDevice));
+ return USBDEVICESTATE_UNSUPPORTED;
+ }
+}
+
+NTSTATUS VBoxUsbFltGetDevice(PVBOXUSBFLTCTX pContext, HVBOXUSBDEVUSR hDevice, PUSBSUP_GETDEV_MON pInfo)
+{
+ if (!hDevice)
+ return STATUS_INVALID_PARAMETER;
+
+ memset (pInfo, 0, sizeof (*pInfo));
+ VBOXUSBFLT_LOCK_ACQUIRE();
+ for (PLIST_ENTRY pEntry = g_VBoxUsbFltGlobals.DeviceList.Flink;
+ pEntry != &g_VBoxUsbFltGlobals.DeviceList;
+ pEntry = pEntry->Flink)
+ {
+ PVBOXUSBFLT_DEVICE pDevice = PVBOXUSBFLT_DEVICE_FROM_LE(pEntry);
+ Assert(pDevice->enmState != VBOXUSBFLT_DEVSTATE_REMOVED);
+ Assert(pDevice->enmState != VBOXUSBFLT_DEVSTATE_ADDED);
+
+ if (pDevice != hDevice)
+ continue;
+
+ USBDEVICESTATE enmUsrState = vboxUsbDevGetUserState(pContext, pDevice);
+ pInfo->enmState = enmUsrState;
+ VBOXUSBFLT_LOCK_RELEASE();
+ return STATUS_SUCCESS;
+ }
+
+ VBOXUSBFLT_LOCK_RELEASE();
+
+ /* We should not get this far with valid input. */
+ return STATUS_INVALID_PARAMETER;
+}
+
+NTSTATUS VBoxUsbFltPdoAdd(PDEVICE_OBJECT pPdo, BOOLEAN *pbFiltered)
+{
+ *pbFiltered = FALSE;
+ PVBOXUSBFLT_DEVICE pDevice;
+
+ /* Find the real PDO+reference. Dereference when we're done with it. Note that
+ * the input pPdo was not explicitly referenced so we're not dropping its ref.
+ */
+ PDEVICE_OBJECT pDevObj = IoGetDeviceAttachmentBaseRef(pPdo);
+ LOG(("DevObj=%p, real PDO=%p\n", pPdo, pDevObj));
+ pPdo = pDevObj;
+
+ /* first check if device is in the a already */
+ VBOXUSBFLT_LOCK_ACQUIRE();
+ pDevice = vboxUsbFltDevGetLocked(pPdo);
+ if (pDevice)
+ {
+ LOG(("found device (0x%p), state(%d) for PDO(0x%p)", pDevice, pDevice->enmState, pPdo));
+ ASSERT_WARN(pDevice->enmState != VBOXUSBFLT_DEVSTATE_ADDED, ("VBOXUSBFLT_DEVSTATE_ADDED state for device(0x%p)", pDevice));
+ ASSERT_WARN(pDevice->enmState != VBOXUSBFLT_DEVSTATE_REMOVED, ("VBOXUSBFLT_DEVSTATE_REMOVED state for device(0x%p)", pDevice));
+ *pbFiltered = pDevice->enmState >= VBOXUSBFLT_DEVSTATE_CAPTURING;
+ VBOXUSBFLT_LOCK_RELEASE();
+ ObDereferenceObject(pPdo);
+ return STATUS_SUCCESS;
+ }
+ VBOXUSBFLT_LOCK_RELEASE();
+ pDevice = (PVBOXUSBFLT_DEVICE)VBoxUsbMonMemAllocZ(sizeof (*pDevice));
+ if (!pDevice)
+ {
+ WARN(("VBoxUsbMonMemAllocZ failed"));
+ ObDereferenceObject(pPdo);
+ return STATUS_NO_MEMORY;
+ }
+
+ pDevice->enmState = VBOXUSBFLT_DEVSTATE_ADDED;
+ pDevice->cRefs = 1;
+ NTSTATUS Status = vboxUsbFltDevPopulate(pDevice, pPdo /* , TRUE /* need all props */);
+ if (!NT_SUCCESS(Status))
+ {
+ WARN(("vboxUsbFltDevPopulate failed, Status 0x%x", Status));
+ ObDereferenceObject(pPdo);
+ VBoxUsbMonMemFree(pDevice);
+ return Status;
+ }
+
+ uintptr_t uId;
+ bool fFilter = false;
+ bool fIsOneShot = false;
+ PVBOXUSBFLTCTX pCtx;
+ PVBOXUSBFLT_DEVICE pTmpDev;
+ VBOXUSBFLT_LOCK_ACQUIRE();
+ /* (paranoia) re-check the device is still not here */
+ pTmpDev = vboxUsbFltDevGetLocked(pPdo);
+
+ /* Drop the PDO ref, now we won't need it anymore. */
+ ObDereferenceObject(pPdo);
+
+ if (pTmpDev)
+ {
+ LOG(("second try: found device (0x%p), state(%d) for PDO(0x%p)", pDevice, pDevice->enmState, pPdo));
+ ASSERT_WARN(pDevice->enmState != VBOXUSBFLT_DEVSTATE_ADDED, ("second try: VBOXUSBFLT_DEVSTATE_ADDED state for device(0x%p)", pDevice));
+ ASSERT_WARN(pDevice->enmState != VBOXUSBFLT_DEVSTATE_REMOVED, ("second try: VBOXUSBFLT_DEVSTATE_REMOVED state for device(0x%p)", pDevice));
+ *pbFiltered = pTmpDev->enmState >= VBOXUSBFLT_DEVSTATE_CAPTURING;
+ VBOXUSBFLT_LOCK_RELEASE();
+ VBoxUsbMonMemFree(pDevice);
+ return STATUS_SUCCESS;
+ }
+
+ LOG(("Created Device 0x%p for PDO 0x%p", pDevice, pPdo));
+
+ pCtx = vboxUsbFltDevMatchLocked(pDevice, &uId,
+ true, /* remove a one-shot filter */
+ &fFilter, &fIsOneShot);
+ LOG(("Matching Info: Filter (0x%p), pCtx(0x%p), fFilter(%d), fIsOneShot(%d)", uId, pCtx, (int)fFilter, (int)fIsOneShot));
+ if (fFilter)
+ {
+ LOG(("Created Device 0x%p should be filtered", pDevice));
+ ASSERT_WARN(pCtx, ("zero ctx"));
+ ASSERT_WARN(uId, ("zero uId"));
+ pDevice->enmState = VBOXUSBFLT_DEVSTATE_CAPTURING;
+ }
+ else
+ {
+ LOG(("Created Device 0x%p should NOT be filtered", pDevice));
+ ASSERT_WARN(!uId == !pCtx, ("invalid uid(0x%p) - ctx(0x%p) pair", uId, pCtx)); /* either both zero or both not */
+ pDevice->enmState = VBOXUSBFLT_DEVSTATE_UNCAPTURED;
+ }
+
+ if (pCtx)
+ vboxUsbFltDevOwnerSetLocked(pDevice, pCtx, fIsOneShot ? 0 : uId, fIsOneShot);
+
+ InsertHeadList(&g_VBoxUsbFltGlobals.DeviceList, &pDevice->GlobalLe);
+
+ /* do not need to signal anything here -
+ * going to do that once the proxy device object starts */
+ VBOXUSBFLT_LOCK_RELEASE();
+
+ *pbFiltered = fFilter;
+
+ return STATUS_SUCCESS;
+}
+
+BOOLEAN VBoxUsbFltPdoIsFiltered(PDEVICE_OBJECT pPdo)
+{
+ VBOXUSBFLT_DEVSTATE enmState = VBOXUSBFLT_DEVSTATE_REMOVED;
+
+ /* Find the real PDO+reference. Dereference when we're done with it. Note that
+ * the input pPdo was not explicitly referenced so we're not dropping its ref.
+ */
+ PDEVICE_OBJECT pDevObj = IoGetDeviceAttachmentBaseRef(pPdo);
+ LOG(("DevObj=%p, real PDO=%p\n", pPdo, pDevObj));
+ pPdo = pDevObj;
+
+ VBOXUSBFLT_LOCK_ACQUIRE();
+
+ PVBOXUSBFLT_DEVICE pDevice = vboxUsbFltDevGetLocked(pPdo);
+ if (pDevice)
+ enmState = pDevice->enmState;
+
+ VBOXUSBFLT_LOCK_RELEASE();
+ ObDereferenceObject(pPdo);
+
+ return enmState >= VBOXUSBFLT_DEVSTATE_CAPTURING;
+}
+
+NTSTATUS VBoxUsbFltPdoRemove(PDEVICE_OBJECT pPdo)
+{
+ PVBOXUSBFLT_DEVICE pDevice;
+ VBOXUSBFLT_DEVSTATE enmOldState;
+
+ /* Find the real PDO+reference. Dereference when we're done with it. Note that
+ * the input pPdo was not explicitly referenced so we're not dropping its ref.
+ */
+ PDEVICE_OBJECT pDevObj = IoGetDeviceAttachmentBaseRef(pPdo);
+ LOG(("DevObj=%p, real PDO=%p\n", pPdo, pDevObj));
+ pPdo = pDevObj;
+
+ VBOXUSBFLT_LOCK_ACQUIRE();
+ pDevice = vboxUsbFltDevGetLocked(pPdo);
+ if (pDevice)
+ {
+ RemoveEntryList(&pDevice->GlobalLe);
+ enmOldState = pDevice->enmState;
+ pDevice->enmState = VBOXUSBFLT_DEVSTATE_REMOVED;
+ }
+ VBOXUSBFLT_LOCK_RELEASE();
+ ObDereferenceObject(pPdo);
+ if (pDevice)
+ vboxUsbFltDevRelease(pDevice);
+ return STATUS_SUCCESS;
+}
+
+HVBOXUSBFLTDEV VBoxUsbFltProxyStarted(PDEVICE_OBJECT pPdo)
+{
+ PVBOXUSBFLT_DEVICE pDevice;
+ VBOXUSBFLT_LOCK_ACQUIRE();
+
+ /* NB: The USB proxy (VBoxUSB.sys) passes us the real PDO, not anything above that. */
+ pDevice = vboxUsbFltDevGetLocked(pPdo);
+ /*
+ * Prevent a host crash when vboxUsbFltDevGetLocked fails to locate the matching PDO
+ * in g_VBoxUsbFltGlobals.DeviceList (see @bugref{6509}).
+ */
+ if (pDevice == NULL)
+ {
+ WARN(("failed to get device for PDO(0x%p)", pPdo));
+ }
+ else if (pDevice->enmState == VBOXUSBFLT_DEVSTATE_CAPTURING)
+ {
+ pDevice->enmState = VBOXUSBFLT_DEVSTATE_CAPTURED;
+ LOG(("The proxy notified proxy start for the captured device 0x%p", pDevice));
+ vboxUsbFltDevRetain(pDevice);
+ }
+ else
+ {
+ WARN(("invalid state, %d", pDevice->enmState));
+ pDevice = NULL;
+ }
+ VBOXUSBFLT_LOCK_RELEASE();
+ return pDevice;
+}
+
+void VBoxUsbFltProxyStopped(HVBOXUSBFLTDEV hDev)
+{
+ PVBOXUSBFLT_DEVICE pDevice = (PVBOXUSBFLT_DEVICE)hDev;
+ /*
+ * Prevent a host crash when VBoxUsbFltProxyStarted fails, returning NULL.
+ * See @bugref{6509}.
+ */
+ if (pDevice == NULL)
+ {
+ WARN(("VBoxUsbFltProxyStopped called with NULL device pointer"));
+ return;
+ }
+ VBOXUSBFLT_LOCK_ACQUIRE();
+ if (pDevice->enmState == VBOXUSBFLT_DEVSTATE_CAPTURED
+ || pDevice->enmState == VBOXUSBFLT_DEVSTATE_USED_BY_GUEST)
+ {
+ /* this is due to devie was physically removed */
+ LOG(("The proxy notified proxy stop for the captured device 0x%p, current state %d", pDevice, pDevice->enmState));
+ pDevice->enmState = VBOXUSBFLT_DEVSTATE_CAPTURING;
+ }
+ else
+ {
+ if (pDevice->enmState != VBOXUSBFLT_DEVSTATE_REPLUGGING)
+ {
+ WARN(("invalid state, %d", pDevice->enmState));
+ }
+ }
+ VBOXUSBFLT_LOCK_RELEASE();
+
+ vboxUsbFltDevRelease(pDevice);
+}
+
+
+static NTSTATUS vboxUsbFltRegKeyQuery(PWSTR ValueName, ULONG ValueType, PVOID ValueData, ULONG ValueLength, PVOID Context, PVOID EntryContext)
+{
+ NTSTATUS Status = STATUS_SUCCESS;
+
+ RT_NOREF(ValueName, Context);
+ if ( ValueType == REG_DWORD
+ && ValueLength == sizeof(ULONG))
+ *(ULONG *)EntryContext = *(ULONG *)ValueData;
+ else
+ Status = STATUS_OBJECT_TYPE_MISMATCH;
+
+ return Status;
+}
+
+
+NTSTATUS VBoxUsbFltInit()
+{
+ int rc = VBoxUSBFilterInit();
+ if (RT_FAILURE(rc))
+ {
+ WARN(("VBoxUSBFilterInit failed, rc (%d)", rc));
+ return STATUS_UNSUCCESSFUL;
+ }
+
+ memset(&g_VBoxUsbFltGlobals, 0, sizeof (g_VBoxUsbFltGlobals));
+ InitializeListHead(&g_VBoxUsbFltGlobals.DeviceList);
+ InitializeListHead(&g_VBoxUsbFltGlobals.ContextList);
+ InitializeListHead(&g_VBoxUsbFltGlobals.BlackDeviceList);
+ vboxUsbFltBlDevPopulateWithKnownLocked();
+ VBOXUSBFLT_LOCK_INIT();
+
+ /*
+ * Check whether the setting to force replugging USB devices when
+ * querying string descriptors fail is set in the registry,
+ * see @bugref{9479}.
+ */
+ RTL_QUERY_REGISTRY_TABLE aParams[] =
+ {
+ {vboxUsbFltRegKeyQuery, 0, L"ForceReplugWhenDevPopulateFails", &g_VBoxUsbFltGlobals.dwForceReplugWhenDevPopulateFails, REG_DWORD, &g_VBoxUsbFltGlobals.dwForceReplugWhenDevPopulateFails, sizeof(ULONG) },
+ { NULL, 0, NULL, NULL, 0, 0, 0 }
+ };
+ UNICODE_STRING UnicodePath = RTL_CONSTANT_STRING(L"\\VBoxUSB");
+
+ NTSTATUS Status = RtlQueryRegistryValues(RTL_REGISTRY_CONTROL, UnicodePath.Buffer, &aParams[0], NULL, NULL);
+ if (Status == STATUS_SUCCESS)
+ {
+ if (g_VBoxUsbFltGlobals.dwForceReplugWhenDevPopulateFails)
+ LOG(("Forcing replug of USB devices where querying the descriptors fail\n"));
+ }
+ else
+ LOG(("RtlQueryRegistryValues() -> %#x, assuming defaults\n", Status));
+
+ return STATUS_SUCCESS;
+}
+
+NTSTATUS VBoxUsbFltTerm()
+{
+ bool bBusy = false;
+ VBOXUSBFLT_LOCK_ACQUIRE();
+ do
+ {
+ if (!IsListEmpty(&g_VBoxUsbFltGlobals.ContextList))
+ {
+ AssertFailed();
+ bBusy = true;
+ break;
+ }
+
+ PLIST_ENTRY pNext = NULL;
+ for (PLIST_ENTRY pEntry = g_VBoxUsbFltGlobals.DeviceList.Flink;
+ pEntry != &g_VBoxUsbFltGlobals.DeviceList;
+ pEntry = pNext)
+ {
+ pNext = pEntry->Flink;
+ PVBOXUSBFLT_DEVICE pDevice = PVBOXUSBFLT_DEVICE_FROM_LE(pEntry);
+ Assert(!pDevice->uFltId);
+ Assert(!pDevice->pOwner);
+ if (pDevice->cRefs != 1)
+ {
+ AssertFailed();
+ bBusy = true;
+ break;
+ }
+ }
+ } while (0);
+
+ VBOXUSBFLT_LOCK_RELEASE()
+
+ if (bBusy)
+ {
+ return STATUS_DEVICE_BUSY;
+ }
+
+ for (PLIST_ENTRY pEntry = g_VBoxUsbFltGlobals.DeviceList.Flink;
+ pEntry != &g_VBoxUsbFltGlobals.DeviceList;
+ pEntry = g_VBoxUsbFltGlobals.DeviceList.Flink)
+ {
+ RemoveEntryList(pEntry);
+ PVBOXUSBFLT_DEVICE pDevice = PVBOXUSBFLT_DEVICE_FROM_LE(pEntry);
+ pDevice->enmState = VBOXUSBFLT_DEVSTATE_REMOVED;
+ vboxUsbFltDevRelease(pDevice);
+ }
+
+ vboxUsbFltBlDevClearLocked();
+
+ VBOXUSBFLT_LOCK_TERM();
+
+ VBoxUSBFilterTerm();
+
+ return STATUS_SUCCESS;
+}
+
diff --git a/src/VBox/HostDrivers/VBoxUSB/win/mon/VBoxUsbFlt.h b/src/VBox/HostDrivers/VBoxUSB/win/mon/VBoxUsbFlt.h
new file mode 100644
index 00000000..2e8c7501
--- /dev/null
+++ b/src/VBox/HostDrivers/VBoxUSB/win/mon/VBoxUsbFlt.h
@@ -0,0 +1,75 @@
+/* $Id: VBoxUsbFlt.h $ */
+/** @file
+ * VBox USB Monitor Device Filtering functionality
+ */
+
+/*
+ * Copyright (C) 2011-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+#ifndef VBOX_INCLUDED_SRC_VBoxUSB_win_mon_VBoxUsbFlt_h
+#define VBOX_INCLUDED_SRC_VBoxUSB_win_mon_VBoxUsbFlt_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+#include "VBoxUsbMon.h"
+#include <VBoxUSBFilterMgr.h>
+
+#include <VBox/usblib-win.h>
+
+typedef struct VBOXUSBFLTCTX
+{
+ LIST_ENTRY ListEntry;
+ RTPROCESS Process; // Purely informational, no function?
+ uint32_t cActiveFilters;
+ BOOLEAN bRemoved; // For debugging only?
+} VBOXUSBFLTCTX, *PVBOXUSBFLTCTX;
+
+NTSTATUS VBoxUsbFltInit();
+NTSTATUS VBoxUsbFltTerm();
+NTSTATUS VBoxUsbFltCreate(PVBOXUSBFLTCTX pContext);
+NTSTATUS VBoxUsbFltClose(PVBOXUSBFLTCTX pContext);
+int VBoxUsbFltAdd(PVBOXUSBFLTCTX pContext, PUSBFILTER pFilter, uintptr_t *pId);
+int VBoxUsbFltRemove(PVBOXUSBFLTCTX pContext, uintptr_t uId);
+NTSTATUS VBoxUsbFltFilterCheck(PVBOXUSBFLTCTX pContext);
+
+NTSTATUS VBoxUsbFltGetDevice(PVBOXUSBFLTCTX pContext, HVBOXUSBDEVUSR hDevice, PUSBSUP_GETDEV_MON pInfo);
+
+typedef void* HVBOXUSBFLTDEV;
+HVBOXUSBFLTDEV VBoxUsbFltProxyStarted(PDEVICE_OBJECT pPdo);
+void VBoxUsbFltProxyStopped(HVBOXUSBFLTDEV hDev);
+
+NTSTATUS VBoxUsbFltPdoAdd(PDEVICE_OBJECT pPdo, BOOLEAN *pbFiltered);
+NTSTATUS VBoxUsbFltPdoRemove(PDEVICE_OBJECT pPdo);
+BOOLEAN VBoxUsbFltPdoIsFiltered(PDEVICE_OBJECT pPdo);
+
+#endif /* !VBOX_INCLUDED_SRC_VBoxUSB_win_mon_VBoxUsbFlt_h */
+
diff --git a/src/VBox/HostDrivers/VBoxUSB/win/mon/VBoxUsbHook.cpp b/src/VBox/HostDrivers/VBoxUSB/win/mon/VBoxUsbHook.cpp
new file mode 100644
index 00000000..83dc8de9
--- /dev/null
+++ b/src/VBox/HostDrivers/VBoxUSB/win/mon/VBoxUsbHook.cpp
@@ -0,0 +1,218 @@
+/* $Id: VBoxUsbHook.cpp $ */
+/** @file
+ * Driver Dispatch Table Hooking API
+ */
+
+/*
+ * Copyright (C) 2011-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include "VBoxUsbMon.h"
+
+
+/*********************************************************************************************************************************
+* Defined Constants And Macros *
+*********************************************************************************************************************************/
+#define VBOXUSBHOOK_MEMTAG 'HUBV'
+
+
+NTSTATUS VBoxUsbHookInstall(PVBOXUSBHOOK_ENTRY pHook)
+{
+ KIRQL Irql;
+ KeAcquireSpinLock(&pHook->Lock, &Irql);
+ if (pHook->fIsInstalled)
+ {
+ WARN(("hook is marked installed, returning failure"));
+ KeReleaseSpinLock(&pHook->Lock, Irql);
+ return STATUS_UNSUCCESSFUL;
+ }
+
+ pHook->pfnOldHandler = (PDRIVER_DISPATCH)InterlockedExchangePointer((PVOID*)&pHook->pDrvObj->MajorFunction[pHook->iMjFunction], pHook->pfnHook);
+ Assert(pHook->pfnOldHandler);
+ Assert(pHook->pfnHook != pHook->pfnOldHandler);
+ pHook->fIsInstalled = TRUE;
+ KeReleaseSpinLock(&pHook->Lock, Irql);
+ return STATUS_SUCCESS;
+
+}
+NTSTATUS VBoxUsbHookUninstall(PVBOXUSBHOOK_ENTRY pHook)
+{
+ KIRQL Irql;
+ KeAcquireSpinLock(&pHook->Lock, &Irql);
+ if (!pHook->fIsInstalled)
+ {
+ KeReleaseSpinLock(&pHook->Lock, Irql);
+ return STATUS_SUCCESS;
+ }
+
+ PDRIVER_DISPATCH pfnOldVal = (PDRIVER_DISPATCH)InterlockedCompareExchangePointer((PVOID*)&pHook->pDrvObj->MajorFunction[pHook->iMjFunction], pHook->pfnOldHandler, pHook->pfnHook);
+ Assert(pfnOldVal == pHook->pfnHook);
+ if (pfnOldVal != pHook->pfnHook)
+ {
+ AssertMsgFailed(("unhook failed!!!\n"));
+ /* this is bad! this could happen if someone else has chained another hook,
+ * or (which is even worse) restored the "initial" entry value it saved when doing a hooking before us
+ * return the failure and don't do anything else
+ * the best thing to do if this happens is to leave everything as is
+ * and to prevent the driver from being unloaded to ensure no one references our unloaded hook routine */
+ KeReleaseSpinLock(&pHook->Lock, Irql);
+ return STATUS_UNSUCCESSFUL;
+ }
+
+ pHook->fIsInstalled = FALSE;
+ KeReleaseSpinLock(&pHook->Lock, Irql);
+
+ /* wait for the current handlers to exit */
+ VBoxDrvToolRefWaitEqual(&pHook->HookRef, 1);
+
+ return STATUS_SUCCESS;
+}
+
+BOOLEAN VBoxUsbHookIsInstalled(PVBOXUSBHOOK_ENTRY pHook)
+{
+ KIRQL Irql;
+ BOOLEAN fIsInstalled;
+ KeAcquireSpinLock(&pHook->Lock, &Irql);
+ fIsInstalled = pHook->fIsInstalled;
+ KeReleaseSpinLock(&pHook->Lock, Irql);
+ return fIsInstalled;
+}
+
+VOID VBoxUsbHookInit(PVBOXUSBHOOK_ENTRY pHook, PDRIVER_OBJECT pDrvObj, UCHAR iMjFunction, PDRIVER_DISPATCH pfnHook)
+{
+ Assert(pDrvObj);
+ Assert(iMjFunction <= IRP_MJ_MAXIMUM_FUNCTION);
+ Assert(pfnHook);
+ memset(pHook, 0, sizeof (*pHook));
+ InitializeListHead(&pHook->RequestList);
+ KeInitializeSpinLock(&pHook->Lock);
+ VBoxDrvToolRefInit(&pHook->HookRef);
+ pHook->pDrvObj = pDrvObj;
+ pHook->iMjFunction = iMjFunction;
+ pHook->pfnHook = pfnHook;
+ Assert(!pHook->pfnOldHandler);
+ Assert(!pHook->fIsInstalled);
+
+}
+
+static void vboxUsbHookRequestRegisterCompletion(PVBOXUSBHOOK_ENTRY pHook, PDEVICE_OBJECT pDevObj, PIRP pIrp, PIO_COMPLETION_ROUTINE pfnCompletion, PVBOXUSBHOOK_REQUEST pRequest)
+{
+ Assert(pfnCompletion);
+ Assert(pRequest);
+ Assert(pDevObj);
+ Assert(pIrp);
+ PIO_STACK_LOCATION pSl = IoGetCurrentIrpStackLocation(pIrp);
+ memset(pRequest, 0, sizeof (*pRequest));
+ pRequest->pHook = pHook;
+ pRequest->OldLocation = *pSl;
+ pRequest->pDevObj = pDevObj;
+ pRequest->pIrp = pIrp;
+ pRequest->bCompletionStopped = FALSE;
+ pSl->CompletionRoutine = pfnCompletion;
+ pSl->Context = pRequest;
+ pSl->Control = SL_INVOKE_ON_SUCCESS | SL_INVOKE_ON_ERROR | SL_INVOKE_ON_CANCEL;
+
+ KIRQL oldIrql;
+ KeAcquireSpinLock(&pHook->Lock, &oldIrql);
+ InsertTailList(&pHook->RequestList, &pRequest->ListEntry);
+ KeReleaseSpinLock(&pHook->Lock, oldIrql);
+}
+
+NTSTATUS VBoxUsbHookRequestPassDownHookCompletion(PVBOXUSBHOOK_ENTRY pHook, PDEVICE_OBJECT pDevObj, PIRP pIrp, PIO_COMPLETION_ROUTINE pfnCompletion, PVBOXUSBHOOK_REQUEST pRequest)
+{
+ Assert(pfnCompletion);
+ vboxUsbHookRequestRegisterCompletion(pHook, pDevObj, pIrp, pfnCompletion, pRequest);
+ return pHook->pfnOldHandler(pDevObj, pIrp);
+}
+
+NTSTATUS VBoxUsbHookRequestPassDownHookSkip(PVBOXUSBHOOK_ENTRY pHook, PDEVICE_OBJECT pDevObj, PIRP pIrp)
+{
+ return pHook->pfnOldHandler(pDevObj, pIrp);
+}
+
+NTSTATUS VBoxUsbHookRequestMoreProcessingRequired(PVBOXUSBHOOK_ENTRY pHook, PDEVICE_OBJECT pDevObj, PIRP pIrp,
+ PVBOXUSBHOOK_REQUEST pRequest)
+{
+ RT_NOREF3(pHook, pDevObj, pIrp);
+ Assert(!pRequest->bCompletionStopped);
+ pRequest->bCompletionStopped = TRUE;
+ return STATUS_MORE_PROCESSING_REQUIRED;
+}
+
+NTSTATUS VBoxUsbHookRequestComplete(PVBOXUSBHOOK_ENTRY pHook, PDEVICE_OBJECT pDevObj, PIRP pIrp, PVBOXUSBHOOK_REQUEST pRequest)
+{
+ NTSTATUS Status = STATUS_SUCCESS;
+
+ if (pRequest->OldLocation.CompletionRoutine && pRequest->OldLocation.Control)
+ {
+ Status = pRequest->OldLocation.CompletionRoutine(pDevObj, pIrp, pRequest->OldLocation.Context);
+ }
+
+ if (Status != STATUS_MORE_PROCESSING_REQUIRED)
+ {
+ if (pRequest->bCompletionStopped)
+ {
+ IoCompleteRequest(pIrp, IO_NO_INCREMENT);
+ }
+ }
+ /*
+ * else - in case driver returned STATUS_MORE_PROCESSING_REQUIRED,
+ * it will call IoCompleteRequest itself
+ */
+
+ KIRQL oldIrql;
+ KeAcquireSpinLock(&pHook->Lock, &oldIrql);
+ RemoveEntryList(&pRequest->ListEntry);
+ KeReleaseSpinLock(&pHook->Lock, oldIrql);
+ return Status;
+}
+
+#define PVBOXUSBHOOK_REQUEST_FROM_LE(_pLe) ( (PVBOXUSBHOOK_REQUEST)( ((uint8_t*)(_pLe)) - RT_OFFSETOF(VBOXUSBHOOK_REQUEST, ListEntry) ) )
+
+VOID VBoxUsbHookVerifyCompletion(PVBOXUSBHOOK_ENTRY pHook, PVBOXUSBHOOK_REQUEST pRequest, PIRP pIrp)
+{
+ KIRQL oldIrql;
+ KeAcquireSpinLock(&pHook->Lock, &oldIrql);
+ for (PLIST_ENTRY pLe = pHook->RequestList.Flink; pLe != &pHook->RequestList; pLe = pLe->Flink)
+ {
+ PVBOXUSBHOOK_REQUEST pCur = PVBOXUSBHOOK_REQUEST_FROM_LE(pLe);
+ if (pCur != pRequest)
+ continue;
+ if (pCur->pIrp != pIrp)
+ continue;
+ WARN(("found pending IRP(0x%p) when it should not be", pIrp));
+ }
+ KeReleaseSpinLock(&pHook->Lock, oldIrql);
+
+}
diff --git a/src/VBox/HostDrivers/VBoxUSB/win/mon/VBoxUsbHook.h b/src/VBox/HostDrivers/VBoxUSB/win/mon/VBoxUsbHook.h
new file mode 100644
index 00000000..f9207297
--- /dev/null
+++ b/src/VBox/HostDrivers/VBoxUSB/win/mon/VBoxUsbHook.h
@@ -0,0 +1,96 @@
+/* $Id: VBoxUsbHook.h $ */
+/** @file
+ * Driver Dispatch Table Hooking API impl
+ */
+/*
+ * Copyright (C) 2011-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+#ifndef VBOX_INCLUDED_SRC_VBoxUSB_win_mon_VBoxUsbHook_h
+#define VBOX_INCLUDED_SRC_VBoxUSB_win_mon_VBoxUsbHook_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+#include "VBoxUsbMon.h"
+
+typedef struct VBOXUSBHOOK_ENTRY
+{
+ LIST_ENTRY RequestList;
+ KSPIN_LOCK Lock;
+ BOOLEAN fIsInstalled;
+ PDRIVER_DISPATCH pfnOldHandler;
+ VBOXDRVTOOL_REF HookRef;
+ PDRIVER_OBJECT pDrvObj;
+ UCHAR iMjFunction;
+ PDRIVER_DISPATCH pfnHook;
+} VBOXUSBHOOK_ENTRY, *PVBOXUSBHOOK_ENTRY;
+
+typedef struct VBOXUSBHOOK_REQUEST
+{
+ LIST_ENTRY ListEntry;
+ PVBOXUSBHOOK_ENTRY pHook;
+ IO_STACK_LOCATION OldLocation;
+ PDEVICE_OBJECT pDevObj;
+ PIRP pIrp;
+ BOOLEAN bCompletionStopped;
+} VBOXUSBHOOK_REQUEST, *PVBOXUSBHOOK_REQUEST;
+
+DECLINLINE(BOOLEAN) VBoxUsbHookRetain(PVBOXUSBHOOK_ENTRY pHook)
+{
+ KIRQL Irql;
+ KeAcquireSpinLock(&pHook->Lock, &Irql);
+ if (!pHook->fIsInstalled)
+ {
+ KeReleaseSpinLock(&pHook->Lock, Irql);
+ return FALSE;
+ }
+
+ VBoxDrvToolRefRetain(&pHook->HookRef);
+ KeReleaseSpinLock(&pHook->Lock, Irql);
+ return TRUE;
+}
+
+DECLINLINE(VOID) VBoxUsbHookRelease(PVBOXUSBHOOK_ENTRY pHook)
+{
+ VBoxDrvToolRefRelease(&pHook->HookRef);
+}
+
+VOID VBoxUsbHookInit(PVBOXUSBHOOK_ENTRY pHook, PDRIVER_OBJECT pDrvObj, UCHAR iMjFunction, PDRIVER_DISPATCH pfnHook);
+NTSTATUS VBoxUsbHookInstall(PVBOXUSBHOOK_ENTRY pHook);
+NTSTATUS VBoxUsbHookUninstall(PVBOXUSBHOOK_ENTRY pHook);
+BOOLEAN VBoxUsbHookIsInstalled(PVBOXUSBHOOK_ENTRY pHook);
+NTSTATUS VBoxUsbHookRequestPassDownHookCompletion(PVBOXUSBHOOK_ENTRY pHook, PDEVICE_OBJECT pDevObj, PIRP pIrp, PIO_COMPLETION_ROUTINE pfnCompletion, PVBOXUSBHOOK_REQUEST pRequest);
+NTSTATUS VBoxUsbHookRequestPassDownHookSkip(PVBOXUSBHOOK_ENTRY pHook, PDEVICE_OBJECT pDevObj, PIRP pIrp);
+NTSTATUS VBoxUsbHookRequestMoreProcessingRequired(PVBOXUSBHOOK_ENTRY pHook, PDEVICE_OBJECT pDevObj, PIRP pIrp, PVBOXUSBHOOK_REQUEST pRequest);
+NTSTATUS VBoxUsbHookRequestComplete(PVBOXUSBHOOK_ENTRY pHook, PDEVICE_OBJECT pDevObj, PIRP pIrp, PVBOXUSBHOOK_REQUEST pRequest);
+VOID VBoxUsbHookVerifyCompletion(PVBOXUSBHOOK_ENTRY pHook, PVBOXUSBHOOK_REQUEST pRequest, PIRP pIrp);
+
+#endif /* !VBOX_INCLUDED_SRC_VBoxUSB_win_mon_VBoxUsbHook_h */
diff --git a/src/VBox/HostDrivers/VBoxUSB/win/mon/VBoxUsbMon.cpp b/src/VBox/HostDrivers/VBoxUSB/win/mon/VBoxUsbMon.cpp
new file mode 100644
index 00000000..349893bd
--- /dev/null
+++ b/src/VBox/HostDrivers/VBoxUSB/win/mon/VBoxUsbMon.cpp
@@ -0,0 +1,1568 @@
+/* $Id: VBoxUsbMon.cpp $ */
+/** @file
+ * VBox USB Monitor
+ */
+
+/*
+ * Copyright (C) 2011-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*
+ *
+ * Theory of Operation
+ * - or -
+ * The Document I Wish The Original Author Had Written
+ *
+ *
+ * The USB Monitor (VBoxUSBMon.sys) serves to capture and uncapture USB
+ * devices. Its job is to ensure that the USB proxy (VBoxUSB.sys) gets installed
+ * for captured devices and removed again when not needed, restoring the regular
+ * driver (if any).
+ *
+ * The USB Monitor does not handle any actual USB traffic; that is the role of
+ * VBoxUSB.sys, the USB proxy. A typical solution for installing such USB proxy
+ * is using a filter driver, but that approach was rejected because filter drivers
+ * cannot be dynamically added and removed. What VBoxUSBMon does instead is hook
+ * into the dispatch routine of the bus driver, i.e. USB hub driver, and alter
+ * the PnP information returned by the bus driver.
+ *
+ * The key functionality for capturing is cycling a USB port (which causes a USB
+ * device reset and triggers re-enumeration in the Windows USB driver stack), and
+ * then modifying IRP_MN_QUERY_ID / BusQueryHardwareIDs and related requests so
+ * that they return the synthetic USB VID/PID that VBoxUSB.sys handles rather than
+ * the true hardware VID/PID. That causes Windows to install VBoxUSB.sys for the
+ * device.
+ *
+ * Uncapturing again cycles the USB port but returns unmodified hardware IDs,
+ * causing Windows to load the normal driver for the device.
+ *
+ * Identifying devices to capture or release (uncapture) is done through USB filters,
+ * a cross-platform concept which matches USB device based on their VID/PID, class,
+ * and other criteria.
+ *
+ * There is an IOCTL interface for adding/removing USB filters and applying them.
+ * The IOCTLs are normally issued by VBoxSVC.
+ *
+ * USB devices are enumerated by finding all USB hubs (GUID_DEVINTERFACE_USB_HUB)
+ * and querying their child devices (i.e. USB devices or other hubs) by sending
+ * IRP_MJ_PNP / IRP_MN_QUERY_DEVICE_RELATIONS / BusRelations. This is done when
+ * applying existing filters.
+ *
+ * Newly arrived USB devices are intercepted early in their PnP enumeration
+ * through the hooked bus driver dispatch routine. Devices which satisty the
+ * filter matching criteria are morphed (see above) such that VBoxUSB.sys loads
+ * for them before any default driver does.
+ *
+ * There is an IDC interface to VBoxUSB.sys which allows the USB proxy to report
+ * that it's installed for a given USB device, and also report when the USB proxy
+ * is unloaded (typically caused by either unplugging the device or uncapturing
+ * and cycling the port). VBoxUSBMon.sys relies on these IDC calls to track
+ * captured devices and be informed when VBoxUSB.sys unloads.
+ *
+ * Windows 8+ complicates the USB Monitor's life by automatically putting some
+ * USB devices to a low-power state where they are unable to respond to any USB
+ * requests and VBoxUSBMon can't read any of their descriptors (note that in
+ * userland, the device descriptor can always be read, but string descriptors
+ * can't). Such devices' USB VID/PID/revision is recovered using the Windows
+ * PnP Manager from their DevicePropertyHardwareID, but their USB class/subclass
+ * and protocol unfortunately cannot be unambiguously recovered from their
+ * DevicePropertyCompatibleIDs.
+ *
+ * Filter drivers add another complication. With filter drivers in place, the
+ * device objects returned by the BusRelations query (or passing through the PnP
+ * hooks) may not be PDOs but rather filter DOs higher in the stack. To avoid
+ * confusion, we flatten the references to their base, i.e. the real PDO, which
+ * should remain the same for the lifetime of a device. Note that VBoxUSB.sys
+ * always passes its own PDO in the proxy startup IOCTL.
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include "VBoxUsbMon.h"
+#include "../cmn/VBoxUsbIdc.h"
+#include <iprt/errcore.h>
+#include <VBox/usblib.h>
+#include <excpt.h>
+
+
+/*********************************************************************************************************************************
+* Defined Constants And Macros *
+*********************************************************************************************************************************/
+#define VBOXUSBMON_MEMTAG 'MUBV'
+
+
+/*********************************************************************************************************************************
+* Structures and Typedefs *
+*********************************************************************************************************************************/
+typedef struct VBOXUSBMONINS
+{
+ void * pvDummy;
+} VBOXUSBMONINS, *PVBOXUSBMONINS;
+
+typedef struct VBOXUSBMONCTX
+{
+ VBOXUSBFLTCTX FltCtx;
+} VBOXUSBMONCTX, *PVBOXUSBMONCTX;
+
+typedef struct VBOXUSBHUB_PNPHOOK
+{
+ VBOXUSBHOOK_ENTRY Hook;
+ bool fUninitFailed;
+} VBOXUSBHUB_PNPHOOK, *PVBOXUSBHUB_PNPHOOK;
+
+typedef struct VBOXUSBHUB_PNPHOOK_COMPLETION
+{
+ VBOXUSBHOOK_REQUEST Rq;
+} VBOXUSBHUB_PNPHOOK_COMPLETION, *PVBOXUSBHUB_PNPHOOK_COMPLETION;
+
+#define VBOXUSBMON_MAXDRIVERS 5
+typedef struct VBOXUSB_PNPDRIVER
+{
+ PDRIVER_OBJECT DriverObject;
+ VBOXUSBHUB_PNPHOOK UsbHubPnPHook;
+ PDRIVER_DISPATCH pfnHookStub;
+} VBOXUSB_PNPDRIVER, *PVBOXUSB_PNPDRIVER;
+
+typedef struct VBOXUSBMONGLOBALS
+{
+ PDEVICE_OBJECT pDevObj;
+ VBOXUSB_PNPDRIVER pDrivers[VBOXUSBMON_MAXDRIVERS];
+ KEVENT OpenSynchEvent;
+ IO_REMOVE_LOCK RmLock;
+ uint32_t cOpens;
+ volatile LONG ulPreventUnloadOn;
+ PFILE_OBJECT pPreventUnloadFileObj;
+} VBOXUSBMONGLOBALS, *PVBOXUSBMONGLOBALS;
+
+
+/*********************************************************************************************************************************
+* Global Variables *
+*********************************************************************************************************************************/
+static VBOXUSBMONGLOBALS g_VBoxUsbMonGlobals;
+
+/*
+ * Note: Must match the VID & PID in the USB driver .inf file!!
+ */
+/*
+ BusQueryDeviceID USB\Vid_80EE&Pid_CAFE
+ BusQueryInstanceID 2
+ BusQueryHardwareIDs USB\Vid_80EE&Pid_CAFE&Rev_0100
+ BusQueryHardwareIDs USB\Vid_80EE&Pid_CAFE
+ BusQueryCompatibleIDs USB\Class_ff&SubClass_00&Prot_00
+ BusQueryCompatibleIDs USB\Class_ff&SubClass_00
+ BusQueryCompatibleIDs USB\Class_ff
+*/
+
+static WCHAR const g_szBusQueryDeviceId[] = L"USB\\Vid_80EE&Pid_CAFE";
+static WCHAR const g_szBusQueryHardwareIDs[] = L"USB\\Vid_80EE&Pid_CAFE&Rev_0100\0USB\\Vid_80EE&Pid_CAFE\0\0";
+static WCHAR const g_szBusQueryCompatibleIDs[] = L"USB\\Class_ff&SubClass_00&Prot_00\0USB\\Class_ff&SubClass_00\0USB\\Class_ff\0\0";
+static WCHAR const g_szDeviceTextDescription[] = L"VirtualBox USB";
+
+
+
+PVOID VBoxUsbMonMemAlloc(SIZE_T cbBytes)
+{
+ PVOID pvMem = ExAllocatePoolWithTag(NonPagedPool, cbBytes, VBOXUSBMON_MEMTAG);
+ Assert(pvMem);
+ return pvMem;
+}
+
+PVOID VBoxUsbMonMemAllocZ(SIZE_T cbBytes)
+{
+ PVOID pvMem = VBoxUsbMonMemAlloc(cbBytes);
+ if (pvMem)
+ {
+ RtlZeroMemory(pvMem, cbBytes);
+ }
+ return pvMem;
+}
+
+VOID VBoxUsbMonMemFree(PVOID pvMem)
+{
+ ExFreePoolWithTag(pvMem, VBOXUSBMON_MEMTAG);
+}
+
+#define VBOXUSBDBG_STRCASE(_t) \
+ case _t: return #_t
+#define VBOXUSBDBG_STRCASE_UNKNOWN(_v) \
+ default: LOG((__FUNCTION__": Unknown Value (0n%d), (0x%x)", _v, _v)); return "Unknown"
+
+/* These minor code are semi-undocumented. */
+#ifndef IRP_MN_QUERY_LEGACY_BUS_INFORMATION
+#define IRP_MN_QUERY_LEGACY_BUS_INFORMATION 0x18
+#endif
+#ifndef IRP_MN_DEVICE_ENUMERATED
+#define IRP_MN_DEVICE_ENUMERATED 0x19
+#endif
+
+static const char* vboxUsbDbgStrPnPMn(UCHAR uMn)
+{
+ switch (uMn)
+ {
+ VBOXUSBDBG_STRCASE(IRP_MN_START_DEVICE);
+ VBOXUSBDBG_STRCASE(IRP_MN_QUERY_REMOVE_DEVICE);
+ VBOXUSBDBG_STRCASE(IRP_MN_REMOVE_DEVICE);
+ VBOXUSBDBG_STRCASE(IRP_MN_CANCEL_REMOVE_DEVICE);
+ VBOXUSBDBG_STRCASE(IRP_MN_STOP_DEVICE);
+ VBOXUSBDBG_STRCASE(IRP_MN_QUERY_STOP_DEVICE);
+ VBOXUSBDBG_STRCASE(IRP_MN_CANCEL_STOP_DEVICE);
+ VBOXUSBDBG_STRCASE(IRP_MN_QUERY_DEVICE_RELATIONS);
+ VBOXUSBDBG_STRCASE(IRP_MN_QUERY_INTERFACE);
+ VBOXUSBDBG_STRCASE(IRP_MN_QUERY_CAPABILITIES);
+ VBOXUSBDBG_STRCASE(IRP_MN_QUERY_RESOURCES);
+ VBOXUSBDBG_STRCASE(IRP_MN_QUERY_RESOURCE_REQUIREMENTS);
+ VBOXUSBDBG_STRCASE(IRP_MN_QUERY_DEVICE_TEXT);
+ VBOXUSBDBG_STRCASE(IRP_MN_FILTER_RESOURCE_REQUIREMENTS);
+ VBOXUSBDBG_STRCASE(IRP_MN_READ_CONFIG);
+ VBOXUSBDBG_STRCASE(IRP_MN_WRITE_CONFIG);
+ VBOXUSBDBG_STRCASE(IRP_MN_EJECT);
+ VBOXUSBDBG_STRCASE(IRP_MN_SET_LOCK);
+ VBOXUSBDBG_STRCASE(IRP_MN_QUERY_ID);
+ VBOXUSBDBG_STRCASE(IRP_MN_QUERY_PNP_DEVICE_STATE);
+ VBOXUSBDBG_STRCASE(IRP_MN_QUERY_BUS_INFORMATION);
+ VBOXUSBDBG_STRCASE(IRP_MN_DEVICE_USAGE_NOTIFICATION);
+ VBOXUSBDBG_STRCASE(IRP_MN_SURPRISE_REMOVAL);
+ VBOXUSBDBG_STRCASE(IRP_MN_QUERY_LEGACY_BUS_INFORMATION);
+ VBOXUSBDBG_STRCASE(IRP_MN_DEVICE_ENUMERATED);
+ VBOXUSBDBG_STRCASE_UNKNOWN(uMn);
+ }
+}
+
+/**
+ * Send IRP_MN_QUERY_DEVICE_RELATIONS
+ *
+ * @returns NT Status
+ * @param pDevObj USB device pointer
+ * @param pFileObj Valid file object pointer
+ * @param pDevRelations Pointer to DEVICE_RELATIONS pointer (out)
+ */
+NTSTATUS VBoxUsbMonQueryBusRelations(PDEVICE_OBJECT pDevObj, PFILE_OBJECT pFileObj, PDEVICE_RELATIONS *pDevRelations)
+{
+ IO_STATUS_BLOCK IoStatus;
+ KEVENT Event;
+ NTSTATUS Status;
+ PIRP pIrp;
+ PIO_STACK_LOCATION pSl;
+
+ KeInitializeEvent(&Event, NotificationEvent, FALSE);
+
+ Assert(pDevRelations);
+ *pDevRelations = NULL;
+
+ pIrp = IoBuildSynchronousFsdRequest(IRP_MJ_PNP, pDevObj, NULL, 0, NULL, &Event, &IoStatus);
+ if (!pIrp)
+ {
+ WARN(("IoBuildDeviceIoControlRequest failed!!"));
+ return STATUS_INSUFFICIENT_RESOURCES;
+ }
+ pIrp->IoStatus.Status = STATUS_NOT_SUPPORTED;
+
+ pSl = IoGetNextIrpStackLocation(pIrp);
+ pSl->MajorFunction = IRP_MJ_PNP;
+ pSl->MinorFunction = IRP_MN_QUERY_DEVICE_RELATIONS;
+ pSl->Parameters.QueryDeviceRelations.Type = BusRelations;
+ pSl->FileObject = pFileObj;
+
+ Status = IoCallDriver(pDevObj, pIrp);
+ if (Status == STATUS_PENDING)
+ {
+ LOG(("IoCallDriver returned STATUS_PENDING!!"));
+ KeWaitForSingleObject(&Event, Executive, KernelMode, FALSE, NULL);
+ Status = IoStatus.Status;
+ }
+
+ if (Status == STATUS_SUCCESS)
+ {
+ PDEVICE_RELATIONS pRel = (PDEVICE_RELATIONS)IoStatus.Information;
+ LOG(("pRel = %p", pRel));
+ if (RT_VALID_PTR(pRel))
+ *pDevRelations = pRel;
+ else
+ {
+ WARN(("Invalid pointer %p", pRel));
+ }
+ }
+ else
+ {
+ WARN(("IRP_MN_QUERY_DEVICE_RELATIONS failed Status(0x%x)", Status));
+ }
+
+ LOG(("IoCallDriver returned %x", Status));
+ return Status;
+}
+
+VOID vboxUsbMonHubDevWalk(PFNVBOXUSBMONDEVWALKER pfnWalker, PVOID pvWalker)
+{
+ NTSTATUS Status = STATUS_UNSUCCESSFUL;
+ PWSTR szwHubList;
+ Status = IoGetDeviceInterfaces(&GUID_DEVINTERFACE_USB_HUB, NULL, 0, &szwHubList);
+ if (Status != STATUS_SUCCESS)
+ {
+ LOG(("IoGetDeviceInterfaces failed with %d\n", Status));
+ return;
+ }
+ if (szwHubList)
+ {
+ UNICODE_STRING UnicodeName;
+ PDEVICE_OBJECT pHubDevObj;
+ PFILE_OBJECT pHubFileObj;
+ PWSTR szwHubName = szwHubList;
+ while (*szwHubName != UNICODE_NULL)
+ {
+ RtlInitUnicodeString(&UnicodeName, szwHubName);
+ Status = IoGetDeviceObjectPointer(&UnicodeName, FILE_READ_DATA, &pHubFileObj, &pHubDevObj);
+ if (Status == STATUS_SUCCESS)
+ {
+ /* We could not log hub name here.
+ * It is the paged memory and we cannot use it in logger cause it increases the IRQL
+ */
+ LOG(("IoGetDeviceObjectPointer returned %p %p", pHubDevObj, pHubFileObj));
+ if (!pfnWalker(pHubFileObj, pHubDevObj, pvWalker))
+ {
+ LOG(("the walker said to stop"));
+ ObDereferenceObject(pHubFileObj);
+ break;
+ }
+
+ LOG(("going forward.."));
+ ObDereferenceObject(pHubFileObj);
+ }
+ szwHubName += wcslen(szwHubName) + 1;
+ }
+ ExFreePool(szwHubList);
+ }
+}
+
+/* NOTE: the stack location data is not the "actual" IRP stack location,
+ * but a copy being preserved on the IRP way down.
+ * See the note in VBoxUsbPnPCompletion for detail */
+static NTSTATUS vboxUsbMonHandlePnPIoctl(PDEVICE_OBJECT pDevObj, PIO_STACK_LOCATION pSl, PIO_STATUS_BLOCK pIoStatus)
+{
+ LOG(("IRQL = %d", KeGetCurrentIrql()));
+ switch(pSl->MinorFunction)
+ {
+ case IRP_MN_QUERY_DEVICE_TEXT:
+ {
+ LOG(("IRP_MN_QUERY_DEVICE_TEXT: pIoStatus->Status = %x", pIoStatus->Status));
+ if (pIoStatus->Status == STATUS_SUCCESS)
+ {
+ WCHAR *pId = (WCHAR *)pIoStatus->Information;
+ if (RT_VALID_PTR(pId))
+ {
+ KIRQL Iqrl = KeGetCurrentIrql();
+ /* IRQL should be always passive here */
+ ASSERT_WARN(Iqrl == PASSIVE_LEVEL, ("irql is not PASSIVE"));
+ switch (pSl->Parameters.QueryDeviceText.DeviceTextType)
+ {
+ case DeviceTextLocationInformation:
+ LOG(("DeviceTextLocationInformation"));
+ LOG_STRW(pId);
+ break;
+
+ case DeviceTextDescription:
+ LOG(("DeviceTextDescription"));
+ LOG_STRW(pId);
+ if (VBoxUsbFltPdoIsFiltered(pDevObj))
+ {
+ LOG(("PDO (0x%p) is filtered", pDevObj));
+ WCHAR *pId2 = (WCHAR *)ExAllocatePool(PagedPool, sizeof(g_szDeviceTextDescription));
+ AssertBreak(pId2);
+ memcpy(pId2, g_szDeviceTextDescription, sizeof(g_szDeviceTextDescription));
+ LOG(("NEW szDeviceTextDescription"));
+ LOG_STRW(pId2);
+ ExFreePool((PVOID)pIoStatus->Information);
+ pIoStatus->Information = (ULONG_PTR)pId2;
+ }
+ else
+ {
+ LOG(("PDO (0x%p) is NOT filtered", pDevObj));
+ }
+ break;
+ default:
+ LOG(("DeviceText %d", pSl->Parameters.QueryDeviceText.DeviceTextType));
+ break;
+ }
+ }
+ else
+ LOG(("Invalid pointer %p", pId));
+ }
+ break;
+ }
+
+ case IRP_MN_QUERY_ID:
+ {
+ LOG(("IRP_MN_QUERY_ID: Irp->pIoStatus->Status = %x", pIoStatus->Status));
+ if (pIoStatus->Status == STATUS_SUCCESS && pDevObj)
+ {
+ WCHAR *pId = (WCHAR *)pIoStatus->Information;
+#ifdef VBOX_USB_WITH_VERBOSE_LOGGING
+ WCHAR *pTmp;
+#endif
+ if (RT_VALID_PTR(pId))
+ {
+ KIRQL Iqrl = KeGetCurrentIrql();
+ /* IRQL should be always passive here */
+ ASSERT_WARN(Iqrl == PASSIVE_LEVEL, ("irql is not PASSIVE"));
+
+ switch (pSl->Parameters.QueryId.IdType)
+ {
+ case BusQueryInstanceID:
+ LOG(("BusQueryInstanceID"));
+ LOG_STRW(pId);
+ break;
+
+ case BusQueryDeviceID:
+ {
+ LOG(("BusQueryDeviceID"));
+ pId = (WCHAR *)ExAllocatePool(PagedPool, sizeof(g_szBusQueryDeviceId));
+ if (!pId)
+ {
+ WARN(("ExAllocatePool failed"));
+ break;
+ }
+
+ BOOLEAN bFiltered = FALSE;
+ NTSTATUS Status = VBoxUsbFltPdoAdd(pDevObj, &bFiltered);
+ if (Status != STATUS_SUCCESS || !bFiltered)
+ {
+ if (Status == STATUS_SUCCESS)
+ {
+ LOG(("PDO (0x%p) is NOT filtered", pDevObj));
+ }
+ else
+ {
+ WARN(("VBoxUsbFltPdoAdd for PDO (0x%p) failed Status 0x%x", pDevObj, Status));
+ }
+ ExFreePool(pId);
+ break;
+ }
+
+ LOG(("PDO (0x%p) is filtered", pDevObj));
+ ExFreePool((PVOID)pIoStatus->Information);
+ memcpy(pId, g_szBusQueryDeviceId, sizeof(g_szBusQueryDeviceId));
+ pIoStatus->Information = (ULONG_PTR)pId;
+ break;
+ }
+ case BusQueryHardwareIDs:
+ {
+ LOG(("BusQueryHardwareIDs"));
+#ifdef VBOX_USB_WITH_VERBOSE_LOGGING
+ while (*pId) //MULTI_SZ
+ {
+ LOG_STRW(pId);
+ while (*pId) pId++;
+ pId++;
+ }
+#endif
+ pId = (WCHAR *)ExAllocatePool(PagedPool, sizeof(g_szBusQueryHardwareIDs));
+ if (!pId)
+ {
+ WARN(("ExAllocatePool failed"));
+ break;
+ }
+
+ BOOLEAN bFiltered = FALSE;
+ NTSTATUS Status = VBoxUsbFltPdoAdd(pDevObj, &bFiltered);
+ if (Status != STATUS_SUCCESS || !bFiltered)
+ {
+ if (Status == STATUS_SUCCESS)
+ {
+ LOG(("PDO (0x%p) is NOT filtered", pDevObj));
+ }
+ else
+ {
+ WARN(("VBoxUsbFltPdoAdd for PDO (0x%p) failed Status 0x%x", pDevObj, Status));
+ }
+ ExFreePool(pId);
+ break;
+ }
+
+ LOG(("PDO (0x%p) is filtered", pDevObj));
+
+ memcpy(pId, g_szBusQueryHardwareIDs, sizeof(g_szBusQueryHardwareIDs));
+#ifdef VBOX_USB_WITH_VERBOSE_LOGGING
+ LOG(("NEW BusQueryHardwareIDs"));
+ pTmp = pId;
+ while (*pTmp) //MULTI_SZ
+ {
+
+ LOG_STRW(pTmp);
+ while (*pTmp) pTmp++;
+ pTmp++;
+ }
+#endif
+ ExFreePool((PVOID)pIoStatus->Information);
+ pIoStatus->Information = (ULONG_PTR)pId;
+ break;
+ }
+ case BusQueryCompatibleIDs:
+ LOG(("BusQueryCompatibleIDs"));
+#ifdef VBOX_USB_WITH_VERBOSE_LOGGING
+ while (*pId) //MULTI_SZ
+ {
+ LOG_STRW(pId);
+ while (*pId) pId++;
+ pId++;
+ }
+#endif
+ if (VBoxUsbFltPdoIsFiltered(pDevObj))
+ {
+ LOG(("PDO (0x%p) is filtered", pDevObj));
+ pId = (WCHAR *)ExAllocatePool(PagedPool, sizeof(g_szBusQueryCompatibleIDs));
+ if (!pId)
+ {
+ WARN(("ExAllocatePool failed"));
+ break;
+ }
+ memcpy(pId, g_szBusQueryCompatibleIDs, sizeof(g_szBusQueryCompatibleIDs));
+#ifdef VBOX_USB_WITH_VERBOSE_LOGGING
+ LOG(("NEW BusQueryCompatibleIDs"));
+ pTmp = pId;
+ while (*pTmp) //MULTI_SZ
+ {
+ LOG_STRW(pTmp);
+ while (*pTmp) pTmp++;
+ pTmp++;
+ }
+#endif
+ ExFreePool((PVOID)pIoStatus->Information);
+ pIoStatus->Information = (ULONG_PTR)pId;
+ }
+ else
+ {
+ LOG(("PDO (0x%p) is NOT filtered", pDevObj));
+ }
+ break;
+
+ default:
+ /** @todo r=bird: handle BusQueryContainerID and whatever else we might see */
+ break;
+ }
+ }
+ else
+ {
+ LOG(("Invalid pointer %p", pId));
+ }
+ }
+ break;
+ }
+
+#ifdef VBOX_USB_WITH_VERBOSE_LOGGING
+ case IRP_MN_QUERY_DEVICE_RELATIONS:
+ {
+ switch(pSl->Parameters.QueryDeviceRelations.Type)
+ {
+ case BusRelations:
+ LOG(("BusRelations"));
+
+ if (pIoStatus->Status == STATUS_SUCCESS)
+ {
+ PDEVICE_RELATIONS pRel = (PDEVICE_RELATIONS)pIoStatus->Information;
+ LOG(("pRel = %p", pRel));
+ if (RT_VALID_PTR(pRel))
+ {
+ for (unsigned i=0;i<pRel->Count;i++)
+ {
+ if (VBoxUsbFltPdoIsFiltered(pDevObj))
+ LOG(("New PDO %p", pRel->Objects[i]));
+ }
+ }
+ else
+ LOG(("Invalid pointer %p", pRel));
+ }
+ break;
+ case TargetDeviceRelation:
+ LOG(("TargetDeviceRelation"));
+ break;
+ case RemovalRelations:
+ LOG(("RemovalRelations"));
+ break;
+ case EjectionRelations:
+ LOG(("EjectionRelations"));
+ break;
+ default:
+ LOG(("QueryDeviceRelations.Type=%d", pSl->Parameters.QueryDeviceRelations.Type));
+ }
+ break;
+ }
+
+ case IRP_MN_QUERY_CAPABILITIES:
+ {
+ LOG(("IRP_MN_QUERY_CAPABILITIES: pIoStatus->Status = %x", pIoStatus->Status));
+ if (pIoStatus->Status == STATUS_SUCCESS)
+ {
+ PDEVICE_CAPABILITIES pCaps = pSl->Parameters.DeviceCapabilities.Capabilities;
+ if (RT_VALID_PTR(pCaps))
+ {
+ LOG(("Caps.SilentInstall = %d", pCaps->SilentInstall));
+ LOG(("Caps.UniqueID = %d", pCaps->UniqueID ));
+ LOG(("Caps.Address = %d", pCaps->Address ));
+ LOG(("Caps.UINumber = %d", pCaps->UINumber ));
+ }
+ else
+ LOG(("Invalid pointer %p", pCaps));
+ }
+ break;
+ }
+
+ default:
+ break;
+#endif
+ } /*switch */
+
+ LOG(("Done returns %x (IRQL = %d)", pIoStatus->Status, KeGetCurrentIrql()));
+ return pIoStatus->Status;
+}
+
+NTSTATUS _stdcall VBoxUsbPnPCompletion(DEVICE_OBJECT *pDevObj, IRP *pIrp, void *pvContext)
+{
+ LOG(("Completion PDO(0x%p), IRP(0x%p), Status(0x%x)", pDevObj, pIrp, pIrp->IoStatus.Status));
+ ASSERT_WARN(pvContext, ("zero context"));
+
+ PVBOXUSBHOOK_REQUEST pRequest = (PVBOXUSBHOOK_REQUEST)pvContext;
+ /* NOTE: despite a regular IRP processing the stack location in our completion
+ * differs from those of the PnP hook since the hook is invoked in the "context" of the calle,
+ * while the completion is in the "coller" context in terms of IRP,
+ * so the completion stack location is one level "up" here.
+ *
+ * Moreover we CAN NOT access irp stack location in the completion because we might not have one at all
+ * in case the hooked driver is at the top of the irp call stack
+ *
+ * This is why we use the stack location we saved on IRP way down.
+ * */
+ PIO_STACK_LOCATION pSl = &pRequest->OldLocation;
+ ASSERT_WARN(pIrp == pRequest->pIrp, ("completed IRP(0x%x) not match request IRP(0x%x)", pIrp, pRequest->pIrp));
+ /* NOTE: we can not rely on pDevObj passed in IoCompletion since it may be zero
+ * in case IRP was created with extra stack locations and the caller did not initialize
+ * the IO_STACK_LOCATION::DeviceObject */
+ DEVICE_OBJECT *pRealDevObj = pRequest->pDevObj;
+// Assert(!pDevObj || pDevObj == pRealDevObj);
+// Assert(pSl->DeviceObject == pDevObj);
+
+ switch(pSl->MinorFunction)
+ {
+ case IRP_MN_QUERY_DEVICE_TEXT:
+ case IRP_MN_QUERY_ID:
+#ifdef VBOX_USB_WITH_VERBOSE_LOGGING
+ case IRP_MN_QUERY_DEVICE_RELATIONS:
+ case IRP_MN_QUERY_CAPABILITIES:
+#endif
+ if (NT_SUCCESS(pIrp->IoStatus.Status))
+ {
+ vboxUsbMonHandlePnPIoctl(pRealDevObj, pSl, &pIrp->IoStatus);
+ }
+ else
+ {
+ ASSERT_WARN(pIrp->IoStatus.Status == STATUS_NOT_SUPPORTED, ("Irp failed with status(0x%x)", pIrp->IoStatus.Status));
+ }
+ break;
+
+ case IRP_MN_SURPRISE_REMOVAL:
+ case IRP_MN_REMOVE_DEVICE:
+ if (NT_SUCCESS(pIrp->IoStatus.Status))
+ {
+ VBoxUsbFltPdoRemove(pRealDevObj);
+ }
+ else
+ {
+ AssertFailed();
+ }
+ break;
+
+ /* These two IRPs are received when the PnP subsystem has determined the id of the newly arrived device */
+ /* IRP_MN_START_DEVICE only arrives if it's a USB device of a known class or with a present host driver */
+ case IRP_MN_QUERY_RESOURCE_REQUIREMENTS:
+ case IRP_MN_QUERY_RESOURCES:
+ /* There used to be code to support SUPUSBFLT_IOCTL_SET_NOTIFY_EVENT but it was not reliable. */
+
+ default:
+ break;
+ }
+
+ LOG(("<==PnP: Mn(%s), PDO(0x%p), IRP(0x%p), Status(0x%x), Sl PDO(0x%p), Compl PDO(0x%p)",
+ vboxUsbDbgStrPnPMn(pSl->MinorFunction),
+ pRealDevObj, pIrp, pIrp->IoStatus.Status,
+ pSl->DeviceObject, pDevObj));
+#ifdef DEBUG_misha
+ NTSTATUS tmpStatus = pIrp->IoStatus.Status;
+#endif
+ PVBOXUSBHOOK_ENTRY pHook = pRequest->pHook;
+ NTSTATUS Status = VBoxUsbHookRequestComplete(pHook, pDevObj, pIrp, pRequest);
+ VBoxUsbMonMemFree(pRequest);
+#ifdef DEBUG_misha
+ if (Status != STATUS_MORE_PROCESSING_REQUIRED)
+ {
+ Assert(pIrp->IoStatus.Status == tmpStatus);
+ }
+#endif
+ VBoxUsbHookRelease(pHook);
+ return Status;
+}
+
+/**
+ * Device PnP hook
+ *
+ * @param pDevObj Device object.
+ * @param pIrp Request packet.
+ */
+static NTSTATUS vboxUsbMonPnPHook(IN PVBOXUSBHOOK_ENTRY pHook, IN PDEVICE_OBJECT pDevObj, IN PIRP pIrp)
+{
+ LOG(("==>PnP: Mn(%s), PDO(0x%p), IRP(0x%p), Status(0x%x)", vboxUsbDbgStrPnPMn(IoGetCurrentIrpStackLocation(pIrp)->MinorFunction), pDevObj, pIrp, pIrp->IoStatus.Status));
+
+ if (!VBoxUsbHookRetain(pHook))
+ {
+ WARN(("VBoxUsbHookRetain failed"));
+ return VBoxUsbHookRequestPassDownHookSkip(pHook, pDevObj, pIrp);
+ }
+
+ PVBOXUSBHUB_PNPHOOK_COMPLETION pCompletion = (PVBOXUSBHUB_PNPHOOK_COMPLETION)VBoxUsbMonMemAlloc(sizeof (*pCompletion));
+ if (!pCompletion)
+ {
+ WARN(("VBoxUsbMonMemAlloc failed"));
+ VBoxUsbHookRelease(pHook);
+ pIrp->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES;
+ pIrp->IoStatus.Information = 0;
+ IoCompleteRequest(pIrp, IO_NO_INCREMENT);
+ return STATUS_INSUFFICIENT_RESOURCES;
+ }
+
+ NTSTATUS Status = VBoxUsbHookRequestPassDownHookCompletion(pHook, pDevObj, pIrp, VBoxUsbPnPCompletion, &pCompletion->Rq);
+#ifdef VBOX_USB_WITH_VERBOSE_LOGGING
+ if (Status != STATUS_PENDING)
+ {
+ LOG(("Request completed, Status(0x%x)", Status));
+ VBoxUsbHookVerifyCompletion(pHook, &pCompletion->Rq, pIrp);
+ }
+ else
+ {
+ LOG(("Request pending"));
+ }
+#endif
+ return Status;
+}
+
+/**
+ * Device PnP hook stubs.
+ *
+ * @param pDevObj Device object.
+ * @param pIrp Request packet.
+ */
+#define VBOX_PNPHOOKSTUB(n) NTSTATUS _stdcall VBoxUsbMonPnPHook##n(IN PDEVICE_OBJECT pDevObj, IN PIRP pIrp) \
+{ \
+ return vboxUsbMonPnPHook(&g_VBoxUsbMonGlobals.pDrivers[n].UsbHubPnPHook.Hook, pDevObj, pIrp); \
+}
+
+#define VBOX_PNPHOOKSTUB_INIT(n) g_VBoxUsbMonGlobals.pDrivers[n].pfnHookStub = VBoxUsbMonPnPHook##n
+
+VBOX_PNPHOOKSTUB(0)
+VBOX_PNPHOOKSTUB(1)
+VBOX_PNPHOOKSTUB(2)
+VBOX_PNPHOOKSTUB(3)
+VBOX_PNPHOOKSTUB(4)
+AssertCompile(VBOXUSBMON_MAXDRIVERS == 5);
+
+typedef struct VBOXUSBMONHOOKDRIVERWALKER
+{
+ PDRIVER_OBJECT pDrvObj;
+} VBOXUSBMONHOOKDRIVERWALKER, *PVBOXUSBMONHOOKDRIVERWALKER;
+
+/**
+ * Logs an error to the system event log.
+ *
+ * @param ErrCode Error to report to event log.
+ * @param ReturnedStatus Error that was reported by the driver to the caller.
+ * @param uErrId Unique error id representing the location in the driver.
+ * @param cbDumpData Number of bytes at pDumpData.
+ * @param pDumpData Pointer to data that will be added to the message (see 'details' tab).
+ *
+ * NB: We only use IoLogMsg.dll as the message file, limiting
+ * ErrCode to status codes and messages defined in ntiologc.h
+ */
+static void vboxUsbMonLogError(NTSTATUS ErrCode, NTSTATUS ReturnedStatus, ULONG uErrId, USHORT cbDumpData, PVOID pDumpData)
+{
+ PIO_ERROR_LOG_PACKET pErrEntry;
+
+
+ /* Truncate dumps that do not fit into IO_ERROR_LOG_PACKET. */
+ if (FIELD_OFFSET(IO_ERROR_LOG_PACKET, DumpData) + cbDumpData > ERROR_LOG_MAXIMUM_SIZE)
+ cbDumpData = ERROR_LOG_MAXIMUM_SIZE - FIELD_OFFSET(IO_ERROR_LOG_PACKET, DumpData);
+
+ pErrEntry = (PIO_ERROR_LOG_PACKET)IoAllocateErrorLogEntry(g_VBoxUsbMonGlobals.pDevObj,
+ FIELD_OFFSET(IO_ERROR_LOG_PACKET, DumpData) + cbDumpData);
+ if (pErrEntry)
+ {
+ uint8_t *pDump = (uint8_t *)pErrEntry->DumpData;
+ if (cbDumpData)
+ memcpy(pDump, pDumpData, cbDumpData);
+ pErrEntry->MajorFunctionCode = 0;
+ pErrEntry->RetryCount = 0;
+ pErrEntry->DumpDataSize = cbDumpData;
+ pErrEntry->NumberOfStrings = 0;
+ pErrEntry->StringOffset = 0;
+ pErrEntry->ErrorCode = ErrCode;
+ pErrEntry->UniqueErrorValue = uErrId;
+ pErrEntry->FinalStatus = ReturnedStatus;
+ pErrEntry->IoControlCode = 0;
+ IoWriteErrorLogEntry(pErrEntry);
+ }
+ else
+ {
+ LOG(("Failed to allocate error log entry (cb=%d)\n", FIELD_OFFSET(IO_ERROR_LOG_PACKET, DumpData) + cbDumpData));
+ }
+}
+
+static DECLCALLBACK(BOOLEAN) vboxUsbMonHookDrvObjWalker(PFILE_OBJECT pHubFile, PDEVICE_OBJECT pHubDo, PVOID pvContext)
+{
+ RT_NOREF2(pHubFile, pvContext);
+ PDRIVER_OBJECT pDrvObj = pHubDo->DriverObject;
+
+ /* First we try to figure out if we are already hooked to this driver. */
+ for (int i = 0; i < VBOXUSBMON_MAXDRIVERS; i++)
+ if (pDrvObj == g_VBoxUsbMonGlobals.pDrivers[i].DriverObject)
+ {
+ LOG(("Found %p at pDrivers[%d]\n", pDrvObj, i));
+ /* We've already hooked to this one -- nothing to do. */
+ return TRUE;
+ }
+ /* We are not hooked yet, find an empty slot. */
+ for (int i = 0; i < VBOXUSBMON_MAXDRIVERS; i++)
+ {
+ if (!g_VBoxUsbMonGlobals.pDrivers[i].DriverObject)
+ {
+ /* Found an emtpy slot, use it. */
+ g_VBoxUsbMonGlobals.pDrivers[i].DriverObject = pDrvObj;
+ ObReferenceObject(pDrvObj);
+ LOG(("pDrivers[%d] = %p, installing the hook...\n", i, pDrvObj));
+ VBoxUsbHookInit(&g_VBoxUsbMonGlobals.pDrivers[i].UsbHubPnPHook.Hook,
+ pDrvObj,
+ IRP_MJ_PNP,
+ g_VBoxUsbMonGlobals.pDrivers[i].pfnHookStub);
+ VBoxUsbHookInstall(&g_VBoxUsbMonGlobals.pDrivers[i].UsbHubPnPHook.Hook);
+ return TRUE; /* Must continue to find all drivers. */
+ }
+ if (pDrvObj == g_VBoxUsbMonGlobals.pDrivers[i].DriverObject)
+ {
+ LOG(("Found %p at pDrivers[%d]\n", pDrvObj, i));
+ /* We've already hooked to this one -- nothing to do. */
+ return TRUE;
+ }
+ }
+ /* No empty slots! No reason to continue. */
+ LOG(("No empty slots!\n"));
+ ANSI_STRING ansiDrvName;
+ NTSTATUS Status = RtlUnicodeStringToAnsiString(&ansiDrvName, &pDrvObj->DriverName, true);
+ if (Status != STATUS_SUCCESS)
+ {
+ ansiDrvName.Length = 0;
+ LOG(("RtlUnicodeStringToAnsiString failed with 0x%x", Status));
+ }
+ vboxUsbMonLogError(IO_ERR_INSUFFICIENT_RESOURCES, STATUS_SUCCESS, 1, ansiDrvName.Length, ansiDrvName.Buffer);
+ if (Status == STATUS_SUCCESS)
+ RtlFreeAnsiString(&ansiDrvName);
+ return FALSE;
+}
+
+/**
+ * Finds all USB drivers in the system and installs hooks if haven't done already.
+ */
+static NTSTATUS vboxUsbMonInstallAllHooks()
+{
+ vboxUsbMonHubDevWalk(vboxUsbMonHookDrvObjWalker, NULL);
+ return STATUS_SUCCESS;
+}
+
+static NTSTATUS vboxUsbMonHookCheckInit()
+{
+ static bool fIsHookInited = false;
+ if (fIsHookInited)
+ {
+ LOG(("hook inited already, success"));
+ return STATUS_SUCCESS;
+ }
+ return vboxUsbMonInstallAllHooks();
+}
+
+static NTSTATUS vboxUsbMonHookInstall()
+{
+ /* Nothing to do here as we have already installed all hooks in vboxUsbMonHookCheckInit(). */
+ return STATUS_SUCCESS;
+}
+
+static NTSTATUS vboxUsbMonHookUninstall()
+{
+#ifdef VBOXUSBMON_DBG_NO_PNPHOOK
+ return STATUS_SUCCESS;
+#else
+ NTSTATUS Status = STATUS_SUCCESS;
+ for (int i = 0; i < VBOXUSBMON_MAXDRIVERS; i++)
+ {
+ if (g_VBoxUsbMonGlobals.pDrivers[i].DriverObject)
+ {
+ Assert(g_VBoxUsbMonGlobals.pDrivers[i].DriverObject == g_VBoxUsbMonGlobals.pDrivers[i].UsbHubPnPHook.Hook.pDrvObj);
+ LOG(("Unhooking from %p...\n", g_VBoxUsbMonGlobals.pDrivers[i].DriverObject));
+ Status = VBoxUsbHookUninstall(&g_VBoxUsbMonGlobals.pDrivers[i].UsbHubPnPHook.Hook);
+ if (!NT_SUCCESS(Status))
+ {
+ /*
+ * We failed to uninstall the hook, so we keep the reference to the driver
+ * in order to prevent another driver re-using this slot because we are
+ * going to mark this hook as fUninitFailed.
+ */
+ //AssertMsgFailed(("usbhub pnp unhook failed, setting the fUninitFailed flag, the current value of fUninitFailed (%d)", g_VBoxUsbMonGlobals.UsbHubPnPHook.fUninitFailed));
+ LOG(("usbhub pnp unhook failed, setting the fUninitFailed flag, the current value of fUninitFailed (%d)", g_VBoxUsbMonGlobals.pDrivers[i].UsbHubPnPHook.fUninitFailed));
+ g_VBoxUsbMonGlobals.pDrivers[i].UsbHubPnPHook.fUninitFailed = true;
+ }
+ else
+ {
+ /* The hook was removed successfully, now we can forget about this driver. */
+ ObDereferenceObject(g_VBoxUsbMonGlobals.pDrivers[i].DriverObject);
+ g_VBoxUsbMonGlobals.pDrivers[i].DriverObject = NULL;
+ }
+ }
+ }
+ return Status;
+#endif
+}
+
+
+static NTSTATUS vboxUsbMonCheckTermStuff()
+{
+ NTSTATUS Status = KeWaitForSingleObject(&g_VBoxUsbMonGlobals.OpenSynchEvent,
+ Executive, KernelMode,
+ FALSE, /* BOOLEAN Alertable */
+ NULL /* IN PLARGE_INTEGER Timeout */
+ );
+ AssertRelease(Status == STATUS_SUCCESS);
+
+ do
+ {
+ if (--g_VBoxUsbMonGlobals.cOpens)
+ break;
+
+ Status = vboxUsbMonHookUninstall();
+
+ NTSTATUS tmpStatus = VBoxUsbFltTerm();
+ if (!NT_SUCCESS(tmpStatus))
+ {
+ /* this means a driver state is screwed up, KeBugCheckEx here ? */
+ AssertReleaseFailed();
+ }
+ } while (0);
+
+ KeSetEvent(&g_VBoxUsbMonGlobals.OpenSynchEvent, 0, FALSE);
+
+ return Status;
+}
+
+static NTSTATUS vboxUsbMonCheckInitStuff()
+{
+ NTSTATUS Status = KeWaitForSingleObject(&g_VBoxUsbMonGlobals.OpenSynchEvent,
+ Executive, KernelMode,
+ FALSE, /* BOOLEAN Alertable */
+ NULL /* IN PLARGE_INTEGER Timeout */
+ );
+ if (Status == STATUS_SUCCESS)
+ {
+ do
+ {
+ if (g_VBoxUsbMonGlobals.cOpens++)
+ {
+ LOG(("opens: %d, success", g_VBoxUsbMonGlobals.cOpens));
+ break;
+ }
+
+ Status = VBoxUsbFltInit();
+ if (NT_SUCCESS(Status))
+ {
+ Status = vboxUsbMonHookCheckInit();
+ if (NT_SUCCESS(Status))
+ {
+ Status = vboxUsbMonHookInstall();
+ if (NT_SUCCESS(Status))
+ {
+ Status = STATUS_SUCCESS;
+ LOG(("succeded!!"));
+ break;
+ }
+ else
+ {
+ WARN(("vboxUsbMonHookInstall failed, Status (0x%x)", Status));
+ }
+ }
+ else
+ {
+ WARN(("vboxUsbMonHookCheckInit failed, Status (0x%x)", Status));
+ }
+ VBoxUsbFltTerm();
+ }
+ else
+ {
+ WARN(("VBoxUsbFltInit failed, Status (0x%x)", Status));
+ }
+
+ --g_VBoxUsbMonGlobals.cOpens;
+ Assert(!g_VBoxUsbMonGlobals.cOpens);
+ } while (0);
+
+ KeSetEvent(&g_VBoxUsbMonGlobals.OpenSynchEvent, 0, FALSE);
+ }
+ else
+ {
+ WARN(("KeWaitForSingleObject failed, Status (0x%x)", Status));
+ }
+ return Status;
+}
+
+static NTSTATUS vboxUsbMonContextCreate(PVBOXUSBMONCTX *ppCtx)
+{
+ NTSTATUS Status;
+ *ppCtx = NULL;
+ PVBOXUSBMONCTX pFileCtx = (PVBOXUSBMONCTX)VBoxUsbMonMemAllocZ(sizeof (*pFileCtx));
+ if (pFileCtx)
+ {
+ Status = vboxUsbMonCheckInitStuff();
+ if (Status == STATUS_SUCCESS)
+ {
+ Status = VBoxUsbFltCreate(&pFileCtx->FltCtx);
+ if (Status == STATUS_SUCCESS)
+ {
+ *ppCtx = pFileCtx;
+ LOG(("succeeded!!"));
+ return STATUS_SUCCESS;
+ }
+ else
+ {
+ WARN(("VBoxUsbFltCreate failed"));
+ }
+ vboxUsbMonCheckTermStuff();
+ }
+ else
+ {
+ WARN(("vboxUsbMonCheckInitStuff failed"));
+ }
+ VBoxUsbMonMemFree(pFileCtx);
+ }
+ else
+ {
+ WARN(("VBoxUsbMonMemAllocZ failed"));
+ Status = STATUS_NO_MEMORY;
+ }
+
+ return Status;
+}
+
+static NTSTATUS vboxUsbMonContextClose(PVBOXUSBMONCTX pCtx)
+{
+ NTSTATUS Status = VBoxUsbFltClose(&pCtx->FltCtx);
+ if (Status == STATUS_SUCCESS)
+ {
+ Status = vboxUsbMonCheckTermStuff();
+ Assert(Status == STATUS_SUCCESS);
+ /* ignore the failure */
+ VBoxUsbMonMemFree(pCtx);
+ }
+
+ return Status;
+}
+
+static NTSTATUS _stdcall VBoxUsbMonClose(PDEVICE_OBJECT pDevObj, PIRP pIrp)
+{
+ PIO_STACK_LOCATION pStack = IoGetCurrentIrpStackLocation(pIrp);
+ PFILE_OBJECT pFileObj = pStack->FileObject;
+ Assert(pFileObj->FsContext);
+ PVBOXUSBMONCTX pCtx = (PVBOXUSBMONCTX)pFileObj->FsContext;
+
+ LOG(("VBoxUsbMonClose"));
+
+ NTSTATUS Status = vboxUsbMonContextClose(pCtx);
+ if (Status != STATUS_SUCCESS)
+ {
+ WARN(("vboxUsbMonContextClose failed, Status (0x%x), prevent unload", Status));
+ if (!InterlockedExchange(&g_VBoxUsbMonGlobals.ulPreventUnloadOn, 1))
+ {
+ LOGREL(("ulPreventUnloadOn not set, preventing unload"));
+ UNICODE_STRING UniName;
+ PDEVICE_OBJECT pTmpDevObj;
+ RtlInitUnicodeString(&UniName, USBMON_DEVICE_NAME_NT);
+ NTSTATUS tmpStatus = IoGetDeviceObjectPointer(&UniName, FILE_ALL_ACCESS, &g_VBoxUsbMonGlobals.pPreventUnloadFileObj, &pTmpDevObj);
+ AssertRelease(NT_SUCCESS(tmpStatus));
+ AssertRelease(pTmpDevObj == pDevObj);
+ }
+ else
+ {
+ WARN(("ulPreventUnloadOn already set"));
+ }
+ LOG(("success!!"));
+ Status = STATUS_SUCCESS;
+ }
+ pFileObj->FsContext = NULL;
+ pIrp->IoStatus.Status = Status;
+ pIrp->IoStatus.Information = 0;
+ IoCompleteRequest(pIrp, IO_NO_INCREMENT);
+ return Status;
+}
+
+
+static NTSTATUS _stdcall VBoxUsbMonCreate(PDEVICE_OBJECT pDevObj, PIRP pIrp)
+{
+ RT_NOREF1(pDevObj);
+ PIO_STACK_LOCATION pStack = IoGetCurrentIrpStackLocation(pIrp);
+ PFILE_OBJECT pFileObj = pStack->FileObject;
+ NTSTATUS Status;
+
+ LOG(("VBoxUSBMonCreate"));
+
+ if (pStack->Parameters.Create.Options & FILE_DIRECTORY_FILE)
+ {
+ WARN(("trying to open as a directory"));
+ pIrp->IoStatus.Status = STATUS_NOT_A_DIRECTORY;
+ pIrp->IoStatus.Information = 0;
+ IoCompleteRequest(pIrp, IO_NO_INCREMENT);
+ return STATUS_NOT_A_DIRECTORY;
+ }
+
+ pFileObj->FsContext = NULL;
+ PVBOXUSBMONCTX pCtx = NULL;
+ Status = vboxUsbMonContextCreate(&pCtx);
+ if (Status == STATUS_SUCCESS)
+ {
+ Assert(pCtx);
+ pFileObj->FsContext = pCtx;
+ }
+ else
+ {
+ WARN(("vboxUsbMonContextCreate failed Status (0x%x)", Status));
+ }
+
+ pIrp->IoStatus.Status = Status;
+ pIrp->IoStatus.Information = 0;
+ IoCompleteRequest(pIrp, IO_NO_INCREMENT);
+ return Status;
+}
+
+static int VBoxUsbMonFltAdd(PVBOXUSBMONCTX pContext, PUSBFILTER pFilter, uintptr_t *pId)
+{
+#ifdef VBOXUSBMON_DBG_NO_FILTERS
+ static uintptr_t idDummy = 1;
+ *pId = idDummy;
+ ++idDummy;
+ return VINF_SUCCESS;
+#else
+ int rc = VBoxUsbFltAdd(&pContext->FltCtx, pFilter, pId);
+ return rc;
+#endif
+}
+
+static int VBoxUsbMonFltRemove(PVBOXUSBMONCTX pContext, uintptr_t uId)
+{
+#ifdef VBOXUSBMON_DBG_NO_FILTERS
+ return VINF_SUCCESS;
+#else
+ int rc = VBoxUsbFltRemove(&pContext->FltCtx, uId);
+ return rc;
+#endif
+}
+
+static NTSTATUS VBoxUsbMonRunFilters(PVBOXUSBMONCTX pContext)
+{
+ NTSTATUS Status = VBoxUsbFltFilterCheck(&pContext->FltCtx);
+ return Status;
+}
+
+static NTSTATUS VBoxUsbMonGetDevice(PVBOXUSBMONCTX pContext, HVBOXUSBDEVUSR hDevice, PUSBSUP_GETDEV_MON pInfo)
+{
+ NTSTATUS Status = VBoxUsbFltGetDevice(&pContext->FltCtx, hDevice, pInfo);
+ return Status;
+}
+
+static NTSTATUS vboxUsbMonIoctlDispatch(PVBOXUSBMONCTX pContext, ULONG Ctl, PVOID pvBuffer, ULONG cbInBuffer,
+ ULONG cbOutBuffer, ULONG_PTR *pInfo)
+{
+ NTSTATUS Status = STATUS_SUCCESS;
+ ULONG_PTR Info = 0;
+ switch (Ctl)
+ {
+ case SUPUSBFLT_IOCTL_GET_VERSION:
+ {
+ PUSBSUP_VERSION pOut = (PUSBSUP_VERSION)pvBuffer;
+
+ LOG(("SUPUSBFLT_IOCTL_GET_VERSION"));
+ if (!pvBuffer || cbOutBuffer != sizeof(*pOut) || cbInBuffer != 0)
+ {
+ WARN(("SUPUSBFLT_IOCTL_GET_VERSION: Invalid input/output sizes. cbIn=%d expected %d. cbOut=%d expected %d.",
+ cbInBuffer, 0, cbOutBuffer, sizeof (*pOut)));
+ Status = STATUS_INVALID_PARAMETER;
+ break;
+ }
+ pOut->u32Major = USBMON_MAJOR_VERSION;
+ pOut->u32Minor = USBMON_MINOR_VERSION;
+ Info = sizeof (*pOut);
+ ASSERT_WARN(Status == STATUS_SUCCESS, ("unexpected status, 0x%x", Status));
+ break;
+ }
+
+ case SUPUSBFLT_IOCTL_ADD_FILTER:
+ {
+ PUSBFILTER pFilter = (PUSBFILTER)pvBuffer;
+ PUSBSUP_FLTADDOUT pOut = (PUSBSUP_FLTADDOUT)pvBuffer;
+ uintptr_t uId = 0;
+ int rc;
+ if (RT_UNLIKELY(!pvBuffer || cbInBuffer != sizeof (*pFilter) || cbOutBuffer != sizeof (*pOut)))
+ {
+ WARN(("SUPUSBFLT_IOCTL_ADD_FILTER: Invalid input/output sizes. cbIn=%d expected %d. cbOut=%d expected %d.",
+ cbInBuffer, sizeof (*pFilter), cbOutBuffer, sizeof (*pOut)));
+ Status = STATUS_INVALID_PARAMETER;
+ break;
+ }
+
+ rc = VBoxUsbMonFltAdd(pContext, pFilter, &uId);
+ pOut->rc = rc;
+ pOut->uId = uId;
+ Info = sizeof (*pOut);
+ ASSERT_WARN(Status == STATUS_SUCCESS, ("unexpected status, 0x%x", Status));
+ break;
+ }
+
+ case SUPUSBFLT_IOCTL_REMOVE_FILTER:
+ {
+ uintptr_t *pIn = (uintptr_t *)pvBuffer;
+ int *pRc = (int *)pvBuffer;
+
+ if (!pvBuffer || cbInBuffer != sizeof (*pIn) || (cbOutBuffer && cbOutBuffer != sizeof (*pRc)))
+ {
+ WARN(("SUPUSBFLT_IOCTL_REMOVE_FILTER: Invalid input/output sizes. cbIn=%d expected %d. cbOut=%d expected %d.",
+ cbInBuffer, sizeof (*pIn), cbOutBuffer, 0));
+ Status = STATUS_INVALID_PARAMETER;
+ break;
+ }
+ LOG(("SUPUSBFLT_IOCTL_REMOVE_FILTER %x", *pIn));
+ int rc = VBoxUsbMonFltRemove(pContext, *pIn);
+ if (cbOutBuffer)
+ {
+ /* we've validated that already */
+ Assert(cbOutBuffer == (ULONG)*pRc);
+ *pRc = rc;
+ Info = sizeof (*pRc);
+ }
+ ASSERT_WARN(Status == STATUS_SUCCESS, ("unexpected status, 0x%x", Status));
+ break;
+ }
+
+ case SUPUSBFLT_IOCTL_RUN_FILTERS:
+ {
+ if (pvBuffer || cbInBuffer || cbOutBuffer)
+ {
+ WARN(("SUPUSBFLT_IOCTL_RUN_FILTERS: Invalid input/output sizes. cbIn=%d expected %d. cbOut=%d expected %d.",
+ cbInBuffer, 0, cbOutBuffer, 0));
+ Status = STATUS_INVALID_PARAMETER;
+ break;
+ }
+ LOG(("SUPUSBFLT_IOCTL_RUN_FILTERS "));
+ Status = VBoxUsbMonRunFilters(pContext);
+ ASSERT_WARN(Status != STATUS_PENDING, ("status pending!"));
+ break;
+ }
+
+ case SUPUSBFLT_IOCTL_GET_DEVICE:
+ {
+ HVBOXUSBDEVUSR hDevice;
+ PUSBSUP_GETDEV_MON pOut = (PUSBSUP_GETDEV_MON)pvBuffer;
+ if (!pvBuffer || cbInBuffer != sizeof (hDevice) || cbOutBuffer < sizeof (*pOut))
+ {
+ WARN(("SUPUSBFLT_IOCTL_GET_DEVICE: Invalid input/output sizes! cbIn=%d expected %d. cbOut=%d expected >= %d.",
+ cbInBuffer, sizeof (hDevice), cbOutBuffer, sizeof (*pOut)));
+ Status = STATUS_INVALID_PARAMETER;
+ break;
+ }
+ hDevice = *(HVBOXUSBDEVUSR*)pvBuffer;
+ if (!hDevice)
+ {
+ WARN(("SUPUSBFLT_IOCTL_GET_DEVICE: hDevice is NULL!",
+ cbInBuffer, sizeof (hDevice), cbOutBuffer, sizeof (*pOut)));
+ Status = STATUS_INVALID_PARAMETER;
+ break;
+ }
+
+ Status = VBoxUsbMonGetDevice(pContext, hDevice, pOut);
+
+ if (NT_SUCCESS(Status))
+ {
+ Info = sizeof (*pOut);
+ }
+ else
+ {
+ WARN(("VBoxUsbMonGetDevice fail 0x%x", Status));
+ }
+ break;
+ }
+
+ default:
+ WARN(("Unknown code 0x%x", Ctl));
+ Status = STATUS_INVALID_PARAMETER;
+ break;
+ }
+
+ ASSERT_WARN(Status != STATUS_PENDING, ("Status pending!"));
+
+ *pInfo = Info;
+ return Status;
+}
+
+static NTSTATUS _stdcall VBoxUsbMonDeviceControl(PDEVICE_OBJECT pDevObj, PIRP pIrp)
+{
+ ULONG_PTR Info = 0;
+ NTSTATUS Status = IoAcquireRemoveLock(&g_VBoxUsbMonGlobals.RmLock, pDevObj);
+ if (NT_SUCCESS(Status))
+ {
+ PIO_STACK_LOCATION pSl = IoGetCurrentIrpStackLocation(pIrp);
+ PFILE_OBJECT pFileObj = pSl->FileObject;
+ Assert(pFileObj);
+ Assert(pFileObj->FsContext);
+ PVBOXUSBMONCTX pCtx = (PVBOXUSBMONCTX)pFileObj->FsContext;
+ Assert(pCtx);
+ Status = vboxUsbMonIoctlDispatch(pCtx,
+ pSl->Parameters.DeviceIoControl.IoControlCode,
+ pIrp->AssociatedIrp.SystemBuffer,
+ pSl->Parameters.DeviceIoControl.InputBufferLength,
+ pSl->Parameters.DeviceIoControl.OutputBufferLength,
+ &Info);
+ ASSERT_WARN(Status != STATUS_PENDING, ("Status pending"));
+
+ IoReleaseRemoveLock(&g_VBoxUsbMonGlobals.RmLock, pDevObj);
+ }
+ else
+ {
+ WARN(("IoAcquireRemoveLock failed Status (0x%x)", Status));
+ }
+
+ pIrp->IoStatus.Information = Info;
+ pIrp->IoStatus.Status = Status;
+ IoCompleteRequest (pIrp, IO_NO_INCREMENT);
+ return Status;
+}
+
+static NTSTATUS vboxUsbMonInternalIoctlDispatch(ULONG Ctl, PVOID pvBuffer, ULONG_PTR *pInfo)
+{
+ NTSTATUS Status = STATUS_SUCCESS;
+ *pInfo = 0;
+ switch (Ctl)
+ {
+ case VBOXUSBIDC_INTERNAL_IOCTL_GET_VERSION:
+ {
+ PVBOXUSBIDC_VERSION pOut = (PVBOXUSBIDC_VERSION)pvBuffer;
+
+ LOG(("VBOXUSBIDC_INTERNAL_IOCTL_GET_VERSION"));
+ if (!pvBuffer)
+ {
+ WARN(("VBOXUSBIDC_INTERNAL_IOCTL_GET_VERSION: Buffer is NULL"));
+ Status = STATUS_INVALID_PARAMETER;
+ break;
+ }
+ pOut->u32Major = VBOXUSBIDC_VERSION_MAJOR;
+ pOut->u32Minor = VBOXUSBIDC_VERSION_MINOR;
+ ASSERT_WARN(Status == STATUS_SUCCESS, ("unexpected status, 0x%x", Status));
+ break;
+ }
+
+ case VBOXUSBIDC_INTERNAL_IOCTL_PROXY_STARTUP:
+ {
+ PVBOXUSBIDC_PROXY_STARTUP pOut = (PVBOXUSBIDC_PROXY_STARTUP)pvBuffer;
+
+ LOG(("VBOXUSBIDC_INTERNAL_IOCTL_PROXY_STARTUP"));
+ if (!pvBuffer)
+ {
+ WARN(("VBOXUSBIDC_INTERNAL_IOCTL_PROXY_STARTUP: Buffer is NULL"));
+ Status = STATUS_INVALID_PARAMETER;
+ break;
+ }
+
+ PDEVICE_OBJECT pDevObj = pOut->u.pPDO;
+ pOut->u.hDev = VBoxUsbFltProxyStarted(pDevObj);
+
+ /* If we couldn't find the PDO in our list, that's a real problem and
+ * the capturing will not really work. Log an error.
+ */
+ if (!pOut->u.hDev)
+ vboxUsbMonLogError(IO_ERR_DRIVER_ERROR, STATUS_SUCCESS, 2, sizeof("INTERNAL_IOCTL_PROXY_STARTUP"), "INTERNAL_IOCTL_PROXY_STARTUP");
+
+ ASSERT_WARN(pOut->u.hDev, ("zero hDev"));
+ ASSERT_WARN(Status == STATUS_SUCCESS, ("unexpected status, 0x%x", Status));
+ break;
+ }
+
+ case VBOXUSBIDC_INTERNAL_IOCTL_PROXY_TEARDOWN:
+ {
+ PVBOXUSBIDC_PROXY_TEARDOWN pOut = (PVBOXUSBIDC_PROXY_TEARDOWN)pvBuffer;
+
+ LOG(("VBOXUSBIDC_INTERNAL_IOCTL_PROXY_TEARDOWN"));
+ if (!pvBuffer)
+ {
+ WARN(("VBOXUSBIDC_INTERNAL_IOCTL_PROXY_TEARDOWN: Buffer is NULL"));
+ Status = STATUS_INVALID_PARAMETER;
+ break;
+ }
+
+ ASSERT_WARN(pOut->hDev, ("zero hDev"));
+ VBoxUsbFltProxyStopped(pOut->hDev);
+ ASSERT_WARN(Status == STATUS_SUCCESS, ("unexpected status, 0x%x", Status));
+ break;
+ }
+
+ default:
+ {
+ WARN(("Unknown code 0x%x", Ctl));
+ Status = STATUS_INVALID_PARAMETER;
+ break;
+ }
+ }
+
+ return Status;
+}
+
+static NTSTATUS _stdcall VBoxUsbMonInternalDeviceControl(PDEVICE_OBJECT pDevObj, PIRP pIrp)
+{
+ ULONG_PTR Info = 0;
+ NTSTATUS Status = IoAcquireRemoveLock(&g_VBoxUsbMonGlobals.RmLock, pDevObj);
+ if (NT_SUCCESS(Status))
+ {
+ PIO_STACK_LOCATION pSl = IoGetCurrentIrpStackLocation(pIrp);
+ Status = vboxUsbMonInternalIoctlDispatch(pSl->Parameters.DeviceIoControl.IoControlCode,
+ pSl->Parameters.Others.Argument1,
+ &Info);
+ Assert(Status != STATUS_PENDING);
+
+ IoReleaseRemoveLock(&g_VBoxUsbMonGlobals.RmLock, pDevObj);
+ }
+
+ pIrp->IoStatus.Information = Info;
+ pIrp->IoStatus.Status = Status;
+ IoCompleteRequest (pIrp, IO_NO_INCREMENT);
+ return Status;
+}
+
+/**
+ * Unload the driver.
+ *
+ * @param pDrvObj Driver object.
+ */
+static void _stdcall VBoxUsbMonUnload(PDRIVER_OBJECT pDrvObj)
+{
+ RT_NOREF1(pDrvObj);
+ LOG(("VBoxUSBMonUnload pDrvObj (0x%p)", pDrvObj));
+
+ IoReleaseRemoveLockAndWait(&g_VBoxUsbMonGlobals.RmLock, &g_VBoxUsbMonGlobals);
+
+ Assert(!g_VBoxUsbMonGlobals.cOpens);
+
+ UNICODE_STRING DosName;
+ RtlInitUnicodeString(&DosName, USBMON_DEVICE_NAME_DOS);
+ IoDeleteSymbolicLink(&DosName);
+
+ IoDeleteDevice(g_VBoxUsbMonGlobals.pDevObj);
+
+ /* cleanup the logger */
+ PRTLOGGER pLogger = RTLogRelSetDefaultInstance(NULL);
+ if (pLogger)
+ RTLogDestroy(pLogger);
+ pLogger = RTLogSetDefaultInstance(NULL);
+ if (pLogger)
+ RTLogDestroy(pLogger);
+}
+
+RT_C_DECLS_BEGIN
+NTSTATUS _stdcall DriverEntry(PDRIVER_OBJECT pDrvObj, PUNICODE_STRING pRegPath);
+RT_C_DECLS_END
+
+/**
+ * Driver entry point.
+ *
+ * @returns appropriate status code.
+ * @param pDrvObj Pointer to driver object.
+ * @param pRegPath Registry base path.
+ */
+NTSTATUS _stdcall DriverEntry(PDRIVER_OBJECT pDrvObj, PUNICODE_STRING pRegPath)
+{
+ RT_NOREF1(pRegPath);
+#ifdef VBOX_USB_WITH_VERBOSE_LOGGING
+ RTLogGroupSettings(0, "+default.e.l.f.l2.l3");
+ RTLogDestinations(0, "debugger");
+#endif
+
+ LOGREL(("Built %s %s", __DATE__, __TIME__));
+
+ memset (&g_VBoxUsbMonGlobals, 0, sizeof (g_VBoxUsbMonGlobals));
+
+ VBOX_PNPHOOKSTUB_INIT(0);
+ VBOX_PNPHOOKSTUB_INIT(1);
+ VBOX_PNPHOOKSTUB_INIT(2);
+ VBOX_PNPHOOKSTUB_INIT(3);
+ VBOX_PNPHOOKSTUB_INIT(4);
+ AssertCompile(VBOXUSBMON_MAXDRIVERS == 5);
+
+ KeInitializeEvent(&g_VBoxUsbMonGlobals.OpenSynchEvent, SynchronizationEvent, TRUE /* signaled */);
+ IoInitializeRemoveLock(&g_VBoxUsbMonGlobals.RmLock, VBOXUSBMON_MEMTAG, 1, 100);
+ UNICODE_STRING DevName;
+ PDEVICE_OBJECT pDevObj;
+ /* create the device */
+ RtlInitUnicodeString(&DevName, USBMON_DEVICE_NAME_NT);
+ NTSTATUS Status = IoAcquireRemoveLock(&g_VBoxUsbMonGlobals.RmLock, &g_VBoxUsbMonGlobals);
+ if (NT_SUCCESS(Status))
+ {
+ Status = IoCreateDevice(pDrvObj, sizeof (VBOXUSBMONINS), &DevName, FILE_DEVICE_UNKNOWN, 0, FALSE, &pDevObj);
+ if (NT_SUCCESS(Status))
+ {
+ UNICODE_STRING DosName;
+ RtlInitUnicodeString(&DosName, USBMON_DEVICE_NAME_DOS);
+ Status = IoCreateSymbolicLink(&DosName, &DevName);
+ if (NT_SUCCESS(Status))
+ {
+ PVBOXUSBMONINS pDevExt = (PVBOXUSBMONINS)pDevObj->DeviceExtension;
+ memset(pDevExt, 0, sizeof(*pDevExt));
+
+ pDrvObj->DriverUnload = VBoxUsbMonUnload;
+ pDrvObj->MajorFunction[IRP_MJ_CREATE] = VBoxUsbMonCreate;
+ pDrvObj->MajorFunction[IRP_MJ_CLOSE] = VBoxUsbMonClose;
+ pDrvObj->MajorFunction[IRP_MJ_DEVICE_CONTROL] = VBoxUsbMonDeviceControl;
+ pDrvObj->MajorFunction[IRP_MJ_INTERNAL_DEVICE_CONTROL] = VBoxUsbMonInternalDeviceControl;
+
+ g_VBoxUsbMonGlobals.pDevObj = pDevObj;
+ LOG(("VBoxUSBMon::DriverEntry returning STATUS_SUCCESS"));
+ return STATUS_SUCCESS;
+ }
+ IoDeleteDevice(pDevObj);
+ }
+ IoReleaseRemoveLockAndWait(&g_VBoxUsbMonGlobals.RmLock, &g_VBoxUsbMonGlobals);
+ }
+
+ return Status;
+}
diff --git a/src/VBox/HostDrivers/VBoxUSB/win/mon/VBoxUsbMon.h b/src/VBox/HostDrivers/VBoxUSB/win/mon/VBoxUsbMon.h
new file mode 100644
index 00000000..e67c6856
--- /dev/null
+++ b/src/VBox/HostDrivers/VBoxUSB/win/mon/VBoxUsbMon.h
@@ -0,0 +1,77 @@
+/* $Id: VBoxUsbMon.h $ */
+/** @file
+ * VBox USB Monitor
+ */
+/*
+ * Copyright (C) 2011-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+#ifndef VBOX_INCLUDED_SRC_VBoxUSB_win_mon_VBoxUsbMon_h
+#define VBOX_INCLUDED_SRC_VBoxUSB_win_mon_VBoxUsbMon_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+#include <VBox/cdefs.h>
+#include <VBox/types.h>
+#include <iprt/assert.h>
+#include <VBox/sup.h>
+#include <iprt/asm.h>
+#include <VBox/log.h>
+
+#ifdef DEBUG
+/* disables filters */
+//#define VBOXUSBMON_DBG_NO_FILTERS
+/* disables pnp hooking */
+//#define VBOXUSBMON_DBG_NO_PNPHOOK
+#endif
+
+#include "../../../win/VBoxDbgLog.h"
+#include "../cmn/VBoxDrvTool.h"
+#include "../cmn/VBoxUsbTool.h"
+
+#include "VBoxUsbHook.h"
+#include "VBoxUsbFlt.h"
+
+PVOID VBoxUsbMonMemAlloc(SIZE_T cbBytes);
+PVOID VBoxUsbMonMemAllocZ(SIZE_T cbBytes);
+VOID VBoxUsbMonMemFree(PVOID pvMem);
+
+NTSTATUS VBoxUsbMonGetDescriptor(PDEVICE_OBJECT pDevObj, void *buffer, int size, int type, int index, int language_id);
+NTSTATUS VBoxUsbMonQueryBusRelations(PDEVICE_OBJECT pDevObj, PFILE_OBJECT pFileObj, PDEVICE_RELATIONS *pDevRelations);
+
+void vboxUsbDbgPrintUnicodeString(PUNICODE_STRING pUnicodeString);
+
+typedef DECLCALLBACKTYPE(BOOLEAN, FNVBOXUSBMONDEVWALKER,(PFILE_OBJECT pHubFile, PDEVICE_OBJECT pHubDo, PVOID pvContext));
+typedef FNVBOXUSBMONDEVWALKER *PFNVBOXUSBMONDEVWALKER;
+
+VOID vboxUsbMonHubDevWalk(PFNVBOXUSBMONDEVWALKER pfnWalker, PVOID pvWalker);
+
+#endif /* !VBOX_INCLUDED_SRC_VBoxUSB_win_mon_VBoxUsbMon_h */
diff --git a/src/VBox/HostDrivers/VBoxUSB/win/mon/VBoxUsbMon.rc b/src/VBox/HostDrivers/VBoxUSB/win/mon/VBoxUsbMon.rc
new file mode 100644
index 00000000..f22b3dd8
--- /dev/null
+++ b/src/VBox/HostDrivers/VBoxUSB/win/mon/VBoxUsbMon.rc
@@ -0,0 +1,70 @@
+/* $Id: VBoxUsbMon.rc $ */
+/** @file
+ * VBoxUSBMon - Resource file containing version info and icon.
+ */
+
+/*
+ * Copyright (C) 2011-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+#include <windows.h>
+#include <VBox/version.h>
+
+LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
+
+VS_VERSION_INFO VERSIONINFO
+ FILEVERSION VBOX_RC_FILE_VERSION
+ PRODUCTVERSION VBOX_RC_FILE_VERSION
+ FILEFLAGSMASK VS_FFI_FILEFLAGSMASK
+ FILEFLAGS VBOX_RC_FILE_FLAGS
+ FILEOS VBOX_RC_FILE_OS
+ FILETYPE VBOX_RC_TYPE_DRV
+ FILESUBTYPE VFT2_DRV_SYSTEM
+BEGIN
+ BLOCK "StringFileInfo"
+ BEGIN
+ BLOCK "040904b0" // Lang=US English, CharSet=Unicode
+ BEGIN
+ VALUE "FileDescription", "VirtualBox USB Monitor Driver\0"
+ VALUE "InternalName", "VBoxUSBMon\0"
+ VALUE "OriginalFilename", "VBoxUSBMon.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_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/HostDrivers/VBoxUSB/win/testcase/Makefile.kup b/src/VBox/HostDrivers/VBoxUSB/win/testcase/Makefile.kup
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/src/VBox/HostDrivers/VBoxUSB/win/testcase/Makefile.kup
diff --git a/src/VBox/HostDrivers/VBoxUSB/win/testcase/USBTest.cpp b/src/VBox/HostDrivers/VBoxUSB/win/testcase/USBTest.cpp
new file mode 100644
index 00000000..6b75c07f
--- /dev/null
+++ b/src/VBox/HostDrivers/VBoxUSB/win/testcase/USBTest.cpp
@@ -0,0 +1,384 @@
+/* $Id: USBTest.cpp $ */
+/** @file
+ * VBox host drivers - USB drivers - Filter & driver installation
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include <iprt/win/windows.h>
+#include <iprt/win/setupapi.h>
+#include <newdev.h>
+
+#include <iprt/assert.h>
+#include <VBox/err.h>
+#include <iprt/param.h>
+#include <iprt/path.h>
+#include <iprt/stream.h>
+#include <iprt/string.h>
+#include <VBox/usblib.h>
+#include <VBox/VBoxDrvCfg-win.h>
+
+
+/*********************************************************************************************************************************
+* Global Variables *
+*********************************************************************************************************************************/
+/** Handle to the open device. */
+static HANDLE g_hUSBMonitor = INVALID_HANDLE_VALUE;
+/** Flags whether or not we started the service. */
+static bool g_fStartedService = false;
+
+
+/**
+ * Attempts to start the service, creating it if necessary.
+ *
+ * @returns 0 on success.
+ * @returns -1 on failure.
+ * @param fRetry Indicates retry call.
+ */
+int usbMonStartService(void)
+{
+ HRESULT hr = VBoxDrvCfgSvcStart(USBMON_SERVICE_NAME_W);
+ if (hr != S_OK)
+ {
+ AssertMsgFailed(("couldn't start service, hr (0x%x)\n", hr));
+ return -1;
+ }
+ return 0;
+}
+
+/**
+ * Stops a possibly running service.
+ *
+ * @returns 0 on success.
+ * @returns -1 on failure.
+ */
+int usbMonStopService(void)
+{
+ RTPrintf("usbMonStopService\n");
+
+ /*
+ * Assume it didn't exist, so we'll create the service.
+ */
+ int rc = -1;
+ SC_HANDLE hSMgr = OpenSCManager(NULL, NULL, SERVICE_STOP | SERVICE_QUERY_STATUS);
+ AssertMsg(hSMgr, ("OpenSCManager(,,delete) failed rc=%d\n", GetLastError()));
+ if (hSMgr)
+ {
+ SC_HANDLE hService = OpenServiceW(hSMgr, USBMON_SERVICE_NAME_W, SERVICE_STOP | SERVICE_QUERY_STATUS);
+ if (hService)
+ {
+ /*
+ * Stop the service.
+ */
+ SERVICE_STATUS Status;
+ QueryServiceStatus(hService, &Status);
+ if (Status.dwCurrentState == SERVICE_STOPPED)
+ rc = 0;
+ else if (ControlService(hService, SERVICE_CONTROL_STOP, &Status))
+ {
+ int iWait = 100;
+ while (Status.dwCurrentState == SERVICE_STOP_PENDING && iWait-- > 0)
+ {
+ Sleep(100);
+ QueryServiceStatus(hService, &Status);
+ }
+ if (Status.dwCurrentState == SERVICE_STOPPED)
+ rc = 0;
+ else
+ AssertMsgFailed(("Failed to stop service. status=%d\n", Status.dwCurrentState));
+ }
+ else
+ AssertMsgFailed(("ControlService failed with LastError=%Rwa. status=%d\n", GetLastError(), Status.dwCurrentState));
+ CloseServiceHandle(hService);
+ }
+ else if (GetLastError() == ERROR_SERVICE_DOES_NOT_EXIST)
+ rc = 0;
+ else
+ AssertMsgFailed(("OpenService failed LastError=%Rwa\n", GetLastError()));
+ CloseServiceHandle(hSMgr);
+ }
+ return rc;
+}
+
+/**
+ * Release specified USB device to the host.
+ *
+ * @returns VBox status code
+ * @param usVendorId Vendor id
+ * @param usProductId Product id
+ * @param usRevision Revision
+ */
+int usbMonReleaseDevice(USHORT usVendorId, USHORT usProductId, USHORT usRevision)
+{
+ USBSUP_RELEASE release;
+ DWORD cbReturned = 0;
+
+ RTPrintf("usbLibReleaseDevice %x %x %x\n", usVendorId, usProductId, usRevision);
+
+ release.usVendorId = usVendorId;
+ release.usProductId = usProductId;
+ release.usRevision = usRevision;
+
+ if (!DeviceIoControl(g_hUSBMonitor, SUPUSBFLT_IOCTL_RELEASE_DEVICE, &release, sizeof(release), NULL, 0, &cbReturned, NULL))
+ {
+ AssertMsgFailed(("DeviceIoControl failed with %d\n", GetLastError()));
+ return RTErrConvertFromWin32(GetLastError());
+ }
+
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * Add USB device filter
+ *
+ * @returns VBox status code.
+ * @param usVendorId Vendor id
+ * @param usProductId Product id
+ * @param usRevision Revision
+ * @param ppID Pointer to filter id
+ */
+int usbMonInsertFilter(USHORT usVendorId, USHORT usProductId, USHORT usRevision, void **ppID)
+{
+ USBFILTER filter;
+ USBSUP_FLTADDOUT flt_add;
+ DWORD cbReturned = 0;
+
+ Assert(g_hUSBMonitor);
+
+ RTPrintf("usblibInsertFilter %04X %04X %04X\n", usVendorId, usProductId, usRevision);
+
+ USBFilterInit(&filter, USBFILTERTYPE_CAPTURE);
+ USBFilterSetNumExact(&filter, USBFILTERIDX_VENDOR_ID, usVendorId, true);
+ USBFilterSetNumExact(&filter, USBFILTERIDX_PRODUCT_ID, usProductId, true);
+ USBFilterSetNumExact(&filter, USBFILTERIDX_DEVICE_REV, usRevision, true);
+
+ if (!DeviceIoControl(g_hUSBMonitor, SUPUSBFLT_IOCTL_ADD_FILTER, &filter, sizeof(filter), &flt_add, sizeof(flt_add), &cbReturned, NULL))
+ {
+ AssertMsgFailed(("DeviceIoControl failed with %d\n", GetLastError()));
+ return RTErrConvertFromWin32(GetLastError());
+ }
+ *ppID = (void *)flt_add.uId;
+ return VINF_SUCCESS;
+}
+
+/**
+ * Applies existing filters to currently plugged-in USB devices
+ *
+ * @returns VBox status code.
+ */
+int usbMonRunFilters(void)
+{
+ DWORD cbReturned = 0;
+
+ Assert(g_hUSBMonitor);
+
+ if (!DeviceIoControl(g_hUSBMonitor, SUPUSBFLT_IOCTL_RUN_FILTERS, NULL, 0, NULL, 0, &cbReturned, NULL))
+ {
+ AssertMsgFailed(("DeviceIoControl failed with %d\n", GetLastError()));
+ return RTErrConvertFromWin32(GetLastError());
+ }
+ return VINF_SUCCESS;
+}
+
+/**
+ * Remove USB device filter
+ *
+ * @returns VBox status code.
+ * @param aID Filter id
+ */
+int usbMonRemoveFilter (void *aID)
+{
+ uintptr_t uId;
+ DWORD cbReturned = 0;
+
+ Assert(g_hUSBMonitor);
+
+ RTPrintf("usblibRemoveFilter %p\n", aID);
+
+ uId = (uintptr_t)aID;
+ if (!DeviceIoControl(g_hUSBMonitor, SUPUSBFLT_IOCTL_REMOVE_FILTER, &uId, sizeof(uId), NULL, 0,&cbReturned, NULL))
+ {
+ AssertMsgFailed(("DeviceIoControl failed with %d\n", GetLastError()));
+ return RTErrConvertFromWin32(GetLastError());
+ }
+ return VINF_SUCCESS;
+}
+
+/**
+ * Initialize the USB monitor
+ *
+ * @returns VBox status code.
+ */
+int usbMonitorInit()
+{
+ int rc;
+ USBSUP_VERSION version = {0};
+ DWORD cbReturned;
+
+ RTPrintf("usbproxy: usbLibInit\n");
+
+ g_hUSBMonitor = CreateFile (USBMON_DEVICE_NAME,
+ GENERIC_READ | GENERIC_WRITE,
+ FILE_SHARE_READ | FILE_SHARE_WRITE,
+ NULL, // no SECURITY_ATTRIBUTES structure
+ OPEN_EXISTING, // No special create flags
+ FILE_ATTRIBUTE_SYSTEM,
+ NULL); // No template file
+
+ if (g_hUSBMonitor == INVALID_HANDLE_VALUE)
+ {
+ usbMonStartService();
+
+ g_hUSBMonitor = CreateFile (USBMON_DEVICE_NAME,
+ GENERIC_READ | GENERIC_WRITE,
+ FILE_SHARE_READ | FILE_SHARE_WRITE,
+ NULL, // no SECURITY_ATTRIBUTES structure
+ OPEN_EXISTING, // No special create flags
+ FILE_ATTRIBUTE_SYSTEM,
+ NULL); // No template file
+
+ if (g_hUSBMonitor == INVALID_HANDLE_VALUE)
+ {
+ /* AssertFailed(); */
+ RTPrintf("usbproxy: Unable to open filter driver!! (rc=%lu)\n", GetLastError());
+ rc = VERR_FILE_NOT_FOUND;
+ goto failure;
+ }
+ }
+
+ /*
+ * Check the version
+ */
+ cbReturned = 0;
+ if (!DeviceIoControl(g_hUSBMonitor, SUPUSBFLT_IOCTL_GET_VERSION, NULL, 0,&version, sizeof(version), &cbReturned, NULL))
+ {
+ RTPrintf("usbproxy: Unable to query filter version!! (rc=%lu)\n", GetLastError());
+ rc = VERR_VERSION_MISMATCH;
+ goto failure;
+ }
+
+ if ( version.u32Major != USBMON_MAJOR_VERSION
+#if USBMON_MINOR_VERSION != 0
+ || version.u32Minor < USBMON_MINOR_VERSION
+#endif
+ )
+ {
+ RTPrintf("usbproxy: Filter driver version mismatch!!\n");
+ rc = VERR_VERSION_MISMATCH;
+ goto failure;
+ }
+
+ return VINF_SUCCESS;
+
+failure:
+ if (g_hUSBMonitor != INVALID_HANDLE_VALUE)
+ {
+ CloseHandle(g_hUSBMonitor);
+ g_hUSBMonitor = INVALID_HANDLE_VALUE;
+ }
+ return rc;
+}
+
+
+
+/**
+ * Terminate the USB monitor
+ *
+ * @returns VBox status code.
+ */
+int usbMonitorTerm()
+{
+ if (g_hUSBMonitor != INVALID_HANDLE_VALUE)
+ {
+ CloseHandle(g_hUSBMonitor);
+ g_hUSBMonitor = INVALID_HANDLE_VALUE;
+ }
+ /*
+ * If we started the service we might consider stopping it too.
+ *
+ * Since this won't work unless the process starting it is the
+ * last user we might wanna skip this...
+ */
+ if (g_fStartedService)
+ {
+ usbMonStopService();
+ g_fStartedService = false;
+ }
+
+ return VINF_SUCCESS;
+}
+
+
+int __cdecl main(int argc, char **argv)
+{
+ int rc;
+ int c;
+ RT_NOREF2(argc, argv);
+
+ RTPrintf("USB test\n");
+
+ rc = usbMonitorInit();
+ AssertRC(rc);
+
+ void *pId1, *pId2, *pId3;
+
+ usbMonInsertFilter(0x0529, 0x0514, 0x0100, &pId1);
+ usbMonInsertFilter(0x0A16, 0x2499, 0x0100, &pId2);
+ usbMonInsertFilter(0x80EE, 0x0030, 0x0110, &pId3);
+
+ RTPrintf("Waiting to capture devices... enter 'r' to run filters\n");
+ c = RTStrmGetCh(g_pStdIn);
+ if (c == 'r')
+ {
+ usbMonRunFilters();
+ RTPrintf("Waiting to capture devices...\n");
+ RTStrmGetCh(g_pStdIn); /* eat the '\n' */
+ RTStrmGetCh(g_pStdIn); /* wait for more input */
+ }
+
+ RTPrintf("Releasing device\n");
+ usbMonReleaseDevice(0xA16, 0x2499, 0x100);
+
+ usbMonRemoveFilter(pId1);
+ usbMonRemoveFilter(pId2);
+ usbMonRemoveFilter(pId3);
+
+ rc = usbMonitorTerm();
+
+ return 0;
+}
+
diff --git a/src/VBox/HostDrivers/VBoxUSB/win/usbd/Makefile.kup b/src/VBox/HostDrivers/VBoxUSB/win/usbd/Makefile.kup
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/src/VBox/HostDrivers/VBoxUSB/win/usbd/Makefile.kup
diff --git a/src/VBox/HostDrivers/VBoxUSB/win/usbd/usbd.def b/src/VBox/HostDrivers/VBoxUSB/win/usbd/usbd.def
new file mode 100644
index 00000000..d3a618ab
--- /dev/null
+++ b/src/VBox/HostDrivers/VBoxUSB/win/usbd/usbd.def
@@ -0,0 +1,74 @@
+; $Id: usbd.def $
+;; @file
+; Export definitions
+;
+
+;
+; Copyright (C) 2006-2023 Oracle and/or its affiliates.
+;
+; This file is part of VirtualBox base platform packages, as
+; available from https://www.virtualbox.org.
+;
+; This program is free software; you can redistribute it and/or
+; modify it under the terms of the GNU General Public License
+; as published by the Free Software Foundation, in version 3 of the
+; License.
+;
+; This program is distributed in the hope that it will be useful, but
+; WITHOUT ANY WARRANTY; without even the implied warranty of
+; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+; General Public License for more details.
+;
+; You should have received a copy of the GNU General Public License
+; along with this program; if not, see <https://www.gnu.org/licenses>.
+;
+; The contents of this file may alternatively be used under the terms
+; of the Common Development and Distribution License Version 1.0
+; (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+; in the VirtualBox distribution, in which case the provisions of the
+; CDDL are applicable instead of those of the GPL.
+;
+; You may elect to license modified versions of this file under the
+; terms and conditions of either the GPL or the CDDL or both.
+;
+; SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+;
+
+LIBRARY USBD.SYS
+
+EXPORTS
+USBD_AllocateDeviceName
+USBD_CalculateUsbBandwidth
+USBD_CompleteRequest
+USBD_CreateConfigurationRequest
+USBD_CreateDevice
+USBD_Debug_GetHeap
+USBD_Debug_LogEntry
+USBD_Debug_RetHeap
+USBD_Dispatch
+USBD_FreeDeviceMutex
+USBD_FreeDeviceName
+USBD_GetDeviceInformation
+USBD_GetInterfaceLength
+USBD_GetPdoRegistryParameter
+USBD_GetSuspendPowerState
+USBD_GetUSBDIVersion
+USBD_InitializeDevice
+USBD_MakePdoName
+USBD_ParseConfigurationDescriptor
+USBD_QueryBusTime
+USBD_RegisterHcDeviceCapabilities
+USBD_RegisterHcFilter
+USBD_RegisterHostController
+USBD_RemoveDevice
+USBD_RestoreDevice
+USBD_SetSuspendPowerState
+USBD_WaitDeviceMutex
+_USBD_CreateConfigurationRequestEx@8
+_USBD_ParseDescriptors@16
+_USBD_ParseConfigurationDescriptorEx@28
+
+; win64 strips the _stdcall postfix
+USBD_CreateConfigurationRequestEx
+USBD_ParseConfigurationDescriptorEx
+USBD_ParseDescriptors
diff --git a/src/VBox/HostDrivers/adpctl/Makefile.kmk b/src/VBox/HostDrivers/adpctl/Makefile.kmk
new file mode 100644
index 00000000..7156fbf7
--- /dev/null
+++ b/src/VBox/HostDrivers/adpctl/Makefile.kmk
@@ -0,0 +1,44 @@
+# $Id: Makefile.kmk $
+## @file
+# Sub-Makefile for VBoxAdpCtl
+#
+# VBoxAdpCtl is a tool for configuring vboxnetX adapters.
+#
+
+#
+# Copyright (C) 2009-2023 Oracle and/or its affiliates.
+#
+# This file is part of VirtualBox base platform packages, as
+# available from https://www.virtualbox.org.
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation, in version 3 of the
+# License.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, see <https://www.gnu.org/licenses>.
+#
+# SPDX-License-Identifier: GPL-3.0-only
+#
+
+SUB_DEPTH = ../../../..
+include $(KBUILD_PATH)/subheader.kmk
+
+PROGRAMS += VBoxNetAdpCtl
+### Another template? We must *not* set RPATH!
+ifneq ($(KBUILD_TYPE),debug)
+ VBoxNetAdpCtl_TEMPLATE = VBoxR3SetUidToRoot
+ VBoxNetAdpCtl_LIBS += $(LIB_RUNTIME)
+else
+ VBoxNetAdpCtl_TEMPLATE = VBoxR3Static
+endif
+VBoxNetAdpCtl_SOURCES = VBoxNetAdpCtl.cpp
+
+include $(FILE_KBUILD_SUB_FOOTER)
+
diff --git a/src/VBox/HostDrivers/adpctl/VBoxNetAdpCtl.cpp b/src/VBox/HostDrivers/adpctl/VBoxNetAdpCtl.cpp
new file mode 100644
index 00000000..5433c969
--- /dev/null
+++ b/src/VBox/HostDrivers/adpctl/VBoxNetAdpCtl.cpp
@@ -0,0 +1,1178 @@
+/* $Id: VBoxNetAdpCtl.cpp $ */
+/** @file
+ * Apps - VBoxAdpCtl, Configuration tool for vboxnetX adapters.
+ */
+
+/*
+ * Copyright (C) 2009-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include <list>
+#include <errno.h>
+#include <getopt.h>
+#include <stdio.h>
+#include <stdarg.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/wait.h>
+#include <sys/ioctl.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+
+#include <iprt/errcore.h>
+#include <iprt/initterm.h>
+#include <iprt/message.h>
+#include <iprt/net.h>
+#include <iprt/string.h>
+#include <iprt/uint128.h>
+
+#ifdef RT_OS_LINUX
+# include <arpa/inet.h>
+# include <net/if.h>
+# include <linux/types.h>
+/* Older versions of ethtool.h rely on these: */
+typedef unsigned long long u64;
+typedef __uint32_t u32;
+typedef __uint16_t u16;
+typedef __uint8_t u8;
+# include <limits.h> /* for INT_MAX */
+# include <linux/ethtool.h>
+# include <linux/sockios.h>
+#endif
+#ifdef RT_OS_SOLARIS
+# include <sys/ioccom.h>
+#endif
+
+/** @todo Error codes must be moved to some header file */
+#define ADPCTLERR_BAD_NAME 2
+#define ADPCTLERR_NO_CTL_DEV 3
+#define ADPCTLERR_IOCTL_FAILED 4
+#define ADPCTLERR_SOCKET_FAILED 5
+
+/** @todo These are duplicates from src/VBox/HostDrivers/VBoxNetAdp/VBoxNetAdpInternal.h */
+#define VBOXNETADP_CTL_DEV_NAME "/dev/vboxnetctl"
+#define VBOXNETADP_MAX_INSTANCES 128
+#define VBOXNETADP_NAME "vboxnet"
+#define VBOXNETADP_MAX_NAME_LEN 32
+#define VBOXNETADP_CTL_ADD _IOWR('v', 1, VBOXNETADPREQ)
+#define VBOXNETADP_CTL_REMOVE _IOW('v', 2, VBOXNETADPREQ)
+typedef struct VBoxNetAdpReq
+{
+ char szName[VBOXNETADP_MAX_NAME_LEN];
+} VBOXNETADPREQ;
+typedef VBOXNETADPREQ *PVBOXNETADPREQ;
+
+#define VBOXADPCTL_IFCONFIG_PATH1 "/sbin/ifconfig"
+#define VBOXADPCTL_IFCONFIG_PATH2 "/bin/ifconfig"
+
+bool verbose;
+bool dry_run;
+
+
+static int usage(void)
+{
+ fprintf(stderr, "Usage: VBoxNetAdpCtl <adapter> <address> ([netmask <address>] | remove)\n");
+ fprintf(stderr, " | VBoxNetAdpCtl [<adapter>] add\n");
+ fprintf(stderr, " | VBoxNetAdpCtl <adapter> remove\n");
+ return EXIT_FAILURE;
+}
+
+
+/*
+ * A wrapper on standard list that provides '<<' operator for adding several list members in a single
+ * line dynamically. For example: "CmdList(arg1) << arg2 << arg3" produces a list with three members.
+ */
+class CmdList
+{
+public:
+ /** Creates an empty list. */
+ CmdList() {};
+ /** Creates a list with a single member. */
+ CmdList(const char *pcszCommand) { m_list.push_back(pcszCommand); };
+ /** Provides access to the underlying standard list. */
+ const std::list<const char *>& getList(void) const { return m_list; };
+ /** Adds a member to the list. */
+ CmdList& operator<<(const char *pcszArgument);
+private:
+ std::list<const char *>m_list;
+};
+
+CmdList& CmdList::operator<<(const char *pcszArgument)
+{
+ m_list.push_back(pcszArgument);
+ return *this;
+}
+
+/** Simple helper to distinguish IPv4 and IPv6 addresses. */
+inline bool isAddrV6(const char *pcszAddress)
+{
+ return !!(strchr(pcszAddress, ':'));
+}
+
+
+/*********************************************************************************************************************************
+* Generic address commands. *
+*********************************************************************************************************************************/
+
+/**
+ * The base class for all address manipulation commands. While being an abstract class,
+ * it provides a generic implementation of 'set' and 'remove' methods, which rely on
+ * pure virtual methods like 'addV4' and 'removeV4' to perform actual command execution.
+ */
+class AddressCommand
+{
+public:
+ AddressCommand() : m_pszPath(0) {};
+ virtual ~AddressCommand() {};
+
+ /** returns true if underlying command (executable) is present in the system. */
+ bool isAvailable(void)
+ { struct stat s; return (!stat(m_pszPath, &s) && S_ISREG(s.st_mode)); };
+
+ /*
+ * Someday we may want to support several IP addresses per adapter, but for now we
+ * have 'set' method only, which replaces all addresses with the one specifed.
+ *
+ * virtual int add(const char *pcszAdapter, const char *pcszAddress, const char *pcszNetmask = 0) = 0;
+ */
+ /** replace existing address(es) */
+ virtual int set(const char *pcszAdapter, const char *pcszAddress, const char *pcszNetmask = 0);
+ /** remove address */
+ virtual int remove(const char *pcszAdapter, const char *pcszAddress);
+protected:
+ /** IPv4-specific handler used by generic implementation of 'set' method if 'setV4' is not supported. */
+ virtual int addV4(const char *pcszAdapter, const char *pcszAddress, const char *pcszNetmask = 0) = 0;
+ /** IPv6-specific handler used by generic implementation of 'set' method. */
+ virtual int addV6(const char *pcszAdapter, const char *pcszAddress, const char *pcszNetmask = 0) = 0;
+ /** IPv4-specific handler used by generic implementation of 'set' method. */
+ virtual int setV4(const char *pcszAdapter, const char *pcszAddress, const char *pcszNetmask = 0) = 0;
+ /** IPv4-specific handler used by generic implementation of 'remove' method. */
+ virtual int removeV4(const char *pcszAdapter, const char *pcszAddress) = 0;
+ /** IPv6-specific handler used by generic implementation of 'remove' method. */
+ virtual int removeV6(const char *pcszAdapter, const char *pcszAddress) = 0;
+ /** Composes the argument list of command that obtains all addresses assigned to the adapter. */
+ virtual CmdList getShowCommand(const char *pcszAdapter) const = 0;
+
+ /** Prepares an array of C strings needed for 'exec' call. */
+ char * const * allocArgv(const CmdList& commandList);
+ /** Hides process creation details. To be used in derived classes. */
+ int execute(CmdList& commandList);
+
+ /** A path to executable command. */
+ const char *m_pszPath;
+private:
+ /** Removes all previously asssigned addresses of a particular protocol family. */
+ int removeAddresses(const char *pcszAdapter, const char *pcszFamily);
+};
+
+/*
+ * A generic implementation of 'ifconfig' command for all platforms.
+ */
+class CmdIfconfig : public AddressCommand
+{
+public:
+ CmdIfconfig()
+ {
+ struct stat s;
+ if ( !stat(VBOXADPCTL_IFCONFIG_PATH1, &s)
+ && S_ISREG(s.st_mode))
+ m_pszPath = (char*)VBOXADPCTL_IFCONFIG_PATH1;
+ else
+ m_pszPath = (char*)VBOXADPCTL_IFCONFIG_PATH2;
+ };
+
+protected:
+ /** Returns platform-specific subcommand to add an address. */
+ virtual const char *addCmdArg(void) const = 0;
+ /** Returns platform-specific subcommand to remove an address. */
+ virtual const char *delCmdArg(void) const = 0;
+ virtual CmdList getShowCommand(const char *pcszAdapter) const
+ { return CmdList(pcszAdapter); };
+ virtual int addV4(const char *pcszAdapter, const char *pcszAddress, const char *pcszNetmask = 0)
+ { return ENOTSUP; NOREF(pcszAdapter); NOREF(pcszAddress); NOREF(pcszNetmask); };
+ virtual int addV6(const char *pcszAdapter, const char *pcszAddress, const char *pcszNetmask = 0)
+ {
+ return execute(CmdList(pcszAdapter) << "inet6" << addCmdArg() << pcszAddress);
+ NOREF(pcszNetmask);
+ };
+ virtual int setV4(const char *pcszAdapter, const char *pcszAddress, const char *pcszNetmask = 0)
+ {
+ if (!pcszNetmask)
+ return execute(CmdList(pcszAdapter) << pcszAddress);
+ return execute(CmdList(pcszAdapter) << pcszAddress << "netmask" << pcszNetmask);
+ };
+ virtual int removeV4(const char *pcszAdapter, const char *pcszAddress)
+ { return execute(CmdList(pcszAdapter) << delCmdArg() << pcszAddress); };
+ virtual int removeV6(const char *pcszAdapter, const char *pcszAddress)
+ { return execute(CmdList(pcszAdapter) << "inet6" << delCmdArg() << pcszAddress); };
+};
+
+
+/*********************************************************************************************************************************
+* Platform-specific commands *
+*********************************************************************************************************************************/
+
+class CmdIfconfigLinux : public CmdIfconfig
+{
+protected:
+ virtual int removeV4(const char *pcszAdapter, const char *pcszAddress)
+ { return execute(CmdList(pcszAdapter) << "0.0.0.0"); NOREF(pcszAddress); };
+ virtual const char *addCmdArg(void) const { return "add"; };
+ virtual const char *delCmdArg(void) const { return "del"; };
+};
+
+class CmdIfconfigDarwin : public CmdIfconfig
+{
+protected:
+ virtual const char *addCmdArg(void) const { return "add"; };
+ virtual const char *delCmdArg(void) const { return "delete"; };
+};
+
+class CmdIfconfigSolaris : public CmdIfconfig
+{
+public:
+ virtual int set(const char *pcszAdapter, const char *pcszAddress, const char *pcszNetmask = 0)
+ {
+ const char *pcszFamily = isAddrV6(pcszAddress) ? "inet6" : "inet";
+ int status;
+
+ status = execute(CmdList(pcszAdapter) << pcszFamily);
+ if (status != EXIT_SUCCESS)
+ status = execute(CmdList(pcszAdapter) << pcszFamily << "plumb" << "up");
+ if (status != EXIT_SUCCESS)
+ return status;
+
+ return CmdIfconfig::set(pcszAdapter, pcszAddress, pcszNetmask);
+ };
+protected:
+ /* We can umplumb IPv4 interfaces only! */
+ virtual int removeV4(const char *pcszAdapter, const char *pcszAddress)
+ {
+ int rc = CmdIfconfig::removeV4(pcszAdapter, pcszAddress);
+
+ /** @todo Do we really need to unplumb inet here? */
+ execute(CmdList(pcszAdapter) << "inet" << "unplumb");
+ return rc;
+ };
+ virtual const char *addCmdArg(void) const { return "addif"; };
+ virtual const char *delCmdArg(void) const { return "removeif"; };
+};
+
+
+#ifdef RT_OS_LINUX
+/*
+ * Helper class to incapsulate IPv4 address conversion.
+ *
+ * Note that this class relies on NetworkAddress to have been used for
+ * checking validity of IP addresses prior calling any methods of this
+ * class.
+ */
+class AddressIPv4
+{
+public:
+ AddressIPv4(const char *pcszAddress, const char *pcszNetmask = 0)
+ {
+ m_Prefix = 0;
+ memset(&m_Address, 0, sizeof(m_Address));
+
+ if (pcszNetmask)
+ m_Prefix = maskToPrefix(pcszNetmask);
+ else
+ {
+ /*
+ * Since guessing network mask is probably futile we simply use 24,
+ * as it matches our defaults. When non-default values are used
+ * providing a proper netmask is up to the user.
+ */
+ m_Prefix = 24;
+ }
+ int rc = RTNetStrToIPv4Addr(pcszAddress, &m_Address);
+ AssertRCReturnVoid(rc);
+ snprintf(m_szAddressAndMask, sizeof(m_szAddressAndMask), "%s/%d", pcszAddress, m_Prefix);
+ deriveBroadcast(&m_Address, m_Prefix);
+ }
+ const char *getBroadcast() const { return m_szBroadcast; };
+ const char *getAddressAndMask() const { return m_szAddressAndMask; };
+private:
+ int maskToPrefix(const char *pcszNetmask);
+ void deriveBroadcast(PCRTNETADDRIPV4 pcAddress, int uPrefix);
+
+ int m_Prefix;
+ RTNETADDRIPV4 m_Address;
+ char m_szAddressAndMask[INET_ADDRSTRLEN + 3]; /* e.g. 192.168.56.101/24 */
+ char m_szBroadcast[INET_ADDRSTRLEN];
+};
+
+int AddressIPv4::maskToPrefix(const char *pcszNetmask)
+{
+ RTNETADDRIPV4 mask;
+ int prefix = 0;
+
+ int rc = RTNetStrToIPv4Addr(pcszNetmask, &mask);
+ AssertRCReturn(rc, 0);
+ rc = RTNetMaskToPrefixIPv4(&mask, &prefix);
+ AssertRCReturn(rc, 0);
+
+ return prefix;
+}
+
+void AddressIPv4::deriveBroadcast(PCRTNETADDRIPV4 pcAddress, int iPrefix)
+{
+ RTNETADDRIPV4 mask, broadcast;
+ int rc = RTNetPrefixToMaskIPv4(iPrefix, &mask);
+ AssertRCReturnVoid(rc);
+ broadcast.au32[0] = (pcAddress->au32[0] & mask.au32[0]) | ~mask.au32[0];
+ inet_ntop(AF_INET, broadcast.au32, m_szBroadcast, sizeof(m_szBroadcast));
+}
+
+
+/*
+ * Linux-specific implementation of 'ip' command, as other platforms do not support it.
+ */
+class CmdIpLinux : public AddressCommand
+{
+public:
+ CmdIpLinux() { m_pszPath = "/sbin/ip"; };
+ /**
+ * IPv4 and IPv6 syntax is the same, so we override `remove` instead of implementing
+ * family-specific commands. It would be easier to use the same body in both
+ * 'removeV4' and 'removeV6', so we override 'remove' to illustrate how to do common
+ * implementation.
+ */
+ virtual int remove(const char *pcszAdapter, const char *pcszAddress)
+ { return execute(CmdList("addr") << "del" << pcszAddress << "dev" << pcszAdapter); };
+protected:
+ virtual int addV4(const char *pcszAdapter, const char *pcszAddress, const char *pcszNetmask = 0)
+ {
+ AddressIPv4 addr(pcszAddress, pcszNetmask);
+ bringUp(pcszAdapter);
+ return execute(CmdList("addr") << "add" << addr.getAddressAndMask() <<
+ "broadcast" << addr.getBroadcast() << "dev" << pcszAdapter);
+ };
+ virtual int addV6(const char *pcszAdapter, const char *pcszAddress, const char *pcszNetmask = 0)
+ {
+ bringUp(pcszAdapter);
+ return execute(CmdList("addr") << "add" << pcszAddress << "dev" << pcszAdapter);
+ NOREF(pcszNetmask);
+ };
+ /**
+ * Our command does not support 'replacing' addresses. Reporting this fact to generic implementation
+ * of 'set' causes it to remove all assigned addresses, then 'add' the new one.
+ */
+ virtual int setV4(const char *pcszAdapter, const char *pcszAddress, const char *pcszNetmask = 0)
+ { return ENOTSUP; NOREF(pcszAdapter); NOREF(pcszAddress); NOREF(pcszNetmask); };
+ /** We use family-agnostic command syntax. See 'remove' above. */
+ virtual int removeV4(const char *pcszAdapter, const char *pcszAddress)
+ { return ENOTSUP; NOREF(pcszAdapter); NOREF(pcszAddress); };
+ /** We use family-agnostic command syntax. See 'remove' above. */
+ virtual int removeV6(const char *pcszAdapter, const char *pcszAddress)
+ { return ENOTSUP; NOREF(pcszAdapter); NOREF(pcszAddress); };
+ virtual CmdList getShowCommand(const char *pcszAdapter) const
+ { return CmdList("addr") << "show" << "dev" << pcszAdapter; };
+private:
+ /** Brings up the adapter */
+ void bringUp(const char *pcszAdapter)
+ { execute(CmdList("link") << "set" << "dev" << pcszAdapter << "up"); };
+};
+#endif /* RT_OS_LINUX */
+
+
+/*********************************************************************************************************************************
+* Generic address command implementations *
+*********************************************************************************************************************************/
+
+int AddressCommand::set(const char *pcszAdapter, const char *pcszAddress, const char *pcszNetmask)
+{
+ if (isAddrV6(pcszAddress))
+ {
+ removeAddresses(pcszAdapter, "inet6");
+ return addV6(pcszAdapter, pcszAddress, pcszNetmask);
+ }
+ int rc = setV4(pcszAdapter, pcszAddress, pcszNetmask);
+ if (rc == ENOTSUP)
+ {
+ removeAddresses(pcszAdapter, "inet");
+ rc = addV4(pcszAdapter, pcszAddress, pcszNetmask);
+ }
+ return rc;
+}
+
+int AddressCommand::remove(const char *pcszAdapter, const char *pcszAddress)
+{
+ if (isAddrV6(pcszAddress))
+ return removeV6(pcszAdapter, pcszAddress);
+ return removeV4(pcszAdapter, pcszAddress);
+}
+
+/*
+ * Allocate an array of exec arguments. In addition to arguments provided
+ * we need to include the full path to the executable as well as "terminating"
+ * null pointer marking the end of the array.
+ */
+char * const * AddressCommand::allocArgv(const CmdList& list)
+{
+ int i = 0;
+ std::list<const char *>::const_iterator it;
+ const char **argv = (const char **)calloc(list.getList().size() + 2, sizeof(const char *));
+ if (argv)
+ {
+ argv[i++] = m_pszPath;
+ for (it = list.getList().begin(); it != list.getList().end(); ++it)
+ argv[i++] = *it;
+ argv[i++] = NULL;
+ }
+ return (char * const*)argv;
+}
+
+int AddressCommand::execute(CmdList& list)
+{
+ char * const pEnv[] = { (char*)"LC_ALL=C", NULL };
+ char * const* argv = allocArgv(list);
+ if (argv == NULL)
+ return EXIT_FAILURE;
+
+ if (verbose)
+ {
+ const char *sep = "";
+ for (const char * const *pArg = argv; *pArg != NULL; ++pArg)
+ {
+ printf("%s%s", sep, *pArg);
+ sep = " ";
+ }
+ printf("\n");
+ }
+ if (dry_run)
+ {
+ free((void *)argv);
+ return EXIT_SUCCESS;
+ }
+
+ int rc = EXIT_FAILURE; /* o/~ hope for the best, expect the worst */
+ pid_t childPid = fork();
+ switch (childPid)
+ {
+ case -1: /* Something went wrong. */
+ perror("fork");
+ break;
+
+ case 0: /* Child process. */
+ if (execve(argv[0], argv, pEnv) == -1)
+ {
+ perror("execve");
+ exit(EXIT_FAILURE);
+ /* NOTREACHED */
+ }
+ break;
+
+ default: /* Parent process. */
+ {
+ int status;
+ pid_t waited = waitpid(childPid, &status, 0);
+ if (waited == childPid) /* likely*/
+ {
+ if (WIFEXITED(status) && WEXITSTATUS(status) == EXIT_SUCCESS)
+ rc = EXIT_SUCCESS;
+ }
+ else if (waited == (pid_t)-1)
+ {
+ perror("waitpid");
+ }
+ else
+ {
+ /* should never happen */
+ fprintf(stderr, "waitpid: unexpected pid %lld\n",
+ (long long int)waited);
+ }
+ break;
+ }
+ }
+
+ free((void*)argv);
+ return rc;
+}
+
+#define MAX_ADDRESSES 128
+#define MAX_ADDRLEN 64
+
+int AddressCommand::removeAddresses(const char *pcszAdapter, const char *pcszFamily)
+{
+ char szBuf[1024];
+ char aszAddresses[MAX_ADDRESSES][MAX_ADDRLEN];
+ int rc = EXIT_SUCCESS;
+ int fds[2];
+
+ memset(aszAddresses, 0, sizeof(aszAddresses));
+
+ rc = pipe(fds);
+ if (rc < 0)
+ return errno;
+
+ pid_t pid = fork();
+ if (pid < 0)
+ return errno;
+
+ if (pid == 0)
+ {
+ /* child */
+ close(fds[0]);
+ close(STDOUT_FILENO);
+ rc = dup2(fds[1], STDOUT_FILENO);
+ if (rc >= 0)
+ {
+ char * const * argv = allocArgv(getShowCommand(pcszAdapter));
+ char * const envp[] = { (char*)"LC_ALL=C", NULL };
+
+ if (execve(argv[0], argv, envp) == -1)
+ {
+ free((void *)argv);
+ return errno;
+ }
+
+ free((void *)argv);
+ }
+ return rc;
+ }
+
+ /* parent */
+ close(fds[1]);
+ FILE *fp = fdopen(fds[0], "r");
+ if (!fp)
+ return false;
+
+ int cAddrs;
+ for (cAddrs = 0; cAddrs < MAX_ADDRESSES && fgets(szBuf, sizeof(szBuf), fp);)
+ {
+ int cbSkipWS = strspn(szBuf, " \t");
+ char *pszWord = strtok(szBuf + cbSkipWS, " ");
+ /* We are concerned with particular family address lines only. */
+ if (!pszWord || strcmp(pszWord, pcszFamily))
+ continue;
+
+ pszWord = strtok(NULL, " ");
+
+ /* Skip "addr:" word if present. */
+ if (pszWord && !strcmp(pszWord, "addr:"))
+ pszWord = strtok(NULL, " ");
+
+ /* Skip link-local address lines. */
+ if (!pszWord || !strncmp(pszWord, "fe80", 4))
+ continue;
+ strncpy(aszAddresses[cAddrs++], pszWord, MAX_ADDRLEN-1);
+ }
+ fclose(fp);
+
+ for (int i = 0; i < cAddrs && rc == EXIT_SUCCESS; i++)
+ rc = remove(pcszAdapter, aszAddresses[i]);
+
+ return rc;
+}
+
+
+/*********************************************************************************************************************************
+* Adapter creation/removal implementations *
+*********************************************************************************************************************************/
+
+/*
+ * A generic implementation of adapter creation/removal ioctl calls.
+ */
+class Adapter
+{
+public:
+ int add(char *pszNameInOut);
+ int remove(const char *pcszName);
+ int checkName(const char *pcszNameIn, char *pszNameOut, size_t cbNameOut);
+protected:
+ virtual int doIOCtl(unsigned long iCmd, VBOXNETADPREQ *pReq);
+};
+
+/*
+ * Solaris does not support dynamic creation/removal of adapters.
+ */
+class AdapterSolaris : public Adapter
+{
+protected:
+ virtual int doIOCtl(unsigned long iCmd, VBOXNETADPREQ *pReq)
+ { return 1 /*ENOTSUP*/; NOREF(iCmd); NOREF(pReq); };
+};
+
+#if defined(RT_OS_LINUX)
+/*
+ * Linux implementation provides a 'workaround' to obtain adapter speed.
+ */
+class AdapterLinux : public Adapter
+{
+public:
+ int getSpeed(const char *pszName, unsigned *puSpeed);
+};
+
+int AdapterLinux::getSpeed(const char *pszName, unsigned *puSpeed)
+{
+ struct ifreq IfReq;
+ struct ethtool_value EthToolVal;
+ struct ethtool_cmd EthToolReq;
+ int fd = socket(AF_INET, SOCK_DGRAM, 0);
+ if (fd < 0)
+ {
+ fprintf(stderr, "VBoxNetAdpCtl: Error while retrieving link "
+ "speed for %s: ", pszName);
+ perror("VBoxNetAdpCtl: failed to open control socket");
+ return ADPCTLERR_SOCKET_FAILED;
+ }
+ /* Get link status first. */
+ memset(&EthToolVal, 0, sizeof(EthToolVal));
+ memset(&IfReq, 0, sizeof(IfReq));
+ snprintf(IfReq.ifr_name, sizeof(IfReq.ifr_name), "%s", pszName);
+
+ EthToolVal.cmd = ETHTOOL_GLINK;
+ IfReq.ifr_data = (caddr_t)&EthToolVal;
+ int rc = ioctl(fd, SIOCETHTOOL, &IfReq);
+ if (rc == 0)
+ {
+ if (EthToolVal.data)
+ {
+ memset(&IfReq, 0, sizeof(IfReq));
+ snprintf(IfReq.ifr_name, sizeof(IfReq.ifr_name), "%s", pszName);
+ EthToolReq.cmd = ETHTOOL_GSET;
+ IfReq.ifr_data = (caddr_t)&EthToolReq;
+ rc = ioctl(fd, SIOCETHTOOL, &IfReq);
+ if (rc == 0)
+ {
+ *puSpeed = EthToolReq.speed;
+ }
+ else
+ {
+ fprintf(stderr, "VBoxNetAdpCtl: Error while retrieving link "
+ "speed for %s: ", pszName);
+ perror("VBoxNetAdpCtl: ioctl failed");
+ rc = ADPCTLERR_IOCTL_FAILED;
+ }
+ }
+ else
+ *puSpeed = 0;
+ }
+ else
+ {
+ fprintf(stderr, "VBoxNetAdpCtl: Error while retrieving link "
+ "status for %s: ", pszName);
+ perror("VBoxNetAdpCtl: ioctl failed");
+ rc = ADPCTLERR_IOCTL_FAILED;
+ }
+
+ close(fd);
+ return rc;
+}
+#endif /* defined(RT_OS_LINUX) */
+
+int Adapter::add(char *pszName /* in/out */)
+{
+ VBOXNETADPREQ Req;
+ memset(&Req, '\0', sizeof(Req));
+ snprintf(Req.szName, sizeof(Req.szName), "%s", pszName);
+ int rc = doIOCtl(VBOXNETADP_CTL_ADD, &Req);
+ if (rc == 0)
+ strncpy(pszName, Req.szName, VBOXNETADP_MAX_NAME_LEN);
+ return rc;
+}
+
+int Adapter::remove(const char *pcszName)
+{
+ VBOXNETADPREQ Req;
+ memset(&Req, '\0', sizeof(Req));
+ snprintf(Req.szName, sizeof(Req.szName), "%s", pcszName);
+ return doIOCtl(VBOXNETADP_CTL_REMOVE, &Req);
+}
+
+int Adapter::checkName(const char *pcszNameIn, char *pszNameOut, size_t cbNameOut)
+{
+ int iAdapterIndex = -1;
+
+ if ( strlen(pcszNameIn) >= VBOXNETADP_MAX_NAME_LEN
+ || sscanf(pcszNameIn, "vboxnet%d", &iAdapterIndex) != 1
+ || iAdapterIndex < 0 || iAdapterIndex >= VBOXNETADP_MAX_INSTANCES )
+ {
+ fprintf(stderr, "VBoxNetAdpCtl: Setting configuration for '%s' is not supported.\n", pcszNameIn);
+ return ADPCTLERR_BAD_NAME;
+ }
+ snprintf(pszNameOut, cbNameOut, "vboxnet%d", iAdapterIndex);
+ if (strcmp(pszNameOut, pcszNameIn))
+ {
+ fprintf(stderr, "VBoxNetAdpCtl: Invalid adapter name '%s'.\n", pcszNameIn);
+ return ADPCTLERR_BAD_NAME;
+ }
+
+ return 0;
+}
+
+int Adapter::doIOCtl(unsigned long iCmd, VBOXNETADPREQ *pReq)
+{
+ int fd = open(VBOXNETADP_CTL_DEV_NAME, O_RDWR);
+ if (fd == -1)
+ {
+ fprintf(stderr, "VBoxNetAdpCtl: Error while %s %s: ",
+ iCmd == VBOXNETADP_CTL_REMOVE ? "removing" : "adding",
+ pReq->szName[0] ? pReq->szName : "new interface");
+ perror("failed to open " VBOXNETADP_CTL_DEV_NAME);
+ return ADPCTLERR_NO_CTL_DEV;
+ }
+
+ int rc = ioctl(fd, iCmd, pReq);
+ if (rc == -1)
+ {
+ fprintf(stderr, "VBoxNetAdpCtl: Error while %s %s: ",
+ iCmd == VBOXNETADP_CTL_REMOVE ? "removing" : "adding",
+ pReq->szName[0] ? pReq->szName : "new interface");
+ perror("VBoxNetAdpCtl: ioctl failed for " VBOXNETADP_CTL_DEV_NAME);
+ rc = ADPCTLERR_IOCTL_FAILED;
+ }
+
+ close(fd);
+
+ return rc;
+}
+
+
+/*********************************************************************************************************************************
+* Global config file implementation *
+*********************************************************************************************************************************/
+
+#define VBOX_GLOBAL_NETWORK_CONFIG_PATH "/etc/vbox/networks.conf"
+#define VBOXNET_DEFAULT_IPV4MASK "255.255.255.0"
+
+class NetworkAddress
+{
+ public:
+ bool isValidString(const char *pcszNetwork);
+ bool isValid() { return m_fValid; };
+ virtual bool matches(const char *pcszNetwork) = 0;
+ virtual const char *defaultNetwork() = 0;
+ protected:
+ bool m_fValid;
+};
+
+bool NetworkAddress::isValidString(const char *pcszNetwork)
+{
+ RTNETADDRIPV4 addrv4;
+ RTNETADDRIPV6 addrv6;
+ int prefix;
+ int rc = RTNetStrToIPv4Cidr(pcszNetwork, &addrv4, &prefix);
+ if (RT_SUCCESS(rc))
+ return true;
+ rc = RTNetStrToIPv6Cidr(pcszNetwork, &addrv6, &prefix);
+ return RT_SUCCESS(rc);
+}
+
+class NetworkAddressIPv4 : public NetworkAddress
+{
+ public:
+ NetworkAddressIPv4(const char *pcszIpAddress, const char *pcszNetMask = VBOXNET_DEFAULT_IPV4MASK);
+ virtual bool matches(const char *pcszNetwork);
+ virtual const char *defaultNetwork() { return "192.168.56.1/21"; }; /* Matches defaults in VBox/Main/include/netif.h, see @bugref{10077}. */
+
+ protected:
+ bool isValidUnicastAddress(PCRTNETADDRIPV4 address);
+
+ private:
+ RTNETADDRIPV4 m_address;
+ int m_prefix;
+};
+
+NetworkAddressIPv4::NetworkAddressIPv4(const char *pcszIpAddress, const char *pcszNetMask)
+{
+ int rc = RTNetStrToIPv4Addr(pcszIpAddress, &m_address);
+ if (RT_SUCCESS(rc))
+ {
+ RTNETADDRIPV4 mask;
+ rc = RTNetStrToIPv4Addr(pcszNetMask, &mask);
+ if (RT_FAILURE(rc))
+ m_fValid = false;
+ else
+ rc = RTNetMaskToPrefixIPv4(&mask, &m_prefix);
+ }
+#if 0 /* cmd.set() does not support CIDR syntax */
+ else
+ rc = RTNetStrToIPv4Cidr(pcszIpAddress, &m_address, &m_prefix);
+#endif
+ m_fValid = RT_SUCCESS(rc) && isValidUnicastAddress(&m_address);
+}
+
+bool NetworkAddressIPv4::isValidUnicastAddress(PCRTNETADDRIPV4 address)
+{
+ /* Multicast addresses are not allowed. */
+ if ((address->au8[0] & 0xF0) == 0xE0)
+ return false;
+
+ /* Broadcast address is not allowed. */
+ if (address->au32[0] == 0xFFFFFFFF) /* Endianess does not matter in this particual case. */
+ return false;
+
+ /* Loopback addresses are not allowed. */
+ if ((address->au8[0] & 0xFF) == 0x7F)
+ return false;
+
+ return true;
+}
+
+bool NetworkAddressIPv4::matches(const char *pcszNetwork)
+{
+ RTNETADDRIPV4 allowedNet, allowedMask;
+ int allowedPrefix;
+ int rc = RTNetStrToIPv4Cidr(pcszNetwork, &allowedNet, &allowedPrefix);
+ if (RT_SUCCESS(rc))
+ rc = RTNetPrefixToMaskIPv4(allowedPrefix, &allowedMask);
+ if (RT_FAILURE(rc))
+ return false;
+ return m_prefix >= allowedPrefix && (m_address.au32[0] & allowedMask.au32[0]) == (allowedNet.au32[0] & allowedMask.au32[0]);
+}
+
+class NetworkAddressIPv6 : public NetworkAddress
+{
+ public:
+ NetworkAddressIPv6(const char *pcszIpAddress);
+ virtual bool matches(const char *pcszNetwork);
+ virtual const char *defaultNetwork() { return "FE80::/10"; };
+ private:
+ RTNETADDRIPV6 m_address;
+ int m_prefix;
+};
+
+NetworkAddressIPv6::NetworkAddressIPv6(const char *pcszIpAddress)
+{
+ int rc = RTNetStrToIPv6Cidr(pcszIpAddress, &m_address, &m_prefix);
+ m_fValid = RT_SUCCESS(rc);
+}
+
+bool NetworkAddressIPv6::matches(const char *pcszNetwork)
+{
+ RTNETADDRIPV6 allowedNet, allowedMask;
+ int allowedPrefix;
+ int rc = RTNetStrToIPv6Cidr(pcszNetwork, &allowedNet, &allowedPrefix);
+ if (RT_SUCCESS(rc))
+ rc = RTNetPrefixToMaskIPv6(allowedPrefix, &allowedMask);
+ if (RT_FAILURE(rc))
+ return false;
+ RTUINT128U u128Provided, u128Allowed;
+ return m_prefix >= allowedPrefix
+ && RTUInt128Compare(RTUInt128And(&u128Provided, &m_address, &allowedMask), RTUInt128And(&u128Allowed, &allowedNet, &allowedMask)) == 0;
+}
+
+
+class GlobalNetworkPermissionsConfig
+{
+ public:
+ bool forbids(const char *pcszIpAddress); /* address or address with mask in cidr */
+ bool forbids(const char *pcszIpAddress, const char *pcszNetMask);
+
+ private:
+ bool forbids(NetworkAddress& address);
+};
+
+bool GlobalNetworkPermissionsConfig::forbids(const char *pcszIpAddress)
+{
+ NetworkAddressIPv6 addrv6(pcszIpAddress);
+
+ if (addrv6.isValid())
+ return forbids(addrv6);
+
+ NetworkAddressIPv4 addrv4(pcszIpAddress);
+
+ if (addrv4.isValid())
+ return forbids(addrv4);
+
+ fprintf(stderr, "Error: invalid address '%s'\n", pcszIpAddress);
+ return true;
+}
+
+bool GlobalNetworkPermissionsConfig::forbids(const char *pcszIpAddress, const char *pcszNetMask)
+{
+ NetworkAddressIPv4 addrv4(pcszIpAddress, pcszNetMask);
+
+ if (addrv4.isValid())
+ return forbids(addrv4);
+
+ fprintf(stderr, "Error: invalid address '%s' with mask '%s'\n", pcszIpAddress, pcszNetMask);
+ return true;
+}
+
+bool GlobalNetworkPermissionsConfig::forbids(NetworkAddress& address)
+{
+ FILE *fp = fopen(VBOX_GLOBAL_NETWORK_CONFIG_PATH, "r");
+ if (!fp)
+ {
+ if (verbose)
+ fprintf(stderr, "Info: matching against default '%s' => %s\n", address.defaultNetwork(),
+ address.matches(address.defaultNetwork()) ? "MATCH" : "no match");
+ return !address.matches(address.defaultNetwork());
+ }
+
+ char *pszToken, szLine[1024];
+ for (int line = 1; fgets(szLine, sizeof(szLine), fp); ++line)
+ {
+ pszToken = strtok(szLine, " \t\n");
+ /* Skip anything except '*' lines */
+ if (pszToken == NULL || strcmp("*", pszToken))
+ continue;
+ /* Match the specified address against each network */
+ while ((pszToken = strtok(NULL, " \t\n")) != NULL)
+ {
+ if (!address.isValidString(pszToken))
+ {
+ fprintf(stderr, "Warning: %s(%d) invalid network '%s'\n", VBOX_GLOBAL_NETWORK_CONFIG_PATH, line, pszToken);
+ continue;
+ }
+ if (verbose)
+ fprintf(stderr, "Info: %s(%d) matching against '%s' => %s\n", VBOX_GLOBAL_NETWORK_CONFIG_PATH, line, pszToken,
+ address.matches(pszToken) ? "MATCH" : "no match");
+ if (address.matches(pszToken))
+ return false;
+ }
+ }
+ fclose(fp);
+ return true;
+}
+
+
+/*********************************************************************************************************************************
+* Main logic, argument parsing, etc. *
+*********************************************************************************************************************************/
+
+#if defined(RT_OS_LINUX)
+static CmdIfconfigLinux g_ifconfig;
+static AdapterLinux g_adapter;
+#elif defined(RT_OS_SOLARIS)
+static CmdIfconfigSolaris g_ifconfig;
+static AdapterSolaris g_adapter;
+#else
+static CmdIfconfigDarwin g_ifconfig;
+static Adapter g_adapter;
+#endif
+
+static AddressCommand& chooseAddressCommand()
+{
+#if defined(RT_OS_LINUX)
+ static CmdIpLinux g_ip;
+ if (g_ip.isAvailable())
+ return g_ip;
+#endif
+ return g_ifconfig;
+}
+
+int main(int argc, char *argv[])
+{
+ char szAdapterName[VBOXNETADP_MAX_NAME_LEN];
+ int rc = RTR3InitExe(argc, &argv, 0 /*fFlags*/);
+ if (RT_FAILURE(rc))
+ return RTMsgInitFailure(rc);
+
+
+ AddressCommand& cmd = chooseAddressCommand();
+
+
+ static const struct option options[] = {
+ { "dry-run", no_argument, NULL, 'n' },
+ { "verbose", no_argument, NULL, 'v' },
+ { NULL, 0, NULL, 0 }
+ };
+
+ int ch;
+ while ((ch = getopt_long(argc, argv, "nv", options, NULL)) != -1)
+ {
+ switch (ch)
+ {
+ case 'n':
+ dry_run = true;
+ verbose = true;
+ break;
+
+ case 'v':
+ verbose = true;
+ break;
+
+ case '?':
+ default:
+ return usage();
+ }
+ }
+ argc -= optind;
+ argv += optind;
+
+ if (argc == 0)
+ return usage();
+
+
+ /*
+ * VBoxNetAdpCtl add
+ */
+ if (strcmp(argv[0], "add") == 0)
+ {
+ if (argc > 1) /* extraneous args */
+ return usage();
+
+ /* Create a new interface, print its name. */
+ *szAdapterName = '\0';
+ rc = g_adapter.add(szAdapterName);
+ if (rc == EXIT_SUCCESS)
+ puts(szAdapterName);
+
+ return rc;
+ }
+
+
+ /*
+ * All other variants are of the form:
+ * VBoxNetAdpCtl if0 ...action...
+ */
+ const char * const ifname = argv[0];
+ const char * const action = argv[1];
+ if (argc < 2)
+ return usage();
+
+
+#ifdef RT_OS_LINUX
+ /*
+ * VBoxNetAdpCtl iface42 speed
+ *
+ * This ugly hack is needed for retrieving the link speed on
+ * pre-2.6.33 kernels (see @bugref{6345}).
+ *
+ * This variant is used with any interface, not just host-only.
+ */
+ if (strcmp(action, "speed") == 0)
+ {
+ if (argc > 2) /* extraneous args */
+ return usage();
+
+ if (strlen(ifname) >= IFNAMSIZ)
+ {
+ fprintf(stderr, "Interface name too long\n");
+ return EXIT_FAILURE;
+ }
+
+ unsigned uSpeed = 0;
+ rc = g_adapter.getSpeed(ifname, &uSpeed);
+ if (rc == EXIT_SUCCESS)
+ printf("%u", uSpeed);
+
+ return rc;
+ }
+#endif /* RT_OS_LINUX */
+
+
+ /*
+ * The rest of the actions only operate on host-only interfaces.
+ */
+ /** @todo Why the code below uses both ifname and szAdapterName? */
+ rc = g_adapter.checkName(ifname, szAdapterName, sizeof(szAdapterName));
+ if (rc != EXIT_SUCCESS)
+ return rc;
+
+
+ /*
+ * VBoxNetAdpCtl vboxnetN remove
+ */
+ if (strcmp(action, "remove") == 0)
+ {
+ if (argc > 2) /* extraneous args */
+ return usage();
+
+ /* Remove an existing interface */
+ return g_adapter.remove(ifname);
+ }
+
+ /*
+ * VBoxNetAdpCtl vboxnetN add
+ */
+ if (strcmp(action, "add") == 0)
+ {
+ if (argc > 2) /* extraneous args */
+ return usage();
+
+ /* Create an interface with the given name, print its name. */
+ rc = g_adapter.add(szAdapterName);
+ if (rc == EXIT_SUCCESS)
+ puts(szAdapterName);
+
+ return rc;
+ }
+
+
+ /*
+ * The rest of the actions are of the form
+ * VBoxNetAdpCtl vboxnetN $addr [...]
+ *
+ * Use the argument after the address to select the action.
+ */
+ /** @todo Do early verification of addr format here? */
+ const char * const addr = argv[1];
+ const char * const keyword = argv[2];
+
+ GlobalNetworkPermissionsConfig config;
+
+ /*
+ * VBoxNetAdpCtl vboxnetN 1.2.3.4
+ */
+ if (keyword == NULL)
+ {
+ if (config.forbids(addr))
+ {
+ fprintf(stderr, "Error: permission denied\n");
+ return -VERR_ACCESS_DENIED;
+ }
+
+ return cmd.set(ifname, addr);
+ }
+
+ /*
+ * VBoxNetAdpCtl vboxnetN 1.2.3.4 netmask 255.255.255.0
+ */
+ if (strcmp(keyword, "netmask") == 0)
+ {
+ if (argc != 4) /* too few or too many args */
+ return usage();
+
+ const char * const mask = argv[3];
+ if (config.forbids(addr, mask))
+ {
+ fprintf(stderr, "Error: permission denied\n");
+ return -VERR_ACCESS_DENIED;
+ }
+
+ return cmd.set(ifname, addr, mask);
+ }
+
+ /*
+ * VBoxNetAdpCtl vboxnetN 1.2.3.4 remove
+ */
+ if (strcmp(keyword, "remove") == 0)
+ {
+ if (argc > 3) /* extraneous args */
+ return usage();
+
+ return cmd.remove(ifname, addr);
+ }
+
+ return usage();
+}
diff --git a/src/VBox/HostDrivers/darwin/Makefile.kmk b/src/VBox/HostDrivers/darwin/Makefile.kmk
new file mode 100644
index 00000000..47938915
--- /dev/null
+++ b/src/VBox/HostDrivers/darwin/Makefile.kmk
@@ -0,0 +1,49 @@
+# $Id: Makefile.kmk $
+## @file
+# Sub-makefile for the darwin host driver helper scripts.
+#
+
+#
+# Copyright (C) 2010-2023 Oracle and/or its affiliates.
+#
+# This file is part of VirtualBox base platform packages, as
+# available from https://www.virtualbox.org.
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation, in version 3 of the
+# License.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, see <https://www.gnu.org/licenses>.
+#
+# The contents of this file may alternatively be used under the terms
+# of the Common Development and Distribution License Version 1.0
+# (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+# in the VirtualBox distribution, in which case the provisions of the
+# CDDL are applicable instead of those of the GPL.
+#
+# You may elect to license modified versions of this file under the
+# terms and conditions of either the GPL or the CDDL or both.
+#
+# SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+#
+
+SUB_DEPTH = ../../../..
+include $(KBUILD_PATH)/subheader.kmk
+
+ifdef VBOX_WITH_VBOXDRV
+ INSTALLS += HostDrivers-darwin-sh
+ HostDrivers-darwin-sh_INST = $(INST_DIST)
+ HostDrivers-darwin-sh_INSTTYPE = stage
+ HostDrivers-darwin-sh_EXEC_SOURCES = \
+ loadall.sh
+endif
+
+include $(FILE_KBUILD_SUB_FOOTER)
+
diff --git a/src/VBox/HostDrivers/darwin/VBoxNetSend.h b/src/VBox/HostDrivers/darwin/VBoxNetSend.h
new file mode 100644
index 00000000..4585a255
--- /dev/null
+++ b/src/VBox/HostDrivers/darwin/VBoxNetSend.h
@@ -0,0 +1,132 @@
+/* $Id: VBoxNetSend.h $ */
+/** @file
+ * A place to share code and definitions between VBoxNetAdp and VBoxNetFlt host drivers.
+ */
+
+/*
+ * Copyright (C) 2014-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+/** @todo move this to src/VBox/HostDrivers/darwin as a .cpp file. */
+
+#ifndef VBOX_INCLUDED_SRC_darwin_VBoxNetSend_h
+#define VBOX_INCLUDED_SRC_darwin_VBoxNetSend_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+#if defined(RT_OS_DARWIN)
+
+# include <iprt/err.h>
+# include <iprt/assert.h>
+# include <iprt/string.h>
+
+# include <sys/socket.h>
+# if MAC_OS_X_VERSION_MIN_REQUIRED >= 101500 /* The 10.15 SDK has a slightly butchered API deprecation attempt. */
+# pragma clang diagnostic push
+# pragma clang diagnostic ignored "-Wmacro-redefined" /* Each header redefines __NKE_API_DEPRECATED. */
+# pragma clang diagnostic ignored "-Wmissing-declarations" /* Misplaced __NKE_API_DEPRECATED; in kpi_mbuf.h. */
+# include <net/kpi_interface.h>
+# include <sys/kpi_mbuf.h>
+# include <net/if.h>
+# pragma clang diagnostic pop
+# else /* < 10.15 */
+# include <net/kpi_interface.h>
+RT_C_DECLS_BEGIN /* Buggy 10.4 headers, fixed in 10.5. */
+# include <sys/kpi_mbuf.h>
+RT_C_DECLS_END
+# include <net/if.h>
+# endif /* < 10.15 */
+
+
+RT_C_DECLS_BEGIN
+
+# if defined(IN_RING0)
+
+/**
+ * Constructs and submits a dummy packet to ifnet_input().
+ *
+ * This is a workaround for "stuck dock icon" issue. When the first packet goes
+ * through the interface DLIL grabs a reference to the thread that submits the
+ * packet and holds it until the interface is destroyed. By submitting this
+ * dummy we make DLIL grab the thread of a non-GUI process.
+ *
+ * Most of this function was copied from vboxNetFltDarwinMBufFromSG().
+ *
+ * @returns VBox status code.
+ * @param pIfNet The interface that will hold the reference to the calling
+ * thread. We submit dummy as if it was coming from this interface.
+ */
+DECLINLINE(int) VBoxNetSendDummy(ifnet_t pIfNet)
+{
+ int rc = VINF_SUCCESS;
+
+ size_t cbTotal = 50; /* No Ethernet header */
+ mbuf_how_t How = MBUF_WAITOK;
+ mbuf_t pPkt = NULL;
+ errno_t err = mbuf_allocpacket(How, cbTotal, NULL, &pPkt);
+ if (!err)
+ {
+ /* Skip zero sized memory buffers (paranoia). */
+ mbuf_t pCur = pPkt;
+ while (pCur && !mbuf_maxlen(pCur))
+ pCur = mbuf_next(pCur);
+ Assert(pCur);
+
+ /* Set the required packet header attributes. */
+ mbuf_pkthdr_setlen(pPkt, cbTotal);
+ mbuf_pkthdr_setheader(pPkt, mbuf_data(pCur));
+
+ mbuf_setlen(pCur, cbTotal);
+ memset(mbuf_data(pCur), 0, cbTotal);
+
+ mbuf_pkthdr_setrcvif(pPkt, pIfNet); /* will crash without this. */
+
+ err = ifnet_input(pIfNet, pPkt, NULL);
+ if (err)
+ {
+ rc = RTErrConvertFromErrno(err);
+ mbuf_freem(pPkt);
+ }
+ }
+ else
+ rc = RTErrConvertFromErrno(err);
+
+ return rc;
+}
+
+# endif /* IN_RING0 */
+
+RT_C_DECLS_END
+
+#endif /* RT_OS_DARWIN */
+
+#endif /* !VBOX_INCLUDED_SRC_darwin_VBoxNetSend_h */
+
diff --git a/src/VBox/HostDrivers/darwin/loadall.sh b/src/VBox/HostDrivers/darwin/loadall.sh
new file mode 100755
index 00000000..3333afd7
--- /dev/null
+++ b/src/VBox/HostDrivers/darwin/loadall.sh
@@ -0,0 +1,48 @@
+#!/bin/bash
+## @file
+# For development, loads all the host drivers.
+#
+
+#
+# Copyright (C) 2010-2023 Oracle and/or its affiliates.
+#
+# This file is part of VirtualBox base platform packages, as
+# available from https://www.virtualbox.org.
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation, in version 3 of the
+# License.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, see <https://www.gnu.org/licenses>.
+#
+# The contents of this file may alternatively be used under the terms
+# of the Common Development and Distribution License Version 1.0
+# (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+# in the VirtualBox distribution, in which case the provisions of the
+# CDDL are applicable instead of those of the GPL.
+#
+# You may elect to license modified versions of this file under the
+# terms and conditions of either the GPL or the CDDL or both.
+#
+# SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+#
+
+MY_DIR=`dirname "$0"`
+MY_DIR=`cd "${MY_DIR}" && pwd`
+if [ ! -d "${MY_DIR}" ]; then
+ echo "Cannot find ${MY_DIR} or it's not a directory..."
+ exit 1;
+fi
+
+set -e
+"${MY_DIR}/load.sh" "$*"
+"${MY_DIR}/loadnetflt.sh" "$*"
+"${MY_DIR}/loadnetadp.sh" "$*"
+
diff --git a/src/VBox/HostDrivers/freebsd/Makefile b/src/VBox/HostDrivers/freebsd/Makefile
new file mode 100644
index 00000000..215ac52b
--- /dev/null
+++ b/src/VBox/HostDrivers/freebsd/Makefile
@@ -0,0 +1,94 @@
+#
+# Makefile for the VirtualBox FreeBSD Host Drivers.
+#
+
+#
+# Copyright (C) 2008-2023 Oracle and/or its affiliates.
+#
+# This file is part of VirtualBox base platform packages, as
+# available from https://www.virtualbox.org.
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation, in version 3 of the
+# License.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, see <https://www.gnu.org/licenses>.
+#
+# The contents of this file may alternatively be used under the terms
+# of the Common Development and Distribution License Version 1.0
+# (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+# in the VirtualBox distribution, in which case the provisions of the
+# CDDL are applicable instead of those of the GPL.
+#
+# You may elect to license modified versions of this file under the
+# terms and conditions of either the GPL or the CDDL or both.
+#
+# SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+#
+
+KBUILD_VERBOSE =
+
+all:
+ @echo "=== Building 'vboxdrv' module ==="
+ @$(MAKE) KBUILD_VERBOSE=$(KBUILD_VERBOSE) -C vboxdrv
+ @cp vboxdrv/vboxdrv.ko .
+ @echo
+ @if [ -d vboxnetflt ]; then \
+ if [ -f vboxdrv/Module.symvers ]; then \
+ cp vboxdrv/Module.symvers vboxnetflt; \
+ fi; \
+ echo "=== Building 'vboxnetflt' module ==="; \
+ $(MAKE) KBUILD_VERBOSE=$(KBUILD_VERBOSE) -C vboxnetflt; \
+ cp vboxnetflt/vboxnetflt.ko .; \
+ echo; \
+ fi
+ @if [ -d vboxnetadp ]; then \
+ if [ -f vboxdrv/Module.symvers ]; then \
+ cp vboxdrv/Module.symvers vboxnetadp; \
+ fi; \
+ echo "=== Building 'vboxnetadp' module ==="; \
+ $(MAKE) KBUILD_VERBOSE=$(KBUILD_VERBOSE) -C vboxnetadp; \
+ cp vboxnetadp/vboxnetadp.ko .; \
+ echo; \
+ fi
+
+
+install:
+ @$(MAKE) KBUILD_VERBOSE=$(KBUILD_VERBOSE) -C vboxdrv install
+ @if [ -d vboxnetflt ]; then \
+ $(MAKE) KBUILD_VERBOSE=$(KBUILD_VERBOSE) -C vboxnetflt install; \
+ fi
+ @if [ -d vboxnetadp ]; then \
+ $(MAKE) KBUILD_VERBOSE=$(KBUILD_VERBOSE) -C vboxnetadp install; \
+ fi
+
+clean:
+ @$(MAKE) -C vboxdrv clean
+ @if [ -d vboxnetflt ]; then \
+ $(MAKE) -C vboxnetflt clean; \
+ fi
+ @if [ -d vboxnetadp ]; then \
+ $(MAKE) -C vboxnetadp clean; \
+ fi
+ rm -f vboxdrv.ko vboxnetflt.ko vboxnetadp.ko
+
+load:
+ @for module in vboxnetadp vboxnetflt vboxdrv; do \
+ if kldstat -n "$$module" >/dev/null; then \
+ echo "Removing previously installed $$module module"; \
+ /sbin/kldunload $$module; \
+ fi; \
+ done
+ @for module in vboxdrv vboxnetflt vboxnetadp; do \
+ if test -f $$module.ko; then \
+ echo "Installing $$module module"; \
+ /sbin/kldload ./$$module.ko; \
+ fi; \
+ done
diff --git a/src/VBox/HostDrivers/freebsd/Makefile.kmk b/src/VBox/HostDrivers/freebsd/Makefile.kmk
new file mode 100644
index 00000000..9c9bf4d2
--- /dev/null
+++ b/src/VBox/HostDrivers/freebsd/Makefile.kmk
@@ -0,0 +1,45 @@
+# $Id: Makefile.kmk $
+## @file
+# Sub-makefile for the FreeBSD host driver helper scripts.
+#
+
+#
+# Copyright (C) 2006-2023 Oracle and/or its affiliates.
+#
+# This file is part of VirtualBox base platform packages, as
+# available from https://www.virtualbox.org.
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation, in version 3 of the
+# License.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, see <https://www.gnu.org/licenses>.
+#
+# The contents of this file may alternatively be used under the terms
+# of the Common Development and Distribution License Version 1.0
+# (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+# in the VirtualBox distribution, in which case the provisions of the
+# CDDL are applicable instead of those of the GPL.
+#
+# You may elect to license modified versions of this file under the
+# terms and conditions of either the GPL or the CDDL or both.
+#
+# SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+#
+
+SUB_DEPTH = ../../../..
+include $(KBUILD_PATH)/subheader.kmk
+
+INSTALLS += HostDrivers-scripts
+HostDrivers-scripts_INST = bin/src/
+HostDrivers-scripts_SOURCES = Makefile
+
+include $(FILE_KBUILD_SUB_FOOTER)
+
diff --git a/src/VBox/HostDrivers/linux/Makefile b/src/VBox/HostDrivers/linux/Makefile
new file mode 100644
index 00000000..c32cb136
--- /dev/null
+++ b/src/VBox/HostDrivers/linux/Makefile
@@ -0,0 +1,248 @@
+#
+# Makefile for the VirtualBox Linux Host Drivers.
+#
+
+#
+# Copyright (C) 2008-2023 Oracle and/or its affiliates.
+#
+# This file is part of VirtualBox base platform packages, as
+# available from https://www.virtualbox.org.
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation, in version 3 of the
+# License.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, see <https://www.gnu.org/licenses>.
+#
+# The contents of this file may alternatively be used under the terms
+# of the Common Development and Distribution License Version 1.0
+# (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+# in the VirtualBox distribution, in which case the provisions of the
+# CDDL are applicable instead of those of the GPL.
+#
+# You may elect to license modified versions of this file under the
+# terms and conditions of either the GPL or the CDDL or both.
+#
+# SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+#
+
+ifneq ($(KERNELRELEASE),)
+
+# Building from kBuild (make -C <kernel_directory> M=`pwd`),
+# or inside a kernel source tree.
+
+obj-m = vboxdrv/
+ ifneq ($(wildcard $(CURDIR)/vboxnetflt/Makefile),)
+obj-m += vboxnetflt/
+ endif
+ ifneq ($(wildcard $(CURDIR)/vboxnetadp/Makefile),)
+obj-m += vboxnetadp/
+ endif
+ ifneq ($(wildcard $(CURDIR)/vboxpci/Makefile),)
+obj-m += vboxpci/
+ endif
+
+else # ! KERNELRELEASE
+
+# convenience Makefile without KERNELRELEASE
+
+ifndef SUDO
+ ifneq ($(shell id -u),0)
+SUDO := $(firstword $(wildcard /usr/bin/sudo /bin/sudo /usr/sbin/sudo))
+ else
+SUDO :=
+ endif
+endif
+
+KBUILD_VERBOSE ?=
+.PHONY: all install clean check unload load \
+ vboxdrv vboxnetflt vboxnetadp vboxpci \
+ install-vboxdrv install-vboxnetflt install-vboxnetadp install-vboxpci \
+ clean-vboxdrv clean-vboxnetflt clean-vboxnetadp clean-vboxpci
+
+all: vboxdrv vboxnetflt vboxnetadp vboxpci
+
+# We want to build on Linux 2.6.18 and later kernels.
+KERN_VER ?= $(shell uname -r)
+ ifneq ($(filter-out 1.% 2.0.% 2.1.% 2.2.% 2.3.% 2.4.% 2.5.%,$(KERN_VER)),)
+
+vboxdrv:
+ @echo "=== Building 'vboxdrv' module ==="
+ +@$(MAKE) KBUILD_VERBOSE=$(KBUILD_VERBOSE) -C vboxdrv
+ @cp vboxdrv/vboxdrv.ko .
+ @echo
+
+vboxnetflt: vboxdrv
+ +@if [ -d vboxnetflt ]; then \
+ if [ -f vboxdrv/Module.symvers ]; then \
+ cp vboxdrv/Module.symvers vboxnetflt; \
+ fi; \
+ echo "=== Building 'vboxnetflt' module ==="; \
+ $(MAKE) KBUILD_VERBOSE=$(KBUILD_VERBOSE) KBUILD_EXTRA_SYMBOLS=$(abspath vboxnetflt/Module.symvers) -C vboxnetflt || exit 1; \
+ cp vboxnetflt/vboxnetflt.ko .; \
+ echo; \
+ fi
+
+vboxnetadp: vboxdrv
+ +@if [ -d vboxnetadp ]; then \
+ if [ -f vboxdrv/Module.symvers ]; then \
+ cp vboxdrv/Module.symvers vboxnetadp; \
+ fi; \
+ echo "=== Building 'vboxnetadp' module ==="; \
+ $(MAKE) KBUILD_VERBOSE=$(KBUILD_VERBOSE) KBUILD_EXTRA_SYMBOLS=$(abspath vboxnetadp/Module.symvers) -C vboxnetadp || exit 1; \
+ cp vboxnetadp/vboxnetadp.ko .; \
+ echo; \
+ fi
+
+vboxpci: vboxdrv
+ +@if [ -d vboxpci ]; then \
+ if [ -f vboxdrv/Module.symvers ]; then \
+ cp vboxdrv/Module.symvers vboxpci; \
+ fi; \
+ echo "=== Building 'vboxpci' module ==="; \
+ $(MAKE) KBUILD_VERBOSE=$(KBUILD_VERBOSE) KBUILD_EXTRA_SYMBOLS=$(abspath vboxpci/Module.symvers) -C vboxpci || exit 1; \
+ cp vboxpci/vboxpci.ko .; \
+ echo; \
+ fi
+
+install-vboxdrv:
+ +@$(MAKE) KBUILD_VERBOSE=$(KBUILD_VERBOSE) -C vboxdrv install
+
+install-vboxnetflt:
+ +@if [ -d vboxnetflt ]; then \
+ $(MAKE) KBUILD_VERBOSE=$(KBUILD_VERBOSE) -C vboxnetflt install; \
+ fi
+
+install-vboxnetadp:
+ +@if [ -d vboxnetadp ]; then \
+ $(MAKE) KBUILD_VERBOSE=$(KBUILD_VERBOSE) -C vboxnetadp install; \
+ fi
+
+install-vboxpci:
+ +@if [ -d vboxpci ]; then \
+ $(MAKE) KBUILD_VERBOSE=$(KBUILD_VERBOSE) -C vboxpci install; \
+ fi
+
+install: install-vboxdrv install-vboxnetflt install-vboxnetadp install-vboxpci
+
+# Look for wrapper modules, sorting them so vmmr0 is first.
+VBOX_WRAPPER_DIRS := $(notdir $(wildcard $(CURDIR)/vbox_*))
+ ifneq ($(VBOX_WRAPPER_DIRS),)
+VBOX_WRAPPER_DIRS := $(filter vbox_vmmr0,$(VBOX_WRAPPER_DIRS)) $(sort $(filter-out vbox_vmmr0,$(VBOX_WRAPPER_DIRS)))
+ endif
+ define wrapper_template
+$(wrapper): $(subst $(wrapper),,vbox_vmmr0)
+ +$$(MAKE) KBUILD_VERBOSE=$$(KBUILD_VERBOSE) -C $(wrapper)/
+
+load-$(wrapper): $(subst load-$(wrapper),,load-vbox_vmmr0)
+ @if ! grep -q "^$(wrapper) " /proc/modules; then \
+ echo "Loading $(wrapper)..."; \
+ $(SUDO) /sbin/insmod $(wrapper)/$(wrapper).ko; \
+ else \
+ echo "Skipping loading $(wrapper) module (already loaded)."; \
+ fi
+
+unload-$(wrapper):
+ @if grep -q "^$(wrapper) " /proc/modules; then \
+ echo "Unloading $(wrapper)..."; \
+ $(SUDO) /sbin/rmmod $(wrapper)/$(wrapper).ko; \
+ fi
+ endef
+$(foreach wrapper,$(VBOX_WRAPPER_DIRS), $(eval $(wrapper_template)))
+
+wrappers: $(VBOX_WRAPPER_DIRS)
+wrappers-load: $(addprefix load-,$(VBOX_WRAPPER_DIRS))
+wrappers-unload:
+ @for module in $(filter-out vbox_vmmr0,$(VBOX_WRAPPER_DIRS)) $(filter vbox_vmmr0,$(VBOX_WRAPPER_DIRS)); \
+ do \
+ if grep -q "^$${module} " /proc/modules; then \
+ echo "Unloading $${module}..."; \
+ $(SUDO) /sbin/rmmod "$${module}"; \
+ fi \
+ done
+wrappers-reload: wrappers-unload
+ +$(MAKE) -f $(lastword $(MAKEFILE_LIST)) --no-print-directory wrappers-load
+
+buildid:
+ @for module in $(foreach module,vboxdrv vboxnetflt vboxnetadp $(VBOX_WRAPPER_DIRS),$(module)/$(module).ko); \
+ do \
+ buildid=`readelf -n $${module} | sed -ne 's/^.*Build ID: *\([[:xdigit:]][[:xdigit:]]\)\(.*\)$$/\1\/\2/p' `; \
+ if [ -n "$${buildid}" ]; then \
+ mkdir -p ~/.debug/.build-id/`dirname $${buildid}`; \
+ ln -sfn -- "$(CURDIR)/$${module}" ~/.debug/.build-id/$${buildid}; \
+ else \
+ echo "warning: No build ID for $${module}"; \
+ fi \
+ done
+
+.PHONY: wrappers wrappers-load wrappers-unload buildid $(VBOX_WRAPPER_DIRS) \
+ $(addprefix load-,$(VBOX_WRAPPER_DIRS)) $(addprefix unload-,$(VBOX_WRAPPER_DIRS))
+
+ else # Too old:
+
+VBOX_WRAPPER_DIRS :=
+
+vboxdrv:
+vboxnetflt:
+vboxnetadp:
+vboxpci:
+install:
+wrappers:
+wrappers-load:
+wrappers-unload:
+buildid:
+
+ endif
+
+clean-vboxdrv:
+ +@$(MAKE) -C vboxdrv clean
+ rm -rf vboxdrv.ko
+
+clean-vboxnetflt:
+ +@if [ -d vboxnetflt ]; then \
+ $(MAKE) -C vboxnetflt clean; \
+ fi
+ rm -rf vboxnetflt.ko
+
+clean-vboxnetadp:
+ +@if [ -d vboxnetadp ]; then \
+ $(MAKE) -C vboxnetadp clean; \
+ fi
+ rm -rf vboxnetadp.ko
+
+clean-vboxpci:
+ +@if [ -d vboxpci ]; then \
+ $(MAKE) -C vboxpci clean; \
+ fi
+ rm -f vboxpci.ko
+
+clean: clean-vboxdrv clean-vboxnetflt clean-vboxnetadp clean-vboxpci
+
+check:
+ +@$(MAKE) KBUILD_VERBOSE=$(KBUILD_VERBOSE) -C vboxdrv check
+
+unload:
+ @for module in vboxpci vboxnetadp vboxnetflt vboxdrv; do \
+ if grep "^$$module " /proc/modules >/dev/null; then \
+ echo "Removing previously installed $$module module"; \
+ $(SUDO) /sbin/rmmod $$module; \
+ fi; \
+ done
+
+load: unload
+ @for module in vboxdrv vboxnetflt vboxnetadp vboxpci; do \
+ if test -f $$module.ko; then \
+ echo "Installing $$module module"; \
+ $(SUDO) /sbin/insmod $$module.ko; \
+ fi; \
+ done
+
+endif # ! KERNELRELEASE
+
diff --git a/src/VBox/HostDrivers/linux/Makefile.kmk b/src/VBox/HostDrivers/linux/Makefile.kmk
new file mode 100644
index 00000000..3d012c47
--- /dev/null
+++ b/src/VBox/HostDrivers/linux/Makefile.kmk
@@ -0,0 +1,50 @@
+# $Id: Makefile.kmk $
+## @file
+# Sub-makefile for the Linux host driver helper scripts.
+#
+
+#
+# Copyright (C) 2006-2023 Oracle and/or its affiliates.
+#
+# This file is part of VirtualBox base platform packages, as
+# available from https://www.virtualbox.org.
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation, in version 3 of the
+# License.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, see <https://www.gnu.org/licenses>.
+#
+# The contents of this file may alternatively be used under the terms
+# of the Common Development and Distribution License Version 1.0
+# (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+# in the VirtualBox distribution, in which case the provisions of the
+# CDDL are applicable instead of those of the GPL.
+#
+# You may elect to license modified versions of this file under the
+# terms and conditions of either the GPL or the CDDL or both.
+#
+# SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+#
+
+SUB_DEPTH = ../../../..
+include $(KBUILD_PATH)/subheader.kmk
+
+INSTALLS += HostDrivers-scripts
+HostDrivers-scripts_INST = $(INST_DIST)
+HostDrivers-scripts_SOURCES = \
+ Makefile=>src/Makefile
+HostDrivers-scripts_EXEC_SOURCES = \
+ loadall.sh \
+ load.sh \
+ build_in_tmp=>src/build_in_tmp
+
+include $(FILE_KBUILD_SUB_FOOTER)
+
diff --git a/src/VBox/HostDrivers/linux/build_in_tmp b/src/VBox/HostDrivers/linux/build_in_tmp
new file mode 100755
index 00000000..e70c9b04
--- /dev/null
+++ b/src/VBox/HostDrivers/linux/build_in_tmp
@@ -0,0 +1,104 @@
+#!/bin/sh
+# $Id: build_in_tmp $
+## @file
+# Script to build a kernel module in /tmp.
+#
+# Useful if the module sources are installed in read-only directory.
+#
+
+#
+# Copyright (C) 2007-2023 Oracle and/or its affiliates.
+#
+# This file is part of VirtualBox base platform packages, as
+# available from https://www.virtualbox.org.
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation, in version 3 of the
+# License.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, see <https://www.gnu.org/licenses>.
+#
+# The contents of this file may alternatively be used under the terms
+# of the Common Development and Distribution License Version 1.0
+# (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+# in the VirtualBox distribution, in which case the provisions of the
+# CDDL are applicable instead of those of the GPL.
+#
+# You may elect to license modified versions of this file under the
+# terms and conditions of either the GPL or the CDDL or both.
+#
+# SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+#
+
+# find a unique temp directory
+num=0
+while true; do
+ tmpdir="/tmp/vbox.$num"
+ if mkdir -m 0755 "$tmpdir" 2> /dev/null; then
+ break
+ fi
+ num=`expr $num + 1`
+ if [ $num -gt 200 ]; then
+ echo "Could not find a valid tmp directory"
+ exit 1
+ fi
+done
+
+# Guest optimal number of make jobs.
+MAKE_JOBS=`grep vendor_id /proc/cpuinfo | wc -l`
+if [ "${MAKE_JOBS}" -le "0" ]; then MAKE_JOBS=1; fi
+
+# Parse our arguments, anything we don't grok is for make.
+while true; do
+ if [ "$1" = "--save-module-symvers" ]; then
+ shift
+ SAVE_MOD_SYMVERS="$1"
+ shift
+ elif [ "$1" = "--use-module-symvers" ]; then
+ shift
+ USE_MOD_SYMVERS="$1"
+ shift
+ elif [ "$1" = "--module-source" ]; then
+ shift
+ MODULE_SOURCE="$1"
+ shift
+ else
+ break
+ fi
+done
+
+# copy
+if [ -n "$MODULE_SOURCE" ]; then
+ cp -a "$MODULE_SOURCE"/* $tmpdir/
+else
+ cp -a ${0%/*}/* $tmpdir/
+fi
+if [ -n "$USE_MOD_SYMVERS" ]; then
+ cp $USE_MOD_SYMVERS $tmpdir/Module.symvers
+ MAKE_EXTRAOPTS="KBUILD_EXTRA_SYMBOLS=$tmpdir/Module.symvers"
+fi
+
+# make, cleanup if success
+cd "$tmpdir"
+if make CONFIG_MODULE_COMPRESS_GZIP= CONFIG_MODULE_COMPRESS_XZ= CONFIG_MODULE_COMPRESS_ZSTD= "-j`echo ${MAKE_JOBS}`" "$@" ${MAKE_EXTRAOPTS}; then # strip leading space from "MAKE_JOBS"
+ if [ -n "$SAVE_MOD_SYMVERS" ]; then
+ if [ -f Module.symvers ]; then
+ cp -f Module.symvers $SAVE_MOD_SYMVERS
+ else
+ cat /dev/null > $SAVE_MOD_SYMVERS
+ fi
+ fi
+ rm -rf $tmpdir
+ exit 0
+fi
+
+# failure
+rm -rf $tmpdir
+exit 1
diff --git a/src/VBox/HostDrivers/linux/export_modules.sh b/src/VBox/HostDrivers/linux/export_modules.sh
new file mode 100755
index 00000000..11c49d47
--- /dev/null
+++ b/src/VBox/HostDrivers/linux/export_modules.sh
@@ -0,0 +1,258 @@
+#!/bin/sh
+# $Id$
+## @file
+# Create a tar archive containing the sources of the vboxdrv kernel module.
+#
+
+#
+# Copyright (C) 2007-2023 Oracle and/or its affiliates.
+#
+# This file is part of VirtualBox base platform packages, as
+# available from https://www.virtualbox.org.
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation, in version 3 of the
+# License.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, see <https://www.gnu.org/licenses>.
+#
+# The contents of this file may alternatively be used under the terms
+# of the Common Development and Distribution License Version 1.0
+# (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+# in the VirtualBox distribution, in which case the provisions of the
+# CDDL are applicable instead of those of the GPL.
+#
+# You may elect to license modified versions of this file under the
+# terms and conditions of either the GPL or the CDDL or both.
+#
+# SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+#
+
+# The below is GNU-specific. See VBox.sh for the longer Solaris/OS X version.
+TARGET=`readlink -e -- "${0}"` || exit 1
+MY_DIR="${TARGET%/[!/]*}"
+
+# What this script does:
+usage_msg="\
+Usage: `basename ${0}` --file <path>|--folder <path> \
+ [--override-svn-rev <rev>] [--extra-version-string <string>] [--without-hardening]
+
+Exports the VirtualBox host kernel modules to the tar.gz archive or folder in \
+<path>, optionally adjusting the Make files to build them without hardening.
+
+Examples:
+ `basename ${0}` --file /tmp/vboxhost.tar.gz
+ `basename ${0}` --folder /tmp/tmpdir --without-hardening"
+
+usage()
+{
+ case "${1}" in
+ 0)
+ echo "${usage_msg}" | fold -s -w80 ;;
+ *)
+ echo "${usage_msg}" | fold -s -w80 >&2 ;;
+ esac
+ exit "${1}"
+}
+
+fail()
+{
+ echo "${1}" | fold -s -w80 >&2
+ exit 1
+}
+
+unset FILE FOLDER
+VBOX_WITH_HARDENING=1
+while test -n "${1}"; do
+ case "${1}" in
+ --file)
+ FILE="${2}"
+ shift 2 ;;
+ --folder)
+ FOLDER="${2}"
+ shift 2 ;;
+ --override-svn-rev)
+ OVERRIDE_SVN_REV="${2}"
+ shift 2 ;;
+ --extra-version-string)
+ EXTRA_VERSION_STRING="${2}"
+ shift 2 ;;
+ --without-hardening)
+ unset VBOX_WITH_HARDENING
+ shift ;;
+ -h|--help)
+ usage 0 ;;
+ *)
+ echo "Unknown parameter ${1}" >&2
+ usage 1 ;;
+ esac
+done
+test -z "$FILE" || test -z "$FOLDER" ||
+ fail "Only one of --file and --folder may be used"
+test -n "$FILE" || test -n "$FOLDER" || usage 1
+
+if test -n "$FOLDER"; then
+ PATH_TMP="$FOLDER"
+else
+ PATH_TMP="`cd \`dirname $FILE\`; pwd`/.vbox_modules"
+ FILE_OUT="`cd \`dirname $FILE\`; pwd`/`basename $FILE`"
+fi
+PATH_OUT=$PATH_TMP
+PATH_ROOT="`cd ${MY_DIR}/../../../..; pwd`"
+PATH_LOG=/tmp/vbox-export-host.log
+PATH_LINUX="$PATH_ROOT/src/VBox/HostDrivers/linux"
+PATH_VBOXDRV="$PATH_ROOT/src/VBox/HostDrivers/Support"
+PATH_VBOXNET="$PATH_ROOT/src/VBox/HostDrivers/VBoxNetFlt"
+PATH_VBOXADP="$PATH_ROOT/src/VBox/HostDrivers/VBoxNetAdp"
+PATH_VBOXPCI="$PATH_ROOT/src/VBox/HostDrivers/VBoxPci"
+
+VBOX_VERSION_MAJOR=`sed -e "s/^ *VBOX_VERSION_MAJOR *= \+\([0-9]\+\)/\1/;t;d" $PATH_ROOT/Version.kmk`
+VBOX_VERSION_MINOR=`sed -e "s/^ *VBOX_VERSION_MINOR *= \+\([0-9]\+\)/\1/;t;d" $PATH_ROOT/Version.kmk`
+VBOX_VERSION_BUILD=`sed -e "s/^ *VBOX_VERSION_BUILD *= \+\([0-9]\+\)/\1/;t;d" $PATH_ROOT/Version.kmk`
+VBOX_VERSION_STRING=$VBOX_VERSION_MAJOR.$VBOX_VERSION_MINOR.$VBOX_VERSION_BUILD
+VBOX_VERSION_BUILD=`sed -e "s/^ *VBOX_VERSION_BUILD *= \+\([0-9]\+\)/\1/;t;d" $PATH_ROOT/Version.kmk`
+VBOX_SVN_CONFIG_REV=`sed -e 's/^ *VBOX_SVN_REV_CONFIG_FALLBACK *:= \+\$(patsubst *%:,, *\$Rev: *\([0-9]\+\) *\$ *) */\1/;t;d' $PATH_ROOT/Config.kmk`
+VBOX_SVN_VERSION_REV=`sed -e 's/^ *VBOX_SVN_REV_VERSION_FALLBACK *:= \+\$(patsubst *%:,, *\$Rev: *\([0-9]\+\) *\$ *) */\1/;t;d' $PATH_ROOT/Version.kmk`
+
+if [ -n "$OVERRIDE_SVN_REV" ]; then
+ VBOX_SVN_REV=$OVERRIDE_SVN_REV
+elif [ "$VBOX_SVN_CONFIG_REV" -gt "$VBOX_SVN_VERSION_REV" ]; then
+ VBOX_SVN_REV=$VBOX_SVN_CONFIG_REV
+else
+ VBOX_SVN_REV=$VBOX_SVN_VERSION_REV
+fi
+VBOX_VENDOR=`sed -e 's/^ *VBOX_VENDOR *= \+\(.\+\)/\1/;t;d' $PATH_ROOT/Config.kmk`
+VBOX_VENDOR_SHORT=`sed -e 's/^ *VBOX_VENDOR_SHORT *= \+\(.\+\)/\1/;t;d' $PATH_ROOT/Config.kmk`
+VBOX_PRODUCT=`sed -e 's/^ *VBOX_PRODUCT *= \+\(.\+\)/\1/;t;d' $PATH_ROOT/Config.kmk`
+VBOX_C_YEAR=`date +%Y`
+VBOX_WITH_PCI_PASSTHROUGH=`sed -e '/^ *VBOX_WITH_PCI_PASSTHROUGH *[:]\?= */!d' -e 's/ *#.*$//' -e 's/^.*= *//' $PATH_ROOT/Config.kmk`
+
+. $PATH_VBOXDRV/linux/files_vboxdrv
+. $PATH_VBOXNET/linux/files_vboxnetflt
+. $PATH_VBOXADP/linux/files_vboxnetadp
+if [ "${VBOX_WITH_PCI_PASSTHROUGH}" = "1" ]; then
+ . $PATH_VBOXPCI/linux/files_vboxpci
+fi
+
+# Temporary path for creating the modules, will be removed later
+rm -rf "$PATH_TMP"
+mkdir $PATH_TMP || exit 1
+
+# Create auto-generated version file, needed by all modules
+echo "#ifndef ___version_generated_h___" > $PATH_TMP/version-generated.h
+echo "#define ___version_generated_h___" >> $PATH_TMP/version-generated.h
+echo "" >> $PATH_TMP/version-generated.h
+echo "#define VBOX_VERSION_MAJOR $VBOX_VERSION_MAJOR" >> $PATH_TMP/version-generated.h
+echo "#define VBOX_VERSION_MINOR $VBOX_VERSION_MINOR" >> $PATH_TMP/version-generated.h
+echo "#define VBOX_VERSION_BUILD $VBOX_VERSION_BUILD" >> $PATH_TMP/version-generated.h
+echo "#define VBOX_VERSION_STRING_RAW \"$VBOX_VERSION_MAJOR.$VBOX_VERSION_MINOR.$VBOX_VERSION_BUILD\"" >> $PATH_TMP/version-generated.h
+echo "#define VBOX_VERSION_STRING \"$VBOX_VERSION_STRING\"" >> $PATH_TMP/version-generated.h
+echo "#define VBOX_API_VERSION_STRING \"${VBOX_VERSION_MAJOR}_${VBOX_VERSION_MINOR}\"" >> $PATH_TMP/version-generated.h
+[ -n "$EXTRA_VERSION_STRING" ] && echo "#define VBOX_EXTRA_VERSION_STRING \" ${EXTRA_VERSION_STRING}\"" >> $PATH_TMP/version-generated.h
+echo "#define VBOX_PRIVATE_BUILD_DESC \"Private build with export_modules\"" >> $PATH_TMP/version-generated.h
+echo "" >> $PATH_TMP/version-generated.h
+echo "#endif" >> $PATH_TMP/version-generated.h
+
+# Create auto-generated revision file, needed by all modules
+echo "#ifndef __revision_generated_h__" > $PATH_TMP/revision-generated.h
+echo "#define __revision_generated_h__" >> $PATH_TMP/revision-generated.h
+echo "" >> $PATH_TMP/revision-generated.h
+echo "#define VBOX_SVN_REV $VBOX_SVN_REV" >> $PATH_TMP/revision-generated.h
+echo "" >> $PATH_TMP/revision-generated.h
+echo "#endif" >> $PATH_TMP/revision-generated.h
+
+# Create auto-generated product file, needed by all modules
+echo "#ifndef ___product_generated_h___" > $PATH_TMP/product-generated.h
+echo "#define ___product_generated_h___" >> $PATH_TMP/product-generated.h
+echo "" >> $PATH_TMP/product-generated.h
+echo "#define VBOX_VENDOR \"$VBOX_VENDOR\"" >> $PATH_TMP/product-generated.h
+echo "#define VBOX_VENDOR_SHORT \"$VBOX_VENDOR_SHORT\"" >> $PATH_TMP/product-generated.h
+echo "" >> $PATH_TMP/product-generated.h
+echo "#define VBOX_PRODUCT \"$VBOX_PRODUCT\"" >> $PATH_TMP/product-generated.h
+echo "#define VBOX_C_YEAR \"$VBOX_C_YEAR\"" >> $PATH_TMP/product-generated.h
+echo "" >> $PATH_TMP/product-generated.h
+echo "#endif" >> $PATH_TMP/product-generated.h
+
+# vboxdrv (VirtualBox host kernel module)
+mkdir $PATH_TMP/vboxdrv || exit 1
+for f in $FILES_VBOXDRV_NOBIN; do
+ install -D -m 0644 `echo $f|cut -d'=' -f1` "$PATH_TMP/vboxdrv/`echo $f|cut -d'>' -f2`"
+done
+for f in $FILES_VBOXDRV_BIN; do
+ install -D -m 0755 `echo $f|cut -d'=' -f1` "$PATH_TMP/vboxdrv/`echo $f|cut -d'>' -f2`"
+done
+if [ -n "$VBOX_WITH_HARDENING" ]; then
+ sed -e "s;VBOX_WITH_EFLAGS_AC_SET_IN_VBOXDRV;;g" \
+ -e "s;IPRT_WITH_EFLAGS_AC_PRESERVING;;g" \
+ < $PATH_VBOXDRV/linux/Makefile > $PATH_TMP/vboxdrv/Makefile
+else
+ sed -e "s;VBOX_WITH_HARDENING;;g" \
+ -e "s;VBOX_WITH_EFLAGS_AC_SET_IN_VBOXDRV;;g" \
+ -e "s;IPRT_WITH_EFLAGS_AC_PRESERVING;;g" \
+ < $PATH_VBOXDRV/linux/Makefile > $PATH_TMP/vboxdrv/Makefile
+fi
+
+# vboxnetflt (VirtualBox netfilter kernel module)
+mkdir $PATH_TMP/vboxnetflt || exit 1
+for f in $VBOX_VBOXNETFLT_SOURCES; do
+ install -D -m 0644 `echo $f|cut -d'=' -f1` "$PATH_TMP/vboxnetflt/`echo $f|cut -d'>' -f2`"
+done
+if [ -n "$VBOX_WITH_HARDENING" ]; then
+ cat $PATH_VBOXNET/linux/Makefile > $PATH_TMP/vboxnetflt/Makefile
+else
+ sed -e "s;VBOX_WITH_HARDENING;;g" < $PATH_VBOXNET/linux/Makefile > $PATH_TMP/vboxnetflt/Makefile
+fi
+
+# vboxnetadp (VirtualBox network adapter kernel module)
+mkdir $PATH_TMP/vboxnetadp || exit 1
+for f in $VBOX_VBOXNETADP_SOURCES; do
+ install -D -m 0644 `echo $f|cut -d'=' -f1` "$PATH_TMP/vboxnetadp/`echo $f|cut -d'>' -f2`"
+done
+if [ -n "$VBOX_WITH_HARDENING" ]; then
+ cat $PATH_VBOXADP/linux/Makefile > $PATH_TMP/vboxnetadp/Makefile
+else
+ sed -e "s;VBOX_WITH_HARDENING;;g" < $PATH_VBOXADP/linux/Makefile > $PATH_TMP/vboxnetadp/Makefile
+fi
+
+# vboxpci (VirtualBox host PCI access kernel module)
+if [ "${VBOX_WITH_PCI_PASSTHROUGH}" = "1" ]; then
+ mkdir $PATH_TMP/vboxpci || exit 1
+ for f in $VBOX_VBOXPCI_SOURCES; do
+ install -D -m 0644 `echo $f|cut -d'=' -f1` "$PATH_TMP/vboxpci/`echo $f|cut -d'>' -f2`"
+ done
+ if [ -n "$VBOX_WITH_HARDENING" ]; then
+ cat $PATH_VBOXPCI/linux/Makefile > $PATH_TMP/vboxpci/Makefile
+ else
+ sed -e "s;VBOX_WITH_HARDENING;;g" < $PATH_VBOXPCI/linux/Makefile > $PATH_TMP/vboxpci/Makefile
+ fi
+fi
+
+install -D -m 0644 $PATH_LINUX/Makefile $PATH_TMP/Makefile
+install -D -m 0755 $PATH_LINUX/build_in_tmp $PATH_TMP/build_in_tmp
+
+# Only temporary, omit from archive
+rm $PATH_TMP/version-generated.h
+rm $PATH_TMP/revision-generated.h
+rm $PATH_TMP/product-generated.h
+
+# If we are exporting to a folder then stop now.
+test -z "$FOLDER" || exit 0
+
+# Do a test build
+echo Doing a test build, this may take a while.
+make -C $PATH_TMP > $PATH_LOG 2>&1 &&
+ make -C $PATH_TMP clean >> $PATH_LOG 2>&1 ||
+ echo "Warning: test build failed. Please check $PATH_LOG"
+
+# Create the archive
+tar -czf $FILE_OUT -C $PATH_TMP . || exit 1
+
+# Remove the temporary directory
+rm -r $PATH_TMP
diff --git a/src/VBox/HostDrivers/linux/load.sh b/src/VBox/HostDrivers/linux/load.sh
new file mode 100755
index 00000000..866951c4
--- /dev/null
+++ b/src/VBox/HostDrivers/linux/load.sh
@@ -0,0 +1,65 @@
+#!/bin/bash
+## @file
+# For development, builds and loads all the host drivers.
+#
+
+#
+# Copyright (C) 2010-2023 Oracle and/or its affiliates.
+#
+# This file is part of VirtualBox base platform packages, as
+# available from https://www.virtualbox.org.
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation, in version 3 of the
+# License.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, see <https://www.gnu.org/licenses>.
+#
+# The contents of this file may alternatively be used under the terms
+# of the Common Development and Distribution License Version 1.0
+# (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+# in the VirtualBox distribution, in which case the provisions of the
+# CDDL are applicable instead of those of the GPL.
+#
+# You may elect to license modified versions of this file under the
+# terms and conditions of either the GPL or the CDDL or both.
+#
+# SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+#
+
+TARGET=`readlink -e -- "${0}"` || exit 1 # The GNU-specific way.
+MY_DIR="${TARGET%/[!/]*}"
+
+set -e
+
+# Parse parameters.
+OPT_UNLOAD_ONLY=
+if [ ${#} -ge 1 -a "${1}" = "-u" ]; then
+ OPT_UNLOAD_ONLY=yes
+ shift
+fi
+if [ ${#} -ge 1 -a '(' "${1}" = "-h" -o "${1}" = "--help" ')' ]; then
+ echo "usage: load.sh [-u] [make arguments]"
+ exit 1
+fi
+
+# Unload, but keep the udev rules.
+sudo "${MY_DIR}/vboxdrv.sh" stop
+
+if [ -z "${OPT_UNLOAD_ONLY}" ]; then
+ # Build and load.
+ MAKE_JOBS=`grep vendor_id /proc/cpuinfo | wc -l`
+ if [ "${MAKE_JOBS}" -le "0" ]; then MAKE_JOBS=1; fi
+ make "-j${MAKE_JOBS}" -C "${MY_DIR}/src/vboxdrv" "$@"
+
+ echo "Installing SUPDrv (aka VBoxDrv/vboxdrv)"
+ sudo /sbin/insmod "${MY_DIR}/src/vboxdrv/vboxdrv.ko"
+fi
+
diff --git a/src/VBox/HostDrivers/linux/loadall.sh b/src/VBox/HostDrivers/linux/loadall.sh
new file mode 100755
index 00000000..7d7d80a0
--- /dev/null
+++ b/src/VBox/HostDrivers/linux/loadall.sh
@@ -0,0 +1,85 @@
+#!/bin/bash
+## @file
+# For development, builds and loads all the host drivers.
+#
+
+#
+# Copyright (C) 2010-2023 Oracle and/or its affiliates.
+#
+# This file is part of VirtualBox base platform packages, as
+# available from https://www.virtualbox.org.
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation, in version 3 of the
+# License.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, see <https://www.gnu.org/licenses>.
+#
+# The contents of this file may alternatively be used under the terms
+# of the Common Development and Distribution License Version 1.0
+# (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+# in the VirtualBox distribution, in which case the provisions of the
+# CDDL are applicable instead of those of the GPL.
+#
+# You may elect to license modified versions of this file under the
+# terms and conditions of either the GPL or the CDDL or both.
+#
+# SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+#
+
+TARGET=`readlink -e -- "${0}"` || exit 1 # The GNU-specific way.
+MY_DIR="${TARGET%/[!/]*}"
+MY_GROUPNAME="vboxusers"
+
+#
+# vboxusers membership check.
+#
+if ! (id -Gn | grep -w ${MY_GROUPNAME}); then
+ echo "loadall.sh: you're not a member of vboxusers...";
+ # Create the group.
+ if ! getent group ${MY_GROUPNAME}; then
+ set -e
+ sudo groupadd -r -f ${MY_GROUPNAME}
+ set +e
+ fi
+ # Add ourselves.
+ MY_USER=`id -un`
+ if [ -z "${MY_USER}" ]; then echo "loadall.sh: cannot figure user name"; exit 1; fi
+ sudo usermod -a -G ${MY_GROUPNAME} "${MY_USER}";
+
+ # Require relogon.
+ echo "loadall.sh: You must log on again to load the new group membership."
+ exit 1
+fi
+
+#
+# Normal action.
+#
+if [ ${#} -eq 0 ]; then
+ if [ -f "${MY_DIR}/src/vboxdrv/vboxdrv.ko" ]; then
+ echo "Cleaning build folder."
+ make -C "${MY_DIR}/src" clean > /dev/null
+ fi
+ sudo "${MY_DIR}/vboxdrv.sh" setup
+
+#
+# Unload and clean up when '-u' is given.
+#
+elif [ ${#} -eq 1 -a "x${1}" = x-u ]; then
+ sudo "${MY_DIR}/vboxdrv.sh" cleanup
+
+#
+# Invalid syntax.
+#
+else
+ echo "Usage: loadall.sh [-u]"
+ exit 1
+fi
+
diff --git a/src/VBox/HostDrivers/win/Makefile.kmk b/src/VBox/HostDrivers/win/Makefile.kmk
new file mode 100644
index 00000000..ed68e39a
--- /dev/null
+++ b/src/VBox/HostDrivers/win/Makefile.kmk
@@ -0,0 +1,66 @@
+# $Id: Makefile.kmk $
+## @file
+# Sub-Makefile for Windows driver tooling lib.
+#
+
+#
+# Copyright (C) 2011-2023 Oracle and/or its affiliates.
+#
+# This file is part of VirtualBox base platform packages, as
+# available from https://www.virtualbox.org.
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation, in version 3 of the
+# License.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, see <https://www.gnu.org/licenses>.
+#
+# The contents of this file may alternatively be used under the terms
+# of the Common Development and Distribution License Version 1.0
+# (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+# in the VirtualBox distribution, in which case the provisions of the
+# CDDL are applicable instead of those of the GPL.
+#
+# You may elect to license modified versions of this file under the
+# terms and conditions of either the GPL or the CDDL or both.
+#
+# SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+#
+
+SUB_DEPTH = ../../../..
+include $(KBUILD_PATH)/subheader.kmk
+
+# For the installer helper DLL.
+LIBRARIES += VBoxDrvCfgSharedStatic
+VBoxDrvCfgSharedStatic_TEMPLATE := VBoxR3StaticDllNoAsan
+VBoxDrvCfgSharedStatic_SDKS = ReorderCompilerIncs $(VBOX_WINPSDK) $(VBOX_WINDDK)
+VBoxDrvCfgSharedStatic_DEFS := _WIN32_WINNT=0x0501 _UNICODE UNICODE
+VBoxDrvCfgSharedStatic_SOURCES := cfg/VBoxDrvCfg.cpp
+
+# For the driver installer/uninstaller executables.
+LIBRARIES += VBoxDrvCfgExe
+VBoxDrvCfgExe_TEMPLATE := VBoxR3Exe
+VBoxDrvCfgExe_EXTENDS := VBoxDrvCfgSharedStatic
+
+# For the regular VBox DLLs.
+LIBRARIES += VBoxDrvCfgDll
+VBoxDrvCfgDll_TEMPLATE := VBoxR3Dll
+VBoxDrvCfgDll_EXTENDS := VBoxDrvCfgSharedStatic
+
+
+INSTALLS += HostDrivers-scripts
+HostDrivers-scripts_INST = $(INST_DIST)
+HostDrivers-scripts_EXEC_SOURCES = \
+ loadall.cmd \
+ load.cmd
+
+# generate rules
+include $(FILE_KBUILD_SUB_FOOTER)
+
diff --git a/src/VBox/HostDrivers/win/VBoxDbgLog.h b/src/VBox/HostDrivers/win/VBoxDbgLog.h
new file mode 100644
index 00000000..2a299e91
--- /dev/null
+++ b/src/VBox/HostDrivers/win/VBoxDbgLog.h
@@ -0,0 +1,150 @@
+/* $Id: VBoxDbgLog.h $ */
+/** @file
+ * Logging helper
+ */
+
+/*
+ * Copyright (C) 2011-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+#ifndef VBOX_INCLUDED_SRC_win_VBoxDbgLog_h
+#define VBOX_INCLUDED_SRC_win_VBoxDbgLog_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+#ifndef VBOX_DBG_LOG_NAME
+# error VBOX_DBG_LOG_NAME should be defined!
+#endif
+
+/* Uncomment to show file/line info in the log */
+/*#define VBOX_DBG_LOG_SHOWLINEINFO*/
+
+#define VBOX_DBG_LOG_PREFIX_FMT VBOX_DBG_LOG_NAME"::"LOG_FN_FMT": "
+#define VBOX_DBG_LOG_PREFIX_PARMS __PRETTY_FUNCTION__
+
+#ifdef VBOX_DBG_LOG_SHOWLINEINFO
+# define VBOX_DBG_LOG_SUFFIX_FMT " (%s:%d)\n"
+# define VBOX_DBG_LOG_SUFFIX_PARMS ,__FILE__, __LINE__
+#else
+# define VBOX_DBG_LOG_SUFFIX_FMT "\n"
+# define VBOX_DBG_LOG_SUFFIX_PARMS
+#endif
+
+#ifdef DEBUG_misha
+# define BP_WARN() AssertFailed()
+#else
+# define BP_WARN() do { } while (0)
+#endif
+
+#define _LOGMSG_EXACT(_logger, _a) \
+ do \
+ { \
+ _logger(_a); \
+ } while (0)
+
+#define _LOGMSG(_logger, _a) \
+ do \
+ { \
+ _logger((VBOX_DBG_LOG_PREFIX_FMT, VBOX_DBG_LOG_PREFIX_PARMS)); \
+ _logger(_a); \
+ _logger((VBOX_DBG_LOG_SUFFIX_FMT VBOX_DBG_LOG_SUFFIX_PARMS)); \
+ } while (0)
+
+/* we can not print paged strings to RT logger, do it this way */
+#define _LOGMSG_STR(_logger, _a, _f) do {\
+ int _i = 0; \
+ _logger(("\"")); \
+ for (;(_a)[_i];++_i) { \
+ _logger(("%"_f, (_a)[_i])); \
+ }\
+ _logger(("\"\n")); \
+ } while (0)
+
+#define _LOGMSG_USTR(_logger, _a) do {\
+ int _i = 0; \
+ _logger(("\"")); \
+ for (;_i<(_a)->Length/2;++_i) { \
+ _logger(("%c", (_a)->Buffer[_i])); \
+ }\
+ _logger(("\"\n")); \
+ } while (0)
+
+#define WARN_NOBP(_a) \
+ do \
+ { \
+ Log((VBOX_DBG_LOG_PREFIX_FMT"WARNING! ", VBOX_DBG_LOG_PREFIX_PARMS)); \
+ Log(_a); \
+ Log((VBOX_DBG_LOG_SUFFIX_FMT VBOX_DBG_LOG_SUFFIX_PARMS)); \
+ } while (0)
+
+#define WARN(_a) \
+ do \
+ { \
+ WARN_NOBP(_a); \
+ BP_WARN(); \
+ } while (0)
+
+#define ASSERT_WARN(_a, _w) do {\
+ if(!(_a)) { \
+ WARN(_w); \
+ }\
+ } while (0)
+
+#define LOG(_a) _LOGMSG(Log, _a)
+#define LOGREL(_a) _LOGMSG(LogRel, _a)
+#define LOGF(_a) _LOGMSG(LogFlow, _a)
+#define LOGF_ENTER() LOGF(("ENTER"))
+#define LOGF_LEAVE() LOGF(("LEAVE"))
+#define LOG_EXACT(_a) _LOGMSG_EXACT(Log, _a)
+#define LOGREL_EXACT(_a) _LOGMSG_EXACT(LogRel, _a)
+/* we can not print paged strings to RT logger, do it this way */
+#define LOG_STRA(_a) do {\
+ _LOGMSG_STR(Log, _a, "c"); \
+ } while (0)
+#define LOG_STRW(_a) do {\
+ _LOGMSG_STR(Log, _a, "c"); \
+ } while (0)
+#define LOG_USTR(_a) do {\
+ _LOGMSG_USTR(Log, _a); \
+ } while (0)
+#define LOGREL_STRA(_a) do {\
+ _LOGMSG_STR(LogRel, _a, "c"); \
+ } while (0)
+#define LOGREL_STRW(_a) do {\
+ _LOGMSG_STR(LogRel, _a, "c"); \
+ } while (0)
+#define LOGREL_USTR(_a) do {\
+ _LOGMSG_USTR(LogRel, _a); \
+ } while (0)
+
+
+#endif /* !VBOX_INCLUDED_SRC_win_VBoxDbgLog_h */
+
diff --git a/src/VBox/HostDrivers/win/cfg/Makefile.kup b/src/VBox/HostDrivers/win/cfg/Makefile.kup
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/src/VBox/HostDrivers/win/cfg/Makefile.kup
diff --git a/src/VBox/HostDrivers/win/cfg/VBoxDrvCfg.cpp b/src/VBox/HostDrivers/win/cfg/VBoxDrvCfg.cpp
new file mode 100644
index 00000000..8a3d9764
--- /dev/null
+++ b/src/VBox/HostDrivers/win/cfg/VBoxDrvCfg.cpp
@@ -0,0 +1,1006 @@
+/* $Id: VBoxDrvCfg.cpp $ */
+/** @file
+ * VBoxDrvCfg.cpp - Windows Driver Manipulation API implementation.
+ *
+ * @note This is EXTREMELY BADLY documented code. Please help improve by
+ * adding comments whenever you've got a chance!
+ */
+
+/*
+ * Copyright (C) 2011-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include <VBox/VBoxDrvCfg-win.h>
+
+#include <iprt/win/setupapi.h>
+#include <iprt/win/shlobj.h>
+#include <Newdev.h>
+
+#include <iprt/alloca.h>
+#include <iprt/mem.h>
+#include <iprt/path.h>
+#include <iprt/string.h>
+#include <iprt/utf16.h>
+
+
+/*********************************************************************************************************************************
+* Global Variables *
+*********************************************************************************************************************************/
+static PFNVBOXDRVCFGLOG g_pfnVBoxDrvCfgLog;
+static void *g_pvVBoxDrvCfgLog;
+
+static PFNVBOXDRVCFGPANIC g_pfnVBoxDrvCfgPanic;
+static void *g_pvVBoxDrvCfgPanic;
+
+
+VBOXDRVCFG_DECL(void) VBoxDrvCfgLoggerSet(PFNVBOXDRVCFGLOG pfnLog, void *pvLog)
+{
+ g_pfnVBoxDrvCfgLog = pfnLog;
+ g_pvVBoxDrvCfgLog = pvLog;
+}
+
+VBOXDRVCFG_DECL(void) VBoxDrvCfgPanicSet(PFNVBOXDRVCFGPANIC pfnPanic, void *pvPanic)
+{
+ g_pfnVBoxDrvCfgPanic = pfnPanic;
+ g_pvVBoxDrvCfgPanic = pvPanic;
+}
+
+static void vboxDrvCfgLogRel(const char *pszFormat, ...)
+{
+ PFNVBOXDRVCFGLOG pfnLog = g_pfnVBoxDrvCfgLog;
+ void *pvLog = g_pvVBoxDrvCfgLog;
+ if (pfnLog)
+ {
+ char szBuffer[4096];
+ va_list va;
+ va_start(va, pszFormat);
+ RTStrPrintfV(szBuffer, RT_ELEMENTS(szBuffer), pszFormat, va);
+ va_end(va);
+ pfnLog(VBOXDRVCFG_LOG_SEVERITY_REL, szBuffer, pvLog);
+ }
+}
+
+static void vboxDrvCfgLogRegular(const char *pszFormat, ...)
+{
+ PFNVBOXDRVCFGLOG pfnLog = g_pfnVBoxDrvCfgLog;
+ void *pvLog = g_pvVBoxDrvCfgLog;
+ if (pfnLog)
+ {
+ char szBuffer[4096];
+ va_list va;
+ va_start(va, pszFormat);
+ RTStrPrintfV(szBuffer, RT_ELEMENTS(szBuffer), pszFormat, va);
+ va_end(va);
+ pfnLog(VBOXDRVCFG_LOG_SEVERITY_REGULAR, szBuffer, pvLog);
+ }
+}
+
+static void vboxDrvCfgLogFlow(const char *pszFormat, ...)
+{
+ PFNVBOXDRVCFGLOG pfnLog = g_pfnVBoxDrvCfgLog;
+ void *pvLog = g_pvVBoxDrvCfgLog;
+ if (pfnLog)
+ {
+ char szBuffer[4096];
+ va_list va;
+ va_start(va, pszFormat);
+ RTStrPrintfV(szBuffer, RT_ELEMENTS(szBuffer), pszFormat, va);
+ va_end(va);
+ pfnLog(VBOXDRVCFG_LOG_SEVERITY_FLOW, szBuffer, pvLog);
+ }
+}
+
+static void vboxDrvCfgPanic(void)
+{
+ PFNVBOXDRVCFGPANIC pfnPanic = g_pfnVBoxDrvCfgPanic;
+ void *pvPanic = g_pvVBoxDrvCfgPanic;
+ if (pfnPanic)
+ pfnPanic(pvPanic);
+}
+
+/* we do not use IPRT Logging because the lib is used in host installer and needs to
+ * post its msgs to MSI logger */
+#define NonStandardLogCrap(_m) do { vboxDrvCfgLogRegular _m ; } while (0)
+#define NonStandardLogFlowCrap(_m) do { vboxDrvCfgLogFlow _m ; } while (0)
+#define NonStandardLogRelCrap(_m) do { vboxDrvCfgLogRel _m ; } while (0)
+#define NonStandardAssertFailed() vboxDrvCfgPanic()
+#define NonStandardAssert(_m) do { \
+ if (RT_UNLIKELY(!(_m))) { vboxDrvCfgPanic(); } \
+ } while (0)
+
+
+/**
+ * This is a simple string vector class.
+ *
+ * @note Is is _NOT_ a list as the name could lead you to believe, but a vector.
+ */
+class VBoxDrvCfgStringList
+{
+public:
+ VBoxDrvCfgStringList(size_t a_cElements);
+ ~VBoxDrvCfgStringList();
+
+ HRESULT add(LPWSTR pStr);
+
+ size_t size() { return m_cUsed; }
+
+ LPWSTR get(size_t i) { return i < m_cUsed ? m_paStrings[i] : NULL; }
+private:
+ HRESULT grow(size_t a_cNew);
+
+ /** Array of strings. */
+ LPWSTR *m_paStrings;
+ size_t m_cAllocated;
+ size_t m_cUsed;
+};
+
+VBoxDrvCfgStringList::VBoxDrvCfgStringList(size_t a_cElements)
+{
+ m_paStrings = (LPWSTR *)RTMemAllocZ(sizeof(m_paStrings[0]) * a_cElements);
+ m_cAllocated = a_cElements;
+ m_cUsed = 0;
+}
+
+VBoxDrvCfgStringList::~VBoxDrvCfgStringList()
+{
+ if (!m_cAllocated)
+ return;
+
+ for (size_t i = 0; i < m_cUsed; ++i)
+ RTMemFree(m_paStrings[i]);
+ RTMemFree(m_paStrings);
+ m_paStrings = NULL;
+ m_cAllocated = 0;
+ m_cUsed = 0;
+}
+
+HRESULT VBoxDrvCfgStringList::add(LPWSTR pStr)
+{
+ if (m_cUsed == m_cAllocated)
+ {
+ int hrc = grow(m_cAllocated + 16);
+ if (SUCCEEDED(hrc))
+ return hrc;
+ }
+ LPWSTR str = (LPWSTR)RTMemDup(pStr, (RTUtf16Len(pStr) + 1) * sizeof(m_paStrings[0][0]));
+ if (!str)
+ return E_OUTOFMEMORY;
+ m_paStrings[m_cUsed] = str;
+ ++m_cUsed;
+ return S_OK;
+}
+
+HRESULT VBoxDrvCfgStringList::grow(size_t a_cNew)
+{
+ NonStandardAssert(a_cNew >= m_cUsed);
+ if (a_cNew < m_cUsed)
+ return E_FAIL;
+ void *pvNew = RTMemReallocZ(m_paStrings, m_cUsed * sizeof(m_paStrings[0]), a_cNew * sizeof(m_paStrings[0]));
+ if (!pvNew)
+ return E_OUTOFMEMORY;
+ m_paStrings = (LPWSTR *)pvNew;
+ m_cAllocated = a_cNew;
+ return S_OK;
+}
+
+/*
+ * inf file manipulation API
+ */
+typedef bool (*PFNVBOXNETCFG_ENUMERATION_CALLBACK_T)(LPCWSTR lpszFileName, PVOID pContext);
+
+typedef struct INF_INFO_T
+{
+ LPCWSTR pwszClassName;
+ LPCWSTR pwszPnPId;
+} INF_INFO_T, *PINF_INFO_T;
+
+typedef struct INFENUM_CONTEXT_T
+{
+ INF_INFO_T InfInfo;
+ DWORD fFlags;
+ HRESULT hrc;
+} INFENUM_CONTEXT_T, *PINFENUM_CONTEXT_T;
+
+static HRESULT vboxDrvCfgInfQueryContext(HINF hInf, LPCWSTR pwszSection, LPCWSTR pwszKey, PINFCONTEXT pCtx)
+{
+ if (!SetupFindFirstLineW(hInf, pwszSection, pwszKey, pCtx))
+ {
+ DWORD dwErr = GetLastError();
+ NonStandardLogRelCrap((__FUNCTION__ ": SetupFindFirstLine failed WinEr (%Rwc) for Section(%ls), Key(%ls)\n",
+ dwErr, pwszSection, pwszKey));
+ return HRESULT_FROM_WIN32(dwErr);
+ }
+ return S_OK;
+}
+
+static HRESULT vboxDrvCfgInfQueryKeyValue(PINFCONTEXT pCtx, DWORD iValue, LPWSTR *ppwszValue, PDWORD pcwcValue)
+{
+ *ppwszValue = NULL;
+ if (pcwcValue)
+ *pcwcValue = 0;
+
+ DWORD cwcValue;
+ if (!SetupGetStringFieldW(pCtx, iValue, NULL, 0, &cwcValue))
+ {
+ DWORD dwErr = GetLastError();
+// NonStandardAssert(dwErr == ERROR_INSUFFICIENT_BUFFER);
+ if (dwErr != ERROR_INSUFFICIENT_BUFFER)
+ {
+ NonStandardLogFlowCrap((__FUNCTION__ ": SetupGetStringField failed WinEr (%Rwc) for iValue(%d)\n", dwErr, iValue));
+ return HRESULT_FROM_WIN32(dwErr);
+ }
+ }
+
+ LPWSTR pwszValue = (LPWSTR)RTMemAlloc(cwcValue * sizeof(pwszValue[0]));
+ NonStandardAssert(pwszValue);
+ if (!pwszValue)
+ {
+ NonStandardLogRelCrap((__FUNCTION__ ": SetCoTaskMemAlloc failed to alloc mem of size (%d), for iValue(%d)\n",
+ cwcValue * sizeof(pwszValue[0]), iValue));
+ return E_FAIL;
+ }
+
+ if (!SetupGetStringFieldW(pCtx, iValue, pwszValue, cwcValue, &cwcValue))
+ {
+ DWORD dwErr = GetLastError();
+ NonStandardLogRelCrap((__FUNCTION__ ": SetupGetStringField failed WinEr (%Rwc) for iValue(%d)\n", dwErr, iValue));
+ NonStandardAssertFailed();
+ RTMemFree(pwszValue);
+ return HRESULT_FROM_WIN32(dwErr);
+ }
+
+ *ppwszValue = pwszValue;
+ if (pcwcValue)
+ *pcwcValue = cwcValue;
+ return S_OK;
+}
+
+#if defined(RT_ARCH_AMD64)
+# define VBOXDRVCFG_ARCHSTR "amd64"
+#else
+# define VBOXDRVCFG_ARCHSTR "x86"
+#endif
+
+static HRESULT vboxDrvCfgInfQueryModelsSectionName(HINF hInf, LPWSTR *ppwszValue, PDWORD pcwcValue)
+{
+ *ppwszValue = NULL;
+ if (pcwcValue)
+ *pcwcValue = 0;
+
+ INFCONTEXT InfCtx;
+ HRESULT hrc = vboxDrvCfgInfQueryContext(hInf, L"Manufacturer", NULL, &InfCtx);
+ if (hrc != S_OK)
+ {
+ NonStandardLogCrap((__FUNCTION__ ": vboxDrvCfgInfQueryContext for Manufacturer failed, hrc=0x%x\n", hrc));
+ return hrc;
+ }
+
+ LPWSTR pwszModels;
+ DWORD cwcModels;
+ hrc = vboxDrvCfgInfQueryKeyValue(&InfCtx, 1, &pwszModels, &cwcModels);
+ if (hrc != S_OK)
+ {
+ NonStandardLogRelCrap((__FUNCTION__ ": vboxDrvCfgRegQueryKeyValue 1 for Manufacturer failed, hrc=0x%x\n", hrc));
+ return hrc;
+ }
+
+ LPWSTR pwszPlatform = NULL;
+ DWORD cwcPlatform = 0;
+ bool fArch = false;
+ bool fNt = false;
+
+ LPWSTR pwszPlatformCur;
+ DWORD cwcPlatformCur;
+ for (DWORD i = 2; (hrc = vboxDrvCfgInfQueryKeyValue(&InfCtx, i, &pwszPlatformCur, &cwcPlatformCur)) == S_OK; ++i)
+ {
+ if (RTUtf16ICmpAscii(pwszPlatformCur, "NT" VBOXDRVCFG_ARCHSTR) == 0)
+ fArch = true;
+ else
+ {
+ if (fNt || RTUtf16ICmpAscii(pwszPlatformCur, "NT") != 0)
+ {
+ RTMemFree(pwszPlatformCur);
+ pwszPlatformCur = NULL;
+ continue;
+ }
+ fNt = true;
+ }
+
+ cwcPlatform = cwcPlatformCur;
+ if (pwszPlatform)
+ RTMemFree(pwszPlatform);
+ pwszPlatform = pwszPlatformCur;
+ pwszPlatformCur = NULL;
+ }
+
+ hrc = S_OK;
+
+ LPWSTR pwszResult = NULL;
+ DWORD cwcResult = 0;
+ if (pwszPlatform)
+ {
+ pwszResult = (LPWSTR)RTMemAlloc((cwcModels + cwcPlatform) * sizeof(pwszResult[0]));
+ if (pwszResult)
+ {
+ memcpy(pwszResult, pwszModels, (cwcModels - 1) * sizeof(pwszResult[0]));
+ pwszResult[cwcModels - 1] = L'.';
+ memcpy(&pwszResult[cwcModels], pwszPlatform, cwcPlatform * sizeof(pwszResult[0]));
+ cwcResult = cwcModels + cwcPlatform;
+ }
+ else
+ hrc = E_OUTOFMEMORY;
+ }
+ else
+ {
+ pwszResult = pwszModels;
+ cwcResult = cwcModels;
+ pwszModels = NULL;
+ }
+
+ if (pwszModels)
+ RTMemFree(pwszModels);
+ if (pwszPlatform)
+ RTMemFree(pwszPlatform);
+
+ if (hrc == S_OK)
+ {
+ *ppwszValue = pwszResult;
+ if (pcwcValue)
+ *pcwcValue = cwcResult;
+ }
+
+ return hrc;
+}
+
+static HRESULT vboxDrvCfgInfQueryFirstPnPId(HINF hInf, LPWSTR *ppwszPnPId)
+{
+ *ppwszPnPId = NULL;
+
+ LPWSTR pwszModels;
+ HRESULT hrc = vboxDrvCfgInfQueryModelsSectionName(hInf, &pwszModels, NULL);
+ NonStandardLogRelCrap((__FUNCTION__ ": vboxDrvCfgInfQueryModelsSectionName returned pwszModels = (%ls)", pwszModels));
+ if (hrc != S_OK)
+ {
+ NonStandardLogCrap((__FUNCTION__ ": vboxDrvCfgRegQueryKeyValue for Manufacturer failed, hrc=0x%x\n", hrc));
+ return hrc;
+ }
+
+ LPWSTR pwszPnPId = NULL;
+ INFCONTEXT InfCtx;
+ hrc = vboxDrvCfgInfQueryContext(hInf, pwszModels, NULL, &InfCtx);
+ if (hrc == S_OK)
+ {
+ hrc = vboxDrvCfgInfQueryKeyValue(&InfCtx, 2, &pwszPnPId, NULL);
+ if (hrc == S_OK)
+ {
+ NonStandardLogRelCrap((__FUNCTION__ ": vboxDrvCfgRegQueryKeyValue for models (%ls) returned pwszPnPId (%ls)\n", pwszModels, pwszPnPId));
+ *ppwszPnPId = pwszPnPId;
+ }
+ else
+ NonStandardLogRelCrap((__FUNCTION__ ": vboxDrvCfgRegQueryKeyValue for models (%ls) failed, hrc=0x%x\n", pwszModels, hrc));
+ }
+ else
+ NonStandardLogRelCrap((__FUNCTION__ ": vboxDrvCfgInfQueryContext for models (%ls) failed, hrc=0x%x\n", pwszModels, hrc));
+
+ RTMemFree(pwszModels);
+ return hrc;
+}
+
+static bool vboxDrvCfgInfEnumerationCallback(LPCWSTR pwszFileName, PVOID pCtxt)
+{
+ PINFENUM_CONTEXT_T pContext = (PINFENUM_CONTEXT_T)pCtxt;
+ NonStandardLogRelCrap((__FUNCTION__": pwszFileName (%ls)\n", pwszFileName));
+ NonStandardLogRelCrap((__FUNCTION__ ": pContext->InfInfo.pwszClassName = (%ls)\n", pContext->InfInfo.pwszClassName));
+ HINF hInf = SetupOpenInfFileW(pwszFileName, pContext->InfInfo.pwszClassName, INF_STYLE_WIN4, NULL /*__in PUINT ErrorLine */);
+ if (hInf == INVALID_HANDLE_VALUE)
+ {
+ DWORD const dwErr = GetLastError();
+// NonStandardAssert(dwErr == ERROR_CLASS_MISMATCH);
+ if (dwErr != ERROR_CLASS_MISMATCH)
+ NonStandardLogCrap((__FUNCTION__ ": SetupOpenInfFileW err dwErr=%u\n", dwErr));
+ else
+ NonStandardLogCrap((__FUNCTION__ ": dwErr == ERROR_CLASS_MISMATCH\n"));
+ return true;
+ }
+
+ LPWSTR pwszPnPId;
+ HRESULT hrc = vboxDrvCfgInfQueryFirstPnPId(hInf, &pwszPnPId);
+ NonStandardLogRelCrap((__FUNCTION__ ": vboxDrvCfgInfQueryFirstPnPId returned pwszPnPId = (%ls)\n", pwszPnPId));
+ NonStandardLogRelCrap((__FUNCTION__ ": pContext->InfInfo.pwszPnPId = (%ls)\n", pContext->InfInfo.pwszPnPId));
+ if (hrc == S_OK)
+ {
+ if (!RTUtf16ICmp(pContext->InfInfo.pwszPnPId, pwszPnPId))
+ {
+ /** @todo bird/2020-09-01: See the following during uninstallation with
+ * windbg attached (see DllMain trick):
+ *
+ * ModLoad: 00007ffa`73c20000 00007ffa`73c4f000 C:\WINDOWS\SYSTEM32\drvsetup.dll
+ * (1b238.1b254): Access violation - code c0000005 (first chance)
+ * First chance exceptions are reported before any exception handling.
+ * This exception may be expected and handled.
+ * KERNELBASE!WaitForMultipleObjectsEx+0x9e:
+ * 00007ffa`8247cb6e 458b74fd00 mov r14d,dword ptr [r13+rdi*8] ds:00000000`00000010=????????
+ * 0:006> k
+ * # Child-SP RetAddr Call Site
+ * 00 00000099`6e4fe7a0 00007ffa`73c2df46 KERNELBASE!WaitForMultipleObjectsEx+0x9e
+ * 01 00000099`6e4fea90 00007ffa`73c32ec2 drvsetup!pSetupStringTableEnum+0x3e
+ * 02 00000099`6e4feae0 00007ffa`73c2ae9d drvsetup!DrvUtilsUpdateInfoEnumDriverInfs+0x8e
+ * 03 00000099`6e4feb20 00007ffa`73c2b1cc drvsetup!DrvSetupUninstallDriverInternal+0x211
+ * 04 00000099`6e4febe0 00007ffa`83eb09d7 drvsetup!pDrvSetupUninstallDriver+0xfc
+ * 05 00000099`6e4fec30 00007ffa`83eb06a0 SETUPAPI!pSetupUninstallOEMInf+0x26b
+ * 06 00000099`6e4fef00 00007ffa`57a39fb7 SETUPAPI!SetupUninstallOEMInfW+0x170
+ * 07 00000099`6e4ff190 00007ffa`57a3ae0c MSID039!vboxDrvCfgInfEnumerationCallback+0xf7 [E:\vbox\svn\trunk\src\VBox\HostDrivers\win\cfg\VBoxDrvCfg.cpp @ 445]
+ * 08 00000099`6e4ff1c0 00007ffa`57a321e6 MSID039!VBoxDrvCfgInfUninstallAllSetupDi+0xfc [E:\vbox\svn\trunk\src\VBox\HostDrivers\win\cfg\VBoxDrvCfg.cpp @ 653]
+ * 09 (Inline Function) --------`-------- MSID039!_removeHostOnlyInterfaces+0x6c [E:\vbox\svn\trunk\src\VBox\Installer\win\InstallHelper\VBoxInstallHelper.cpp @ 1523]
+ * 0a 00000099`6e4ff240 00007ffa`610f59d3 MSID039!RemoveHostOnlyInterfaces+0x76 [E:\vbox\svn\trunk\src\VBox\Installer\win\InstallHelper\VBoxInstallHelper.cpp @ 1545]
+ * 0b 00000099`6e4ff270 00007ffa`610d80ac msi!CallCustomDllEntrypoint+0x2b
+ * 0c 00000099`6e4ff2e0 00007ffa`84567034 msi!CMsiCustomAction::CustomActionThread+0x34c
+ * 0d 00000099`6e4ff8d0 00007ffa`849a2651 KERNEL32!BaseThreadInitThunk+0x14
+ * 0e 00000099`6e4ff900 00000000`00000000 ntdll!RtlUserThreadStart+0x21
+ * 0:006> r
+ * rax=000000996e114000 rbx=0000000000000002 rcx=0000000000000002
+ * rdx=0000000000000000 rsi=0000000000000000 rdi=0000000000000000
+ * rip=00007ffa8247cb6e rsp=000000996e4fe7a0 rbp=0000000000000004
+ * r8=0000000000000000 r9=00000000ffffffff r10=0000000000000000
+ * r11=0000000000000246 r12=00000000ffffffff r13=0000000000000010
+ * r14=00007ffa73c32e00 r15=0000000000000001
+ * iopl=0 nv up ei ng nz ac pe cy
+ * cs=0033 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00010293
+ * KERNELBASE!WaitForMultipleObjectsEx+0x9e:
+ * 00007ffa`8247cb6e 458b74fd00 mov r14d,dword ptr [r13+rdi*8] ds:00000000`00000010=????????
+ *
+ * Happens with the filter driver too:
+ *
+ * (1b238.1b7e0): Access violation - code c0000005 (first chance)
+ * First chance exceptions are reported before any exception handling.
+ * This exception may be expected and handled.
+ * KERNELBASE!WaitForMultipleObjectsEx+0x9e:
+ * 00007ffa`8247cb6e 458b74fd00 mov r14d,dword ptr [r13+rdi*8] ds:00000000`00000010=????????
+ * 0:006> k
+ * # Child-SP RetAddr Call Site
+ * 00 00000099`6e4fe8c0 00007ffa`6558df46 KERNELBASE!WaitForMultipleObjectsEx+0x9e
+ * 01 00000099`6e4febb0 00007ffa`65592ec2 drvsetup!pSetupStringTableEnum+0x3e
+ * 02 00000099`6e4fec00 00007ffa`6558ae9d drvsetup!DrvUtilsUpdateInfoEnumDriverInfs+0x8e
+ * 03 00000099`6e4fec40 00007ffa`6558b1cc drvsetup!DrvSetupUninstallDriverInternal+0x211
+ * 04 00000099`6e4fed00 00007ffa`83eb09d7 drvsetup!pDrvSetupUninstallDriver+0xfc
+ * 05 00000099`6e4fed50 00007ffa`83eb06a0 SETUPAPI!pSetupUninstallOEMInf+0x26b
+ * 06 00000099`6e4ff020 00007ffa`57a39fb7 SETUPAPI!SetupUninstallOEMInfW+0x170
+ * 07 00000099`6e4ff2b0 00007ffa`57a3abaf MSI398C!vboxDrvCfgInfEnumerationCallback+0xf7 [E:\vbox\svn\trunk\src\VBox\HostDrivers\win\cfg\VBoxDrvCfg.cpp @ 445]
+ * 08 (Inline Function) --------`-------- MSI398C!vboxDrvCfgEnumFiles+0x4f [E:\vbox\svn\trunk\src\VBox\HostDrivers\win\cfg\VBoxDrvCfg.cpp @ 670]
+ * 09 00000099`6e4ff2e0 00007ffa`57a3792e MSI398C!VBoxDrvCfgInfUninstallAllF+0xdf [E:\vbox\svn\trunk\src\VBox\HostDrivers\win\cfg\VBoxDrvCfg.cpp @ 723]
+ * 0a 00000099`6e4ff7b0 00007ffa`57a33411 MSI398C!vboxNetCfgWinNetLwfUninstall+0x9e [E:\vbox\svn\trunk\src\VBox\HostDrivers\VBoxNetFlt\win\cfg\VBoxNetCfg.cpp @ 2249]
+ * 0b 00000099`6e4ff7e0 00007ffa`57a3263d MSI398C!_uninstallNetLwf+0x71 [E:\vbox\svn\trunk\src\VBox\Installer\win\InstallHelper\VBoxInstallHelper.cpp @ 1206]
+ * 0c 00000099`6e4ff810 00007ffa`610f59d3 MSI398C!UninstallNetFlt+0xd [E:\vbox\svn\trunk\src\VBox\Installer\win\InstallHelper\VBoxInstallHelper.cpp @ 1124]
+ * 0d 00000099`6e4ff840 00007ffa`610d80ac msi!CallCustomDllEntrypoint+0x2b
+ * 0e 00000099`6e4ff8b0 00007ffa`84567034 msi!CMsiCustomAction::CustomActionThread+0x34c
+ * 0f 00000099`6e4ffea0 00007ffa`849a2651 KERNEL32!BaseThreadInitThunk+0x14
+ * 10 00000099`6e4ffed0 00000000`00000000 ntdll!RtlUserThreadStart+0x21
+ * 0:006> r
+ * rax=000000996e114000 rbx=0000000000000002 rcx=0000000000000002
+ * rdx=0000000000000000 rsi=0000000000000000 rdi=0000000000000000
+ * rip=00007ffa8247cb6e rsp=000000996e4fe8c0 rbp=0000000000000004
+ * r8=0000000000000000 r9=00000000ffffffff r10=0000000000000000
+ * r11=0000000000000246 r12=00000000ffffffff r13=0000000000000010
+ * r14=00007ffa65592e00 r15=0000000000000000
+ * iopl=0 nv up ei ng nz ac pe cy
+ * cs=0033 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00010293
+ * KERNELBASE!WaitForMultipleObjectsEx+0x9e:
+ * 00007ffa`8247cb6e 458b74fd00 mov r14d,dword ptr [r13+rdi*8] ds:00000000`00000010=????????
+ *
+ * BUGBUG
+ */
+#if 0
+ if (!SetupUninstallOEMInfW(pwszFileName, pContext->fFlags, /* could be SUOI_FORCEDELETE */ NULL /* Reserved */))
+#else /* Just in case the API doesn't catch it itself (seems it does on w10/19044). */
+ BOOL fRc = TRUE;
+ __try
+ {
+ fRc = SetupUninstallOEMInfW(pwszFileName, pContext->fFlags, /* could be SUOI_FORCEDELETE */ NULL /* Reserved */);
+ }
+ __except(hrc = GetExceptionCode(), EXCEPTION_EXECUTE_HANDLER)
+ {
+ NonStandardLogRelCrap((__FUNCTION__ ": SetupUninstallOEMInf raised an exception: %#x\n", hrc));
+ hrc = E_ABORT;
+ }
+ if (!fRc)
+#endif
+ {
+ DWORD const dwErr = GetLastError();
+ NonStandardLogRelCrap((__FUNCTION__ ": SetupUninstallOEMInf failed for file (%ls), dwErr=%u\n", pwszFileName, dwErr));
+ NonStandardAssertFailed();
+ hrc = HRESULT_FROM_WIN32( dwErr );
+ }
+ }
+
+ RTMemFree(pwszPnPId);
+ }
+ else
+ NonStandardLogCrap((__FUNCTION__ ": vboxDrvCfgInfQueryFirstPnPId failed, hrc=0x%x\n", hrc));
+
+ SetupCloseInfFile(hInf);
+ return true;
+}
+
+
+#define VBOXDRVCFG_S_INFEXISTS (HRESULT_FROM_WIN32(ERROR_FILE_EXISTS))
+
+static HRESULT vboxDrvCfgInfCopyEx(IN LPCWSTR pwszInfPath, IN DWORD fCopyStyle, OUT LPWSTR pwszDstName, IN DWORD cwcDstName,
+ OUT PDWORD pcwcDstNameRet, OUT LPWSTR *pwszDstNameComponent)
+{
+ /* Extract the director from pwszInfPath */
+ size_t cchPath = RTUtf16Len(pwszInfPath);
+ while (cchPath > 0 && !RTPATH_IS_SEP(pwszInfPath[cchPath - 1]))
+ cchPath--;
+
+ WCHAR *pwszMediaLocation = (WCHAR *)alloca(((cchPath) + 1) * sizeof(pwszMediaLocation[0]));
+ memcpy(pwszMediaLocation, pwszInfPath, cchPath * sizeof(pwszMediaLocation[0]));
+ pwszMediaLocation[cchPath] = '\0';
+
+
+ if (!SetupCopyOEMInfW(pwszInfPath, pwszMediaLocation, SPOST_PATH, fCopyStyle,
+ pwszDstName, cwcDstName, pcwcDstNameRet, pwszDstNameComponent))
+ {
+ DWORD const dwErr = GetLastError();
+ HRESULT hrc = HRESULT_FROM_WIN32(dwErr);
+ if (fCopyStyle != SP_COPY_REPLACEONLY || hrc != VBOXDRVCFG_S_INFEXISTS)
+ NonStandardLogRelCrap((__FUNCTION__ ": SetupCopyOEMInf fail dwErr=%u\n", dwErr));
+ return hrc;
+ }
+
+ return S_OK;
+}
+
+VBOXDRVCFG_DECL(HRESULT) VBoxDrvCfgInfInstall(IN LPCWSTR pwszInfPath)
+{
+ return vboxDrvCfgInfCopyEx(pwszInfPath, 0 /*fCopyStyle*/, NULL /*pwszDstName*/, 0 /*cwcDstName*/,
+ NULL /*pcwcDstNameRet*/, NULL /*pwszDstNameComponent*/);
+}
+
+VBOXDRVCFG_DECL(HRESULT) VBoxDrvCfgInfUninstall(IN LPCWSTR pwszInfPath, DWORD fFlags)
+{
+ WCHAR wszDstInfName[MAX_PATH];
+ DWORD cwcDword = RT_ELEMENTS(wszDstInfName);
+ HRESULT hrc = vboxDrvCfgInfCopyEx(pwszInfPath, SP_COPY_REPLACEONLY, wszDstInfName, cwcDword, &cwcDword, NULL);
+ if (hrc == VBOXDRVCFG_S_INFEXISTS)
+ {
+ if (!SetupUninstallOEMInfW(wszDstInfName, fFlags, NULL /*Reserved*/))
+ {
+ DWORD dwErr = GetLastError();
+ NonStandardLogRelCrap((__FUNCTION__ ": SetupUninstallOEMInf failed for file (%ls), oem(%ls), dwErr=%u\n",
+ pwszInfPath, wszDstInfName, dwErr));
+ NonStandardAssertFailed();
+ return HRESULT_FROM_WIN32(dwErr);
+ }
+ }
+ return S_OK;
+}
+
+
+static HRESULT vboxDrvCfgCollectInfsSetupDi(const GUID *pGuid, LPCWSTR pwszPnPId, VBoxDrvCfgStringList &a_rList)
+{
+ DWORD dwErrRet = ERROR_SUCCESS;
+ HDEVINFO hDevInfo = SetupDiCreateDeviceInfoList(pGuid, /*ClassGuid*/ NULL /*hwndParent*/);
+ if (hDevInfo != INVALID_HANDLE_VALUE)
+ {
+ /** @todo bird/2020-09-01: seeing this during uninstall when windbg is
+ * attached to msiexec.exe (see trick in DllMain):
+ *
+ * (1b238.1b254): Access violation - code c0000005 (first chance)
+ * First chance exceptions are reported before any exception handling.
+ * This exception may be expected and handled.
+ * SETUPAPI!SpSignVerifyInfFile+0x246:
+ * 00007ffa`83e3ee3e 663907 cmp word ptr [rdi],ax ds:00000000`00000000=????
+ * 0:006> k
+ * # Child-SP RetAddr Call Site
+ * 00 00000099`6e4f8340 00007ffa`83e1e765 SETUPAPI!SpSignVerifyInfFile+0x246
+ * 01 00000099`6e4f8420 00007ffa`83e9ebfd SETUPAPI!DrvSearchCallback+0x1155
+ * 02 00000099`6e4f9380 00007ffa`83e9eed3 SETUPAPI!InfCacheSearchDirectory+0x469
+ * 03 00000099`6e4f98b0 00007ffa`83e9f454 SETUPAPI!InfCacheSearchDirectoryRecursive+0xcf
+ * 04 00000099`6e4f9fe0 00007ffa`83e9da10 SETUPAPI!InfCacheSearchPath+0x1a0
+ * 05 00000099`6e4fa2b0 00007ffa`83e262a2 SETUPAPI!EnumDrvInfsInDirPathList+0x560
+ * 06 00000099`6e4fa3f0 00007ffa`57a39a21 SETUPAPI!SetupDiBuildDriverInfoList+0x1242
+ * 07 00000099`6e4fab10 00007ffa`57a3ad6e MSID039!vboxDrvCfgCollectInfsSetupDi+0x71 [E:\vbox\svn\trunk\src\VBox\HostDrivers\win\cfg\VBoxDrvCfg.cpp @ 526]
+ * 08 00000099`6e4ff1c0 00007ffa`57a321e6 MSID039!VBoxDrvCfgInfUninstallAllSetupDi+0x5e [E:\vbox\svn\trunk\src\VBox\HostDrivers\win\cfg\VBoxDrvCfg.cpp @ 633]
+ * 09 (Inline Function) --------`-------- MSID039!_removeHostOnlyInterfaces+0x6c [E:\vbox\svn\trunk\src\VBox\Installer\win\InstallHelper\VBoxInstallHelper.cpp @ 1523]
+ * 0a 00000099`6e4ff240 00007ffa`610f59d3 MSID039!RemoveHostOnlyInterfaces+0x76 [E:\vbox\svn\trunk\src\VBox\Installer\win\InstallHelper\VBoxInstallHelper.cpp @ 1545]
+ * 0b 00000099`6e4ff270 00007ffa`610d80ac msi!CallCustomDllEntrypoint+0x2b
+ * 0c 00000099`6e4ff2e0 00007ffa`84567034 msi!CMsiCustomAction::CustomActionThread+0x34c
+ * 0d 00000099`6e4ff8d0 00007ffa`849a2651 KERNEL32!BaseThreadInitThunk+0x14
+ * 0e 00000099`6e4ff900 00000000`00000000 ntdll!RtlUserThreadStart+0x21
+ * 0:006> r
+ * rax=0000000000000000 rbx=0000000000000490 rcx=aa222a2675da0000
+ * rdx=0000000000000000 rsi=0000000000000000 rdi=0000000000000000
+ * rip=00007ffa83e3ee3e rsp=000000996e4f8340 rbp=000000996e4f9480
+ * r8=0000000000050004 r9=00007ffa83ef5418 r10=0000000000008000
+ * r11=000000996e4f76f0 r12=000000996e4f84c8 r13=0000000000000000
+ * r14=000000996e4f88d0 r15=0000000000000000
+ * iopl=0 nv up ei pl nz ac pe cy
+ * cs=0033 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00010213
+ * SETUPAPI!SpSignVerifyInfFile+0x246:
+ * 00007ffa`83e3ee3e 663907 cmp word ptr [rdi],ax ds:00000000`00000000=????
+ */
+#if 0
+ if (SetupDiBuildDriverInfoList(hDevInfo, NULL /*DeviceInfoData*/, SPDIT_CLASSDRIVER))
+#else /* Just in case the API doesn't catch it itself (seems it does on w10/19044). */
+ BOOL fRc = FALSE;
+ DWORD uXcpt = 0;
+ __try
+ {
+ fRc = SetupDiBuildDriverInfoList(hDevInfo, NULL /*DeviceInfoData*/, SPDIT_CLASSDRIVER);
+ }
+ __except(uXcpt = GetExceptionCode(), EXCEPTION_EXECUTE_HANDLER)
+ {
+ NonStandardLogRelCrap((__FUNCTION__ ": SetupDiBuildDriverInfoList raised an exception: %#x\n", uXcpt));
+ }
+ if (fRc)
+#endif
+ {
+ SP_DRVINFO_DATA DrvInfo;
+ DrvInfo.cbSize = sizeof(SP_DRVINFO_DATA);
+
+ union
+ {
+ SP_DRVINFO_DETAIL_DATA_W s;
+ uint8_t ab[16384];
+ } DrvDetail;
+
+ /* Ensure zero terminated buffer: */
+ DrvDetail.ab[sizeof(DrvDetail) - 1] = '\0';
+ DrvDetail.ab[sizeof(DrvDetail) - 2] = '\0';
+
+ for (DWORD i = 0; dwErrRet == ERROR_SUCCESS; i++)
+ {
+ if (SetupDiEnumDriverInfo(hDevInfo, NULL /*DeviceInfoData*/, SPDIT_CLASSDRIVER /*DriverType*/,
+ i /*MemberIndex*/, &DrvInfo /*DriverInfoData*/))
+ {
+ DWORD dwReq = 0;
+ DrvDetail.s.cbSize = sizeof(SP_DRVINFO_DETAIL_DATA);
+ if (SetupDiGetDriverInfoDetail(hDevInfo, NULL /*DeviceInfoData*/, &DrvInfo,
+ &DrvDetail.s, sizeof(DrvDetail) - 2 /*our terminator*/, &dwReq))
+ {
+ for (WCHAR *pwszHwId = DrvDetail.s.HardwareID;
+ *pwszHwId != '\0' && (uintptr_t)pwszHwId < (uintptr_t)&DrvDetail.ab[sizeof(DrvDetail)];
+ pwszHwId += RTUtf16Len(pwszHwId) + 1)
+ {
+ if (RTUtf16ICmp(pwszHwId, pwszPnPId) == 0)
+ {
+ NonStandardAssert(DrvDetail.s.InfFileName[0]);
+ if (DrvDetail.s.InfFileName[0])
+ {
+ HRESULT hrc = a_rList.add(DrvDetail.s.InfFileName);
+ NonStandardLogRelCrap((__FUNCTION__": %ls added to list (%#x)", DrvDetail.s.InfFileName, hrc));
+ if (hrc != S_OK)
+ {
+ dwErrRet = ERROR_OUTOFMEMORY;
+ break;
+ }
+ }
+ }
+ }
+ }
+ else
+ {
+ DWORD dwErr2 = GetLastError();
+ NonStandardLogRelCrap((__FUNCTION__": SetupDiGetDriverInfoDetail fail dwErr=%u, size(%d)", dwErr2, dwReq));
+// NonStandardAssertFailed();
+ }
+ }
+ else
+ {
+ DWORD dwErr2 = GetLastError();
+ if (dwErr2 == ERROR_NO_MORE_ITEMS)
+ {
+ NonStandardLogRelCrap((__FUNCTION__": dwErr == ERROR_NO_MORE_ITEMS -> search was finished "));
+ break;
+ }
+ NonStandardAssertFailed();
+ }
+ }
+
+ SetupDiDestroyDriverInfoList(hDevInfo, NULL /*DeviceInfoData*/, SPDIT_CLASSDRIVER);
+ }
+ else
+ {
+ dwErrRet = GetLastError();
+ NonStandardAssertFailed();
+ }
+
+ SetupDiDestroyDeviceInfoList(hDevInfo);
+ }
+ else
+ {
+ dwErrRet = GetLastError();
+ NonStandardAssertFailed();
+ }
+
+ return HRESULT_FROM_WIN32(dwErrRet);
+}
+
+#if 0
+VBOXDRVCFG_DECL(HRESULT) VBoxDrvCfgInit()
+{
+ int rc = RTR3InitDll(0);
+ if (rc != VINF_SUCCESS)
+ {
+ NonStandardLogRelCrap(("Could not init IPRT!, rc (%d)\n", rc));
+ return E_FAIL;
+ }
+
+ return S_OK;
+}
+
+VBOXDRVCFG_DECL(HRESULT) VBoxDrvCfgTerm()
+{
+ return S_OK;
+}
+#endif
+
+VBOXDRVCFG_DECL(HRESULT) VBoxDrvCfgInfUninstallAllSetupDi(IN const GUID *pGuidClass, IN LPCWSTR pwszClassName,
+ IN LPCWSTR pwszPnPId, IN DWORD fFlags)
+{
+ VBoxDrvCfgStringList list(128);
+ HRESULT hrc = vboxDrvCfgCollectInfsSetupDi(pGuidClass, pwszPnPId, list);
+ NonStandardLogRelCrap((__FUNCTION__": vboxDrvCfgCollectInfsSetupDi returned %d devices with PnPId %ls and class name %ls",
+ list.size(), pwszPnPId, pwszClassName));
+ if (hrc == S_OK)
+ {
+ INFENUM_CONTEXT_T Context;
+ Context.InfInfo.pwszClassName = pwszClassName;
+ Context.InfInfo.pwszPnPId = pwszPnPId;
+ Context.fFlags = fFlags;
+ Context.hrc = S_OK;
+ size_t const cItems = list.size();
+ for (size_t i = 0; i < cItems; ++i)
+ {
+ LPCWSTR pwszInf = list.get(i);
+
+ /* Find the start of the filename: */
+ size_t offFilename = RTUtf16Len(pwszInf);
+ while (offFilename > 0 && !RTPATH_IS_SEP(pwszInf[offFilename - 1]))
+ offFilename--;
+
+ vboxDrvCfgInfEnumerationCallback(&pwszInf[offFilename], &Context);
+ NonStandardLogRelCrap((__FUNCTION__": inf = %ls\n", pwszInf));
+ }
+ }
+ return hrc;
+}
+
+static HRESULT vboxDrvCfgEnumFiles(LPCWSTR pwszDirAndPattern, PFNVBOXNETCFG_ENUMERATION_CALLBACK_T pfnCallback, PVOID pContext)
+{
+ HRESULT hrc = S_OK;
+
+ WIN32_FIND_DATAW Data;
+ RT_ZERO(Data);
+ HANDLE hEnum = FindFirstFileW(pwszDirAndPattern, &Data);
+ if (hEnum != INVALID_HANDLE_VALUE)
+ {
+ for (;;)
+ {
+ if (!pfnCallback(Data.cFileName, pContext))
+ break;
+
+ /* next iteration */
+ RT_ZERO(Data);
+ BOOL fNext = FindNextFile(hEnum, &Data);
+ if (!fNext)
+ {
+ DWORD dwErr = GetLastError();
+ if (dwErr != ERROR_NO_MORE_FILES)
+ {
+ NonStandardLogRelCrap((__FUNCTION__": FindNextFile fail dwErr=%u\n", dwErr));
+ NonStandardAssertFailed();
+ hrc = HRESULT_FROM_WIN32(dwErr);
+ }
+ break;
+ }
+ }
+
+ FindClose(hEnum);
+ }
+ else
+ {
+ DWORD dwErr = GetLastError();
+ if (dwErr != ERROR_NO_MORE_FILES)
+ {
+ NonStandardLogRelCrap((__FUNCTION__": FindFirstFile fail dwErr=%u\n", dwErr));
+ NonStandardAssertFailed();
+ hrc = HRESULT_FROM_WIN32(dwErr);
+ }
+ }
+
+ return hrc;
+}
+
+VBOXDRVCFG_DECL(HRESULT) VBoxDrvCfgInfUninstallAllF(LPCWSTR pwszClassName, LPCWSTR pwszPnPId, DWORD fFlags)
+{
+ static WCHAR const s_wszFilter[] = L"\\inf\\oem*.inf";
+
+ HRESULT hrc;
+ WCHAR wszInfDirPath[MAX_PATH];
+ UINT cwcInput = RT_ELEMENTS(wszInfDirPath) - RT_ELEMENTS(s_wszFilter);
+ UINT cwcWindows = GetSystemWindowsDirectoryW(wszInfDirPath, cwcInput);
+ if (cwcWindows > 0 && cwcWindows < cwcInput)
+ {
+ RTUtf16Copy(&wszInfDirPath[cwcWindows], RT_ELEMENTS(wszInfDirPath) - cwcWindows, s_wszFilter);
+
+ INFENUM_CONTEXT_T Context;
+ Context.InfInfo.pwszClassName = pwszClassName;
+ Context.InfInfo.pwszPnPId = pwszPnPId;
+ Context.fFlags = fFlags;
+ Context.hrc = S_OK;
+ NonStandardLogRelCrap((__FUNCTION__": Calling vboxDrvCfgEnumFiles(wszInfDirPath, vboxDrvCfgInfEnumerationCallback, &Context)"));
+ hrc = vboxDrvCfgEnumFiles(wszInfDirPath, vboxDrvCfgInfEnumerationCallback, &Context);
+ NonStandardAssert(hrc == S_OK);
+ if (hrc == S_OK)
+ hrc = Context.hrc;
+ else
+ NonStandardLogRelCrap((__FUNCTION__": vboxDrvCfgEnumFiles failed, hrc=0x%x\n", hrc));
+ }
+ else
+ {
+ NonStandardLogRelCrap((__FUNCTION__": GetSystemWindowsDirectory failed, cwcWindows=%u lasterr=%u\n", cwcWindows, GetLastError()));
+ NonStandardAssertFailed();
+ hrc = E_FAIL;
+ }
+
+ return hrc;
+
+}
+
+/* time intervals in milliseconds */
+/* max time to wait for the service to startup */
+#define VBOXDRVCFG_SVC_WAITSTART_TIME 10000
+/* sleep time before service status polls */
+#define VBOXDRVCFG_SVC_WAITSTART_TIME_PERIOD 100
+/* number of service start polls */
+#define VBOXDRVCFG_SVC_WAITSTART_RETRIES (VBOXDRVCFG_SVC_WAITSTART_TIME/VBOXDRVCFG_SVC_WAITSTART_TIME_PERIOD)
+
+VBOXDRVCFG_DECL(HRESULT) VBoxDrvCfgSvcStart(LPCWSTR pwszSvcName)
+{
+ SC_HANDLE hMgr = OpenSCManagerW(NULL, NULL, SERVICE_QUERY_STATUS | SERVICE_START);
+ if (hMgr == NULL)
+ {
+ DWORD dwErr = GetLastError();
+ NonStandardLogRelCrap((__FUNCTION__": OpenSCManager failed, dwErr=%u\n", dwErr));
+ return HRESULT_FROM_WIN32(dwErr);
+ }
+
+ HRESULT hrc = S_OK;
+ SC_HANDLE hSvc = OpenServiceW(hMgr, pwszSvcName, SERVICE_QUERY_STATUS | SERVICE_START);
+ if (hSvc)
+ {
+ SERVICE_STATUS Status;
+ BOOL fRc = QueryServiceStatus(hSvc, &Status);
+ if (fRc)
+ {
+ if (Status.dwCurrentState != SERVICE_RUNNING && Status.dwCurrentState != SERVICE_START_PENDING)
+ {
+ NonStandardLogRelCrap(("Starting service (%ls)\n", pwszSvcName));
+
+ fRc = StartService(hSvc, 0, NULL);
+ if (!fRc)
+ {
+ DWORD dwErr = GetLastError();
+ NonStandardLogRelCrap((__FUNCTION__": StartService failed dwErr=%u\n", dwErr));
+ hrc = HRESULT_FROM_WIN32(dwErr);
+ }
+ }
+
+ if (fRc)
+ {
+ fRc = QueryServiceStatus(hSvc, &Status);
+ if (fRc)
+ {
+ if (Status.dwCurrentState == SERVICE_START_PENDING)
+ for (size_t i = 0; i < VBOXDRVCFG_SVC_WAITSTART_RETRIES; ++i)
+ {
+ Sleep(VBOXDRVCFG_SVC_WAITSTART_TIME_PERIOD);
+ fRc = QueryServiceStatus(hSvc, &Status);
+ if (!fRc)
+ {
+ DWORD dwErr = GetLastError();
+ NonStandardLogRelCrap((__FUNCTION__": QueryServiceStatus failed dwErr=%u\n", dwErr));
+ hrc = HRESULT_FROM_WIN32(dwErr);
+ break;
+ }
+ if (Status.dwCurrentState != SERVICE_START_PENDING)
+ break;
+ }
+
+ if (hrc != S_OK || Status.dwCurrentState != SERVICE_RUNNING)
+ {
+ NonStandardLogRelCrap((__FUNCTION__": Failed to start the service\n"));
+ hrc = E_FAIL;
+ }
+ }
+ else
+ {
+ DWORD dwErr = GetLastError();
+ NonStandardLogRelCrap((__FUNCTION__": QueryServiceStatus failed dwErr=%u\n", dwErr));
+ hrc = HRESULT_FROM_WIN32(dwErr);
+ }
+ }
+ }
+ else
+ {
+ DWORD dwErr = GetLastError();
+ NonStandardLogRelCrap((__FUNCTION__": QueryServiceStatus failed dwErr=%u\n", dwErr));
+ hrc = HRESULT_FROM_WIN32(dwErr);
+ }
+
+ CloseServiceHandle(hSvc);
+ }
+ else
+ {
+ DWORD dwErr = GetLastError();
+ NonStandardLogRelCrap((__FUNCTION__": OpenServiceW failed, dwErr=%u\n", dwErr));
+ hrc = HRESULT_FROM_WIN32(dwErr);
+ }
+
+ CloseServiceHandle(hMgr);
+ return hrc;
+}
+
+
+HRESULT VBoxDrvCfgDrvUpdate(LPCWSTR pszwHwId, LPCWSTR psxwInf, BOOL *pfRebootRequired)
+{
+ if (pfRebootRequired)
+ *pfRebootRequired = FALSE;
+
+ WCHAR wszInfFullPath[MAX_PATH];
+ DWORD dwChars = GetFullPathNameW(psxwInf, MAX_PATH, wszInfFullPath, NULL /*lpFilePart*/);
+ if (!dwChars || dwChars >= MAX_PATH)
+ {
+ NonStandardLogCrap(("GetFullPathNameW failed, dwErr=%u, dwChars=%ld\n", GetLastError(), dwChars));
+ return E_INVALIDARG;
+ }
+
+ BOOL fRebootRequired = FALSE;
+ if (!UpdateDriverForPlugAndPlayDevicesW(NULL /*hwndParent*/, pszwHwId, wszInfFullPath, INSTALLFLAG_FORCE, &fRebootRequired))
+ {
+ DWORD dwErr = GetLastError();
+ NonStandardLogCrap(("UpdateDriverForPlugAndPlayDevicesW failed, dwErr=%u\n", dwErr));
+ return HRESULT_FROM_WIN32(dwErr);
+ }
+
+ if (fRebootRequired)
+ NonStandardLogCrap(("!!Driver Update: REBOOT REQUIRED!!\n"));
+
+ if (pfRebootRequired)
+ *pfRebootRequired = fRebootRequired;
+
+ return S_OK;
+}
+
diff --git a/src/VBox/HostDrivers/win/load.cmd b/src/VBox/HostDrivers/win/load.cmd
new file mode 100644
index 00000000..3015c787
--- /dev/null
+++ b/src/VBox/HostDrivers/win/load.cmd
@@ -0,0 +1,117 @@
+@echo off
+rem $Id: load.cmd $
+rem rem @file
+rem Windows NT batch script for loading the support driver.
+rem
+
+rem
+rem Copyright (C) 2009-2023 Oracle and/or its affiliates.
+rem
+rem This file is part of VirtualBox base platform packages, as
+rem available from https://www.virtualbox.org.
+rem
+rem This program is free software; you can redistribute it and/or
+rem modify it under the terms of the GNU General Public License
+rem as published by the Free Software Foundation, in version 3 of the
+rem License.
+rem
+rem This program is distributed in the hope that it will be useful, but
+rem WITHOUT ANY WARRANTY; without even the implied warranty of
+rem MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+rem General Public License for more details.
+rem
+rem You should have received a copy of the GNU General Public License
+rem along with this program; if not, see <https://www.gnu.org/licenses>.
+rem
+rem The contents of this file may alternatively be used under the terms
+rem of the Common Development and Distribution License Version 1.0
+rem (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+rem in the VirtualBox distribution, in which case the provisions of the
+rem CDDL are applicable instead of those of the GPL.
+rem
+rem You may elect to license modified versions of this file under the
+rem terms and conditions of either the GPL or the CDDL or both.
+rem
+rem SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+rem
+
+
+setlocal ENABLEEXTENSIONS
+setlocal ENABLEDELAYEDEXPANSION
+setlocal
+
+
+rem
+rem find the directory we're in.
+rem
+set MY_DIR=%~dp0
+if exist "%MY_DIR%\load.cmd" goto dir_okay
+echo load.cmd: failed to find load.sh in "%~dp0".
+goto end
+
+:dir_okay
+rem
+rem We don't use the driver files directly any more because of win10 keeping the open,
+rem so create an alternative directory for the binaries.
+rem
+set MY_ALTDIR=%MY_DIR%\..\LoadedDrivers
+if not exist "%MY_ALTDIR%" mkdir "%MY_ALTDIR%"
+
+rem
+rem Display device states.
+rem
+for %%i in (VBoxNetAdp VBoxNetAdp6 VBoxNetFlt VBoxNetLwf VBoxUSBMon VBoxUSB VBoxSup) do (
+ set type=
+ for /f "usebackq tokens=*" %%f in (`sc query %%i`) do (set xxx=%%f&&if "!xxx:~0,5!" =="STATE" set type=!xxx!)
+ for /f "usebackq tokens=2 delims=:" %%f in ('!type!') do set type=%%f
+ if "!type!x" == "x" set type= not configured, probably
+ echo load.sh: %%i -!type!
+)
+
+rem
+rem Copy uninstallers and installers and VBoxRT into the dir:
+rem
+echo **
+echo ** Copying installers and uninstallers into %MY_ALTDIR%...
+echo **
+set MY_FAILED=no
+for %%i in (NetAdpUninstall.exe NetAdp6Uninstall.exe USBUninstall.exe NetFltUninstall.exe NetLwfUninstall.exe SUPUninstall.exe ^
+ NetAdpInstall.exe NetAdp6Install.exe USBInstall.exe NetFltInstall.exe NetLwfInstall.exe SUPInstall.exe ^
+ VBoxRT.dll) do if exist "%MY_DIR%\%%i" (copy "%MY_DIR%\%%i" "%MY_ALTDIR%\%%i" || set MY_FAILED=yes)
+if "%MY_FAILED%" == "yes" goto end
+
+rem
+rem Unload the drivers.
+rem
+echo **
+echo ** Unloading drivers...
+echo **
+for %%i in (NetAdpUninstall.exe NetAdp6Uninstall.exe USBUninstall.exe NetFltUninstall.exe NetLwfUninstall.exe SUPUninstall.exe) do (
+ if exist "%MY_ALTDIR%\%%i" (echo ** Running %%i...&& "%MY_ALTDIR%\%%i")
+)
+
+rem
+rem Copy the driver files into the directory now that they no longer should be in use and can be overwritten.
+rem
+echo **
+echo ** Copying drivers into %MY_ALTDIR%...
+echo **
+set MY_FAILED=no
+for %%i in (VBoxSup.sys VBoxSup.inf VBoxSup.cat) do if exist "%MY_DIR%\%%i" (copy "%MY_DIR%\%%i" "%MY_ALTDIR%\%%i" || set MY_FAILED=yes)
+if "%MY_FAILED%" == "yes" goto end
+
+rem
+rem Invoke the installer if asked to do so.
+rem
+if "%1%" == "-u" goto end
+if "%1%" == "--uninstall" goto end
+echo **
+echo ** Loading drivers...
+echo **
+for %%i in (SUPInstall.exe) do "%MY_ALTDIR%\%%i" || goto end
+
+:end
+endlocal
+endlocal
+endlocal
+
diff --git a/src/VBox/HostDrivers/win/loadall.cmd b/src/VBox/HostDrivers/win/loadall.cmd
new file mode 100644
index 00000000..593907bc
--- /dev/null
+++ b/src/VBox/HostDrivers/win/loadall.cmd
@@ -0,0 +1,142 @@
+@echo off
+rem $Id: loadall.cmd $
+rem rem @file
+rem Windows NT batch script for loading the host drivers.
+rem
+
+rem
+rem Copyright (C) 2009-2023 Oracle and/or its affiliates.
+rem
+rem This file is part of VirtualBox base platform packages, as
+rem available from https://www.virtualbox.org.
+rem
+rem This program is free software; you can redistribute it and/or
+rem modify it under the terms of the GNU General Public License
+rem as published by the Free Software Foundation, in version 3 of the
+rem License.
+rem
+rem This program is distributed in the hope that it will be useful, but
+rem WITHOUT ANY WARRANTY; without even the implied warranty of
+rem MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+rem General Public License for more details.
+rem
+rem You should have received a copy of the GNU General Public License
+rem along with this program; if not, see <https://www.gnu.org/licenses>.
+rem
+rem The contents of this file may alternatively be used under the terms
+rem of the Common Development and Distribution License Version 1.0
+rem (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+rem in the VirtualBox distribution, in which case the provisions of the
+rem CDDL are applicable instead of those of the GPL.
+rem
+rem You may elect to license modified versions of this file under the
+rem terms and conditions of either the GPL or the CDDL or both.
+rem
+rem SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+rem
+
+
+setlocal ENABLEEXTENSIONS
+setlocal ENABLEDELAYEDEXPANSION
+setlocal
+
+
+rem
+rem find the directory we're in.
+rem
+set MY_DIR=%~dp0
+if exist "%MY_DIR%\load.cmd" goto dir_okay
+echo load.cmd: failed to find load.sh in "%~dp0".
+goto end
+
+:dir_okay
+rem
+rem We don't use the driver files directly any more because of win10 keeping the open,
+rem so create an alternative directory for the binaries.
+rem
+set MY_ALTDIR=%MY_DIR%\..\LoadedDrivers
+if not exist "%MY_ALTDIR%" mkdir "%MY_ALTDIR%"
+
+rem
+rem Display device states.
+rem
+for %%i in (VBoxNetAdp VBoxNetAdp6 VBoxNetFlt VBoxNetLwf VBoxUSBMon VBoxUSB VBoxSup) do (
+ set type=
+ for /f "usebackq tokens=*" %%f in (`sc query %%i`) do (set xxx=%%f&&if "!xxx:~0,5!" =="STATE" set type=!xxx!)
+ for /f "usebackq tokens=2 delims=:" %%f in ('!type!') do set type=%%f
+ if "!type!x" == "x" set type= not configured, probably
+ echo load.sh: %%i -!type!
+)
+
+rem
+rem Copy uninstallers and installers and VBoxRT into the dir:
+rem
+echo **
+echo ** Copying installers and uninstallers into %MY_ALTDIR%...
+echo **
+set MY_FAILED=no
+for %%i in (NetAdpUninstall.exe NetAdp6Uninstall.exe USBUninstall.exe NetFltUninstall.exe NetLwfUninstall.exe SUPUninstall.exe ^
+ NetAdpInstall.exe NetAdp6Install.exe USBInstall.exe NetFltInstall.exe NetLwfInstall.exe SUPInstall.exe ^
+ VBoxRT.dll) do if exist "%MY_DIR%\%%i" (copy "%MY_DIR%\%%i" "%MY_ALTDIR%\%%i" || set MY_FAILED=yes)
+if "%MY_FAILED%" == "yes" goto end
+
+rem
+rem Unload the drivers.
+rem
+echo **
+echo ** Unloading drivers...
+echo **
+for %%i in (NetAdpUninstall.exe NetAdp6Uninstall.exe USBUninstall.exe NetFltUninstall.exe NetLwfUninstall.exe SUPUninstall.exe) do (
+ if exist "%MY_ALTDIR%\%%i" (echo * Running %%i...&& "%MY_ALTDIR%\%%i")
+)
+
+rem
+rem Copy the driver files into the directory now that they no longer should be in use and can be overwritten.
+rem
+echo **
+echo ** Copying drivers into %MY_ALTDIR%...
+echo **
+set MY_FAILED=no
+for %%i in (^
+ VBoxSup.sys VBoxSup.inf VBoxSup.cat VBoxSup-PreW10.cat ^
+ VBoxNetAdp.sys VBoxNetAdp.inf VBoxNetAdp.cat ^
+ VBoxNetAdp6.sys VBoxNetAdp6.inf VBoxNetAdp6.cat VBoxNetAdp6-PreW10.cat ^
+ VBoxNetFlt.sys VBoxNetFlt.inf VBoxNetFlt.cat VBoxNetFltNobj.dll ^
+ VBoxNetFltM.inf ^
+ VBoxNetLwf.sys VBoxNetLwf.inf VBoxNetLwf.cat VBoxNetLwf-PreW10.cat ^
+ VBoxUSB.sys VBoxUSB.inf VBoxUSB.cat VBoxUSB-PreW10.cat ^
+ VBoxUSBMon.sys VBoxUSBMon.inf VBoxUSBMon.cat VBoxUSBMon-PreW10.cat ) ^
+do if exist "%MY_DIR%\%%i" (copy "%MY_DIR%\%%i" "%MY_ALTDIR%\%%i" || set MY_FAILED=yes)
+if "%MY_FAILED%" == "yes" goto end
+
+rem
+rem Invoke the installer if asked to do so.
+rem
+if "%1%" == "-u" goto end
+if "%1%" == "--uninstall" goto end
+
+set MY_VER=
+for /f "usebackq delims=[] tokens=2" %%f in (`cmd /c ver`) do set MY_VER=%%f
+for /f "usebackq tokens=2" %%f in ('%MY_VER%') do set MY_VER=%%f
+for /f "usebackq delims=. tokens=1" %%f in ('%MY_VER%') do set MY_VER=%%f
+set MY_VER=0000%MY_VER%
+set MY_VER=%MY_VER:~-2%
+
+echo **
+echo ** Loading drivers (for windows version %MY_VER%)...
+echo **
+
+if "%MY_VER%" GEQ "06" (
+ set MY_INSTALLERS=SUPInstall.exe USBInstall.exe NetLwfInstall.exe NetAdp6Install.exe
+) else (
+ set MY_INSTALLERS=SUPInstall.exe USBInstall.exe NetFltInstall.exe
+ rem NetAdpInstall.exe; - busted
+)
+for %%i in (%MY_INSTALLERS%) do (echo * Running %%i...&& "%MY_ALTDIR%\%%i" || (echo loadall.cmd: %%i failed&& goto end))
+
+echo * loadall.cmd completed successfully.
+:end
+endlocal
+endlocal
+endlocal
+